IntervalTest.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.test;

/** Test cases for intervals.
 *
 * <p>Called, with varying implementations of {@link Fixture},
 * from both parser and validator test.
 */
public class IntervalTest {
  private final Fixture f;

  public IntervalTest(Fixture fixture) {
    this.f = fixture;
  }

  /** Runs all tests. */
  public void testAll() {
    // Tests that should pass both parser and validator
    subTestIntervalYearPositive();
    subTestIntervalYearToMonthPositive();
    subTestIntervalMonthPositive();
    subTestIntervalDayPositive();
    subTestIntervalDayToHourPositive();
    subTestIntervalDayToMinutePositive();
    subTestIntervalDayToSecondPositive();
    subTestIntervalHourPositive();
    subTestIntervalHourToMinutePositive();
    subTestIntervalHourToSecondPositive();
    subTestIntervalMinutePositive();
    subTestIntervalMinuteToSecondPositive();
    subTestIntervalSecondPositive();
    subTestIntervalWeekPositive();
    subTestIntervalQuarterPositive();
    subTestIntervalPlural();

    // Tests that should pass parser but fail validator
    subTestIntervalYearNegative();
    subTestIntervalYearToMonthNegative();
    subTestIntervalMonthNegative();
    subTestIntervalDayNegative();
    subTestIntervalDayToHourNegative();
    subTestIntervalDayToMinuteNegative();
    subTestIntervalDayToSecondNegative();
    subTestIntervalHourNegative();
    subTestIntervalHourToMinuteNegative();
    subTestIntervalHourToSecondNegative();
    subTestIntervalMinuteNegative();
    subTestIntervalMinuteToSecondNegative();
    subTestIntervalSecondNegative();

    subTestMisc();
  }

  /**
   * Runs tests for INTERVAL... YEAR that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalYearPositive() {
    // default precision
    f.expr("INTERVAL '1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL '99' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' YEAR(2)")
        .columnType("INTERVAL YEAR(2) NOT NULL");
    f.expr("INTERVAL '99' YEAR(2)")
        .columnType("INTERVAL YEAR(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' YEAR(10)")
        .columnType("INTERVAL YEAR(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' YEAR(1)")
        .columnType("INTERVAL YEAR(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' YEAR(4)")
        .columnType("INTERVAL YEAR(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL '-1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL +'1' YEAR")
        .assertParse("INTERVAL '1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL +'+1' YEAR")
        .assertParse("INTERVAL '+1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL +'-1' YEAR")
        .assertParse("INTERVAL '-1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL -'1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL -'+1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.expr("INTERVAL -'-1' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... YEAR TO MONTH that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalYearToMonthPositive() {
    // default precision
    f.expr("INTERVAL '1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL '99-11' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL '99-0' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1-2' YEAR(2) TO MONTH")
        .columnType("INTERVAL YEAR(2) TO MONTH NOT NULL");
    f.expr("INTERVAL '99-11' YEAR(2) TO MONTH")
        .columnType("INTERVAL YEAR(2) TO MONTH NOT NULL");
    f.expr("INTERVAL '99-0' YEAR(2) TO MONTH")
        .columnType("INTERVAL YEAR(2) TO MONTH NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647-11' YEAR(10) TO MONTH")
        .columnType("INTERVAL YEAR(10) TO MONTH NOT NULL");

    // min precision
    f.expr("INTERVAL '0-0' YEAR(1) TO MONTH")
        .columnType("INTERVAL YEAR(1) TO MONTH NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2006-2' YEAR(4) TO MONTH")
        .columnType("INTERVAL YEAR(4) TO MONTH NOT NULL");

    // sign
    f.expr("INTERVAL '-1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL '+1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL +'1-2' YEAR TO MONTH")
        .assertParse("INTERVAL '1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL +'-1-2' YEAR TO MONTH")
        .assertParse("INTERVAL '-1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL +'+1-2' YEAR TO MONTH")
        .assertParse("INTERVAL '+1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL -'1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL -'-1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.expr("INTERVAL -'+1-2' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... MONTH that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalMonthPositive() {
    // default precision
    f.expr("INTERVAL '1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL '99' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' MONTH(2)")
        .columnType("INTERVAL MONTH(2) NOT NULL");
    f.expr("INTERVAL '99' MONTH(2)")
        .columnType("INTERVAL MONTH(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' MONTH(10)")
        .columnType("INTERVAL MONTH(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' MONTH(1)")
        .columnType("INTERVAL MONTH(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' MONTH(4)")
        .columnType("INTERVAL MONTH(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL '-1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL +'1' MONTH")
        .assertParse("INTERVAL '1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL +'+1' MONTH")
        .assertParse("INTERVAL '+1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL +'-1' MONTH")
        .assertParse("INTERVAL '-1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL -'1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL -'+1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL -'-1' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... DAY that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalDayPositive() {
    // default precision
    f.expr("INTERVAL '1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL '99' DAY")
        .columnType("INTERVAL DAY NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' DAY(2)")
        .columnType("INTERVAL DAY(2) NOT NULL");
    f.expr("INTERVAL '99' DAY(2)")
        .columnType("INTERVAL DAY(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' DAY(10)")
        .columnType("INTERVAL DAY(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' DAY(1)")
        .columnType("INTERVAL DAY(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' DAY(4)")
        .columnType("INTERVAL DAY(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL '-1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL +'1' DAY")
        .assertParse("INTERVAL '1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL +'+1' DAY")
        .assertParse("INTERVAL '+1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL +'-1' DAY")
        .assertParse("INTERVAL '-1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL -'1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL -'+1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL -'-1' DAY")
        .columnType("INTERVAL DAY NOT NULL");
  }

  public void subTestIntervalDayToHourPositive() {
    // default precision
    f.expr("INTERVAL '1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL '99 23' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL '99 0' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1 2' DAY(2) TO HOUR")
        .columnType("INTERVAL DAY(2) TO HOUR NOT NULL");
    f.expr("INTERVAL '99 23' DAY(2) TO HOUR")
        .columnType("INTERVAL DAY(2) TO HOUR NOT NULL");
    f.expr("INTERVAL '99 0' DAY(2) TO HOUR")
        .columnType("INTERVAL DAY(2) TO HOUR NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647 23' DAY(10) TO HOUR")
        .columnType("INTERVAL DAY(10) TO HOUR NOT NULL");

    // min precision
    f.expr("INTERVAL '0 0' DAY(1) TO HOUR")
        .columnType("INTERVAL DAY(1) TO HOUR NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345 2' DAY(4) TO HOUR")
        .columnType("INTERVAL DAY(4) TO HOUR NOT NULL");

    // sign
    f.expr("INTERVAL '-1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL '+1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL +'1 2' DAY TO HOUR")
        .assertParse("INTERVAL '1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL +'-1 2' DAY TO HOUR")
        .assertParse("INTERVAL '-1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL +'+1 2' DAY TO HOUR")
        .assertParse("INTERVAL '+1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL -'1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL -'-1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.expr("INTERVAL -'+1 2' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... DAY TO MINUTE that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalDayToMinutePositive() {
    // default precision
    f.expr("INTERVAL '1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL '99 23:59' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL '99 0:0' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1 2:3' DAY(2) TO MINUTE")
        .columnType("INTERVAL DAY(2) TO MINUTE NOT NULL");
    f.expr("INTERVAL '99 23:59' DAY(2) TO MINUTE")
        .columnType("INTERVAL DAY(2) TO MINUTE NOT NULL");
    f.expr("INTERVAL '99 0:0' DAY(2) TO MINUTE")
        .columnType("INTERVAL DAY(2) TO MINUTE NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647 23:59' DAY(10) TO MINUTE")
        .columnType("INTERVAL DAY(10) TO MINUTE NOT NULL");

    // min precision
    f.expr("INTERVAL '0 0:0' DAY(1) TO MINUTE")
        .columnType("INTERVAL DAY(1) TO MINUTE NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345 6:7' DAY(4) TO MINUTE")
        .columnType("INTERVAL DAY(4) TO MINUTE NOT NULL");

    // sign
    f.expr("INTERVAL '-1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL '+1 2:3' DAY TO MINUTE")
        .assertParse("INTERVAL '+1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL +'1 2:3' DAY TO MINUTE")
        .assertParse("INTERVAL '1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL +'-1 2:3' DAY TO MINUTE")
        .assertParse("INTERVAL '-1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL +'+1 2:3' DAY TO MINUTE")
        .assertParse("INTERVAL '+1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL -'1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL -'-1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.expr("INTERVAL -'+1 2:3' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... DAY TO SECOND that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalDayToSecondPositive() {
    // default precision
    f.expr("INTERVAL '1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL '99 23:59:59' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL '99 0:0:0' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL '99 23:59:59.999999' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL '99 0:0:0.0' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1 2:3:4' DAY(2) TO SECOND")
        .columnType("INTERVAL DAY(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99 23:59:59' DAY(2) TO SECOND")
        .columnType("INTERVAL DAY(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99 0:0:0' DAY(2) TO SECOND")
        .columnType("INTERVAL DAY(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99 23:59:59.999999' DAY TO SECOND(6)")
        .columnType("INTERVAL DAY TO SECOND(6) NOT NULL");
    f.expr("INTERVAL '99 0:0:0.0' DAY TO SECOND(6)")
        .columnType("INTERVAL DAY TO SECOND(6) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647 23:59:59' DAY(10) TO SECOND")
        .columnType("INTERVAL DAY(10) TO SECOND NOT NULL");
    f.expr("INTERVAL '2147483647 23:59:59.999999999' DAY(10) TO SECOND(9)")
        .columnType("INTERVAL DAY(10) TO SECOND(9) NOT NULL");

    // min precision
    f.expr("INTERVAL '0 0:0:0' DAY(1) TO SECOND")
        .columnType("INTERVAL DAY(1) TO SECOND NOT NULL");
    f.expr("INTERVAL '0 0:0:0.0' DAY(1) TO SECOND(1)")
        .columnType("INTERVAL DAY(1) TO SECOND(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345 6:7:8' DAY(4) TO SECOND")
        .columnType("INTERVAL DAY(4) TO SECOND NOT NULL");
    f.expr("INTERVAL '2345 6:7:8.9012' DAY(4) TO SECOND(4)")
        .columnType("INTERVAL DAY(4) TO SECOND(4) NOT NULL");

    // sign
    f.expr("INTERVAL '-1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL '+1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL +'1 2:3:4' DAY TO SECOND")
        .assertParse("INTERVAL '1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL +'-1 2:3:4' DAY TO SECOND")
        .assertParse("INTERVAL '-1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL +'+1 2:3:4' DAY TO SECOND")
        .assertParse("INTERVAL '+1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL -'1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL -'-1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
    f.expr("INTERVAL -'+1 2:3:4' DAY TO SECOND")
        .columnType("INTERVAL DAY TO SECOND NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... HOUR that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalHourPositive() {
    // default precision
    f.expr("INTERVAL '1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL '99' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' HOUR(2)")
        .columnType("INTERVAL HOUR(2) NOT NULL");
    f.expr("INTERVAL '99' HOUR(2)")
        .columnType("INTERVAL HOUR(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' HOUR(10)")
        .columnType("INTERVAL HOUR(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' HOUR(1)")
        .columnType("INTERVAL HOUR(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' HOUR(4)")
        .columnType("INTERVAL HOUR(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL '-1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL +'1' HOUR")
        .assertParse("INTERVAL '1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL +'+1' HOUR")
        .assertParse("INTERVAL '+1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL +'-1' HOUR")
        .assertParse("INTERVAL '-1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL -'1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL -'+1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL -'-1' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... HOUR TO MINUTE that should pass both parser
   * and validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalHourToMinutePositive() {
    // default precision
    f.expr("INTERVAL '2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL '23:59' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL '99:0' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '2:3' HOUR(2) TO MINUTE")
        .columnType("INTERVAL HOUR(2) TO MINUTE NOT NULL");
    f.expr("INTERVAL '23:59' HOUR(2) TO MINUTE")
        .columnType("INTERVAL HOUR(2) TO MINUTE NOT NULL");
    f.expr("INTERVAL '99:0' HOUR(2) TO MINUTE")
        .columnType("INTERVAL HOUR(2) TO MINUTE NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647:59' HOUR(10) TO MINUTE")
        .columnType("INTERVAL HOUR(10) TO MINUTE NOT NULL");

    // min precision
    f.expr("INTERVAL '0:0' HOUR(1) TO MINUTE")
        .columnType("INTERVAL HOUR(1) TO MINUTE NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345:7' HOUR(4) TO MINUTE")
        .columnType("INTERVAL HOUR(4) TO MINUTE NOT NULL");

    // sign
    f.expr("INTERVAL '-1:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL '+1:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL +'2:3' HOUR TO MINUTE")
        .assertParse("INTERVAL '2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL +'-2:3' HOUR TO MINUTE")
        .assertParse("INTERVAL '-2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL +'+2:3' HOUR TO MINUTE")
        .assertParse("INTERVAL '+2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL -'2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL -'-2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.expr("INTERVAL -'+2:3' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... HOUR TO SECOND that should pass both parser
   * and validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalHourToSecondPositive() {
    // default precision
    f.expr("INTERVAL '2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL '23:59:59' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0:0' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL '23:59:59.999999' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0:0.0' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '2:3:4' HOUR(2) TO SECOND")
        .columnType("INTERVAL HOUR(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:59:59' HOUR(2) TO SECOND")
        .columnType("INTERVAL HOUR(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0:0' HOUR(2) TO SECOND")
        .columnType("INTERVAL HOUR(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:59:59.999999' HOUR TO SECOND(6)")
        .columnType("INTERVAL HOUR TO SECOND(6) NOT NULL");
    f.expr("INTERVAL '99:0:0.0' HOUR TO SECOND(6)")
        .columnType("INTERVAL HOUR TO SECOND(6) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647:59:59' HOUR(10) TO SECOND")
        .columnType("INTERVAL HOUR(10) TO SECOND NOT NULL");
    f.expr("INTERVAL '2147483647:59:59.999999999' HOUR(10) TO SECOND(9)")
        .columnType("INTERVAL HOUR(10) TO SECOND(9) NOT NULL");

    // min precision
    f.expr("INTERVAL '0:0:0' HOUR(1) TO SECOND")
        .columnType("INTERVAL HOUR(1) TO SECOND NOT NULL");
    f.expr("INTERVAL '0:0:0.0' HOUR(1) TO SECOND(1)")
        .columnType("INTERVAL HOUR(1) TO SECOND(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345:7:8' HOUR(4) TO SECOND")
        .columnType("INTERVAL HOUR(4) TO SECOND NOT NULL");
    f.expr("INTERVAL '2345:7:8.9012' HOUR(4) TO SECOND(4)")
        .columnType("INTERVAL HOUR(4) TO SECOND(4) NOT NULL");

    // sign
    f.expr("INTERVAL '-2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL '+2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL +'2:3:4' HOUR TO SECOND")
        .assertParse("INTERVAL '2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL +'-2:3:4' HOUR TO SECOND")
        .assertParse("INTERVAL '-2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL +'+2:3:4' HOUR TO SECOND")
        .assertParse("INTERVAL '+2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL -'2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL -'-2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.expr("INTERVAL -'+2:3:4' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... MINUTE that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalMinutePositive() {
    // default precision
    f.expr("INTERVAL '1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL '99' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' MINUTE(2)")
        .columnType("INTERVAL MINUTE(2) NOT NULL");
    f.expr("INTERVAL '99' MINUTE(2)")
        .columnType("INTERVAL MINUTE(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' MINUTE(10)")
        .columnType("INTERVAL MINUTE(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' MINUTE(1)")
        .columnType("INTERVAL MINUTE(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' MINUTE(4)")
        .columnType("INTERVAL MINUTE(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL '-1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL +'1' MINUTE")
        .assertParse("INTERVAL '1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL +'+1' MINUTE")
        .assertParse("INTERVAL '+1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL +'-1' MINUTE")
        .assertParse("INTERVAL '-1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL -'1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL -'+1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.expr("INTERVAL -'-1' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... MINUTE TO SECOND that should pass both parser
   * and validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalMinuteToSecondPositive() {
    // default precision
    f.expr("INTERVAL '2:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL '59:59' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL '59:59.999999' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0.0' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '2:4' MINUTE(2) TO SECOND")
        .columnType("INTERVAL MINUTE(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:59' MINUTE(2) TO SECOND")
        .columnType("INTERVAL MINUTE(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:0' MINUTE(2) TO SECOND")
        .columnType("INTERVAL MINUTE(2) TO SECOND NOT NULL");
    f.expr("INTERVAL '99:59.999999' MINUTE TO SECOND(6)")
        .columnType("INTERVAL MINUTE TO SECOND(6) NOT NULL");
    f.expr("INTERVAL '99:0.0' MINUTE TO SECOND(6)")
        .columnType("INTERVAL MINUTE TO SECOND(6) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647:59' MINUTE(10) TO SECOND")
        .columnType("INTERVAL MINUTE(10) TO SECOND NOT NULL");
    f.expr("INTERVAL '2147483647:59.999999999' MINUTE(10) TO SECOND(9)")
        .columnType("INTERVAL MINUTE(10) TO SECOND(9) NOT NULL");

    // min precision
    f.expr("INTERVAL '0:0' MINUTE(1) TO SECOND")
        .columnType("INTERVAL MINUTE(1) TO SECOND NOT NULL");
    f.expr("INTERVAL '0:0.0' MINUTE(1) TO SECOND(1)")
        .columnType("INTERVAL MINUTE(1) TO SECOND(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '2345:8' MINUTE(4) TO SECOND")
        .columnType("INTERVAL MINUTE(4) TO SECOND NOT NULL");
    f.expr("INTERVAL '2345:7.8901' MINUTE(4) TO SECOND(4)")
        .columnType("INTERVAL MINUTE(4) TO SECOND(4) NOT NULL");

    // sign
    f.expr("INTERVAL '-3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL '+3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL +'3:4' MINUTE TO SECOND")
        .assertParse("INTERVAL '3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL +'-3:4' MINUTE TO SECOND")
        .assertParse("INTERVAL '-3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL +'+3:4' MINUTE TO SECOND")
        .assertParse("INTERVAL '+3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL -'3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL -'-3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.expr("INTERVAL -'+3:4' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... SECOND that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalSecondPositive() {
    // default precision
    f.expr("INTERVAL '1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL '99' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' SECOND(2)")
        .columnType("INTERVAL SECOND(2) NOT NULL");
    f.expr("INTERVAL '99' SECOND(2)")
        .columnType("INTERVAL SECOND(2) NOT NULL");
    f.expr("INTERVAL '1' SECOND(2, 6)")
        .columnType("INTERVAL SECOND(2, 6) NOT NULL");
    f.expr("INTERVAL '99' SECOND(2, 6)")
        .columnType("INTERVAL SECOND(2, 6) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' SECOND(10)")
        .columnType("INTERVAL SECOND(10) NOT NULL");
    f.expr("INTERVAL '2147483647.999999999' SECOND(10, 9)")
        .columnType("INTERVAL SECOND(10, 9) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' SECOND(1)")
        .columnType("INTERVAL SECOND(1) NOT NULL");
    f.expr("INTERVAL '0.0' SECOND(1, 1)")
        .columnType("INTERVAL SECOND(1, 1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' SECOND(4)")
        .columnType("INTERVAL SECOND(4) NOT NULL");
    f.expr("INTERVAL '1234.56789' SECOND(4, 5)")
        .columnType("INTERVAL SECOND(4, 5) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL '-1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL +'1' SECOND")
        .assertParse("INTERVAL '1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL +'+1' SECOND")
        .assertParse("INTERVAL '+1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL +'-1' SECOND")
        .assertParse("INTERVAL '-1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL -'1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL -'+1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL -'-1' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... YEAR that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalYearNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' YEAR")
        .fails("Illegal interval literal format '-' for INTERVAL YEAR.*");
    f.wholeExpr("INTERVAL '1-2' YEAR")
        .fails("Illegal interval literal format '1-2' for INTERVAL YEAR.*");
    f.wholeExpr("INTERVAL '1.2' YEAR")
        .fails("Illegal interval literal format '1.2' for INTERVAL YEAR.*");
    f.wholeExpr("INTERVAL '1 2' YEAR")
        .fails("Illegal interval literal format '1 2' for INTERVAL YEAR.*");
    f.wholeExpr("INTERVAL '1-2' YEAR(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL YEAR\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' YEAR")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL YEAR.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' YEAR")
        .fails("Illegal interval literal format '--1' for INTERVAL YEAR.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
    f.wholeExpr("INTERVAL '100' YEAR(2)")
        .fails("Interval field value 100 exceeds precision of YEAR\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' YEAR(3)")
        .fails("Interval field value 1,000 exceeds precision of YEAR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' YEAR(3)")
        .fails("Interval field value -1,000 exceeds precision of YEAR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' YEAR(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "YEAR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' YEAR(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "YEAR\\(10\\) field");

    // precision > maximum
    f.expr("INTERVAL '1' ^YEAR(11)^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL YEAR\\(11\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^YEAR(0)^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL YEAR\\(0\\)");
  }

  /**
   * Runs tests for INTERVAL... YEAR TO MONTH that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalYearToMonthNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' YEAR TO MONTH")
        .fails("Illegal interval literal format '-' for INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1' YEAR TO MONTH")
        .fails("Illegal interval literal format '1' for INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1:2' YEAR TO MONTH")
        .fails("Illegal interval literal format '1:2' for INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1.2' YEAR TO MONTH")
        .fails("Illegal interval literal format '1.2' for INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1 2' YEAR TO MONTH")
        .fails("Illegal interval literal format '1 2' for INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1:2' YEAR(2) TO MONTH")
        .fails("Illegal interval literal format '1:2' for "
            + "INTERVAL YEAR\\(2\\) TO MONTH");
    f.wholeExpr("INTERVAL 'bogus text' YEAR TO MONTH")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL YEAR TO MONTH");

    // negative field values
    f.wholeExpr("INTERVAL '--1-2' YEAR TO MONTH")
        .fails("Illegal interval literal format '--1-2' for "
            + "INTERVAL YEAR TO MONTH");
    f.wholeExpr("INTERVAL '1--2' YEAR TO MONTH")
        .fails("Illegal interval literal format '1--2' for "
            + "INTERVAL YEAR TO MONTH");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100-0' YEAR TO MONTH")
        .columnType("INTERVAL YEAR TO MONTH NOT NULL");
    f.wholeExpr("INTERVAL '100-0' YEAR(2) TO MONTH")
        .fails("Interval field value 100 exceeds precision of YEAR\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000-0' YEAR(3) TO MONTH")
        .fails("Interval field value 1,000 exceeds precision of YEAR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000-0' YEAR(3) TO MONTH")
        .fails("Interval field value -1,000 exceeds precision of YEAR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648-0' YEAR(10) TO MONTH")
        .fails("Interval field value 2,147,483,648 exceeds precision of YEAR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648-0' YEAR(10) TO MONTH")
        .fails("Interval field value -2,147,483,648 exceeds precision of YEAR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1-12' YEAR TO MONTH")
        .fails("Illegal interval literal format '1-12' for INTERVAL YEAR TO MONTH.*");

    // precision > maximum
    f.expr("INTERVAL '1-1' ^YEAR(11) TO MONTH^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL YEAR\\(11\\) TO MONTH");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0-0' ^YEAR(0) TO MONTH^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL YEAR\\(0\\) TO MONTH");
  }

  /**
   * Runs tests for INTERVAL... WEEK that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlValidatorTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalWeekPositive() {
    // default precision
    f.expr("INTERVAL '1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL '99' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' WEEK(2)")
        .columnType("INTERVAL WEEK(2) NOT NULL");
    f.expr("INTERVAL '99' WEEK(2)")
        .columnType("INTERVAL WEEK(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' WEEK(10)")
        .columnType("INTERVAL WEEK(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' WEEK(1)")
        .columnType("INTERVAL WEEK(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' WEEK(4)")
        .columnType("INTERVAL WEEK(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL '-1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL +'1' WEEK")
        .assertParse("INTERVAL '1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL +'+1' WEEK")
        .assertParse("INTERVAL '+1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL +'-1' WEEK")
        .assertParse("INTERVAL '-1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL -'1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL -'+1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL -'-1' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... QUARTER that should pass both parser and
   * validator. A substantially identical set of tests exists in
   * SqlValidatorTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXPositive() tests.
   */
  public void subTestIntervalQuarterPositive() {
    // default precision
    f.expr("INTERVAL '1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL '99' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");

    // explicit precision equal to default
    f.expr("INTERVAL '1' QUARTER(2)")
        .columnType("INTERVAL QUARTER(2) NOT NULL");
    f.expr("INTERVAL '99' QUARTER(2)")
        .columnType("INTERVAL QUARTER(2) NOT NULL");

    // max precision
    f.expr("INTERVAL '2147483647' QUARTER(10)")
        .columnType("INTERVAL QUARTER(10) NOT NULL");

    // min precision
    f.expr("INTERVAL '0' QUARTER(1)")
        .columnType("INTERVAL QUARTER(1) NOT NULL");

    // alternate precision
    f.expr("INTERVAL '1234' QUARTER(4)")
        .columnType("INTERVAL QUARTER(4) NOT NULL");

    // sign
    f.expr("INTERVAL '+1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL '-1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL +'1' QUARTER")
        .assertParse("INTERVAL '1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL +'+1' QUARTER")
        .assertParse("INTERVAL '+1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL +'-1' QUARTER")
        .assertParse("INTERVAL '-1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL -'1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL -'+1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL -'-1' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
  }

  public void subTestIntervalPlural() {
    f.expr("INTERVAL '+2' SECONDS")
        .assertParse("INTERVAL '+2' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.expr("INTERVAL '+2' HOURS")
        .assertParse("INTERVAL '+2' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.expr("INTERVAL '+2' DAYS")
        .assertParse("INTERVAL '+2' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.expr("INTERVAL '+2' WEEKS")
        .assertParse("INTERVAL '+2' WEEK")
        .columnType("INTERVAL WEEK NOT NULL");
    f.expr("INTERVAL '+2' QUARTERS")
        .assertParse("INTERVAL '+2' QUARTER")
        .columnType("INTERVAL QUARTER NOT NULL");
    f.expr("INTERVAL '+2' MONTHS")
        .assertParse("INTERVAL '+2' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.expr("INTERVAL '+2' YEARS")
        .assertParse("INTERVAL '+2' YEAR")
        .columnType("INTERVAL YEAR NOT NULL");
  }

  /**
   * Runs tests for INTERVAL... MONTH that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalMonthNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' MONTH")
        .fails("Illegal interval literal format '-' for INTERVAL MONTH.*");
    f.wholeExpr("INTERVAL '1-2' MONTH")
        .fails("Illegal interval literal format '1-2' for INTERVAL MONTH.*");
    f.wholeExpr("INTERVAL '1.2' MONTH")
        .fails("Illegal interval literal format '1.2' for INTERVAL MONTH.*");
    f.wholeExpr("INTERVAL '1 2' MONTH")
        .fails("Illegal interval literal format '1 2' for INTERVAL MONTH.*");
    f.wholeExpr("INTERVAL '1-2' MONTH(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL MONTH\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' MONTH")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL MONTH.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' MONTH")
        .fails("Illegal interval literal format '--1' for INTERVAL MONTH.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' MONTH")
        .columnType("INTERVAL MONTH NOT NULL");
    f.wholeExpr("INTERVAL '100' MONTH(2)")
        .fails("Interval field value 100 exceeds precision of MONTH\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' MONTH(3)")
        .fails("Interval field value 1,000 exceeds precision of MONTH\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' MONTH(3)")
        .fails("Interval field value -1,000 exceeds precision of MONTH\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' MONTH(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of MONTH\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' MONTH(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of MONTH\\(10\\) field.*");

    // precision > maximum
    f.expr("INTERVAL '1' ^MONTH(11)^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL MONTH\\(11\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^MONTH(0)^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL MONTH\\(0\\)");
  }

  /**
   * Runs tests for INTERVAL... DAY that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalDayNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' DAY")
        .fails("Illegal interval literal format '-' for INTERVAL DAY.*");
    f.wholeExpr("INTERVAL '1-2' DAY")
        .fails("Illegal interval literal format '1-2' for INTERVAL DAY.*");
    f.wholeExpr("INTERVAL '1.2' DAY")
        .fails("Illegal interval literal format '1.2' for INTERVAL DAY.*");
    f.wholeExpr("INTERVAL '1 2' DAY")
        .fails("Illegal interval literal format '1 2' for INTERVAL DAY.*");
    f.wholeExpr("INTERVAL '1:2' DAY")
        .fails("Illegal interval literal format '1:2' for INTERVAL DAY.*");
    f.wholeExpr("INTERVAL '1-2' DAY(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL DAY\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' DAY")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL DAY.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' DAY")
        .fails("Illegal interval literal format '--1' for INTERVAL DAY.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' DAY")
        .columnType("INTERVAL DAY NOT NULL");
    f.wholeExpr("INTERVAL '100' DAY(2)")
        .fails("Interval field value 100 exceeds precision of DAY\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' DAY(3)")
        .fails("Interval field value 1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' DAY(3)")
        .fails("Interval field value -1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' DAY(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' DAY(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");

    // precision > maximum
    f.expr("INTERVAL '1' ^DAY(11)^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL DAY\\(11\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^DAY(0)^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL DAY\\(0\\)");
  }

  /**
   * Runs tests for INTERVAL... DAY TO HOUR that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalDayToHourNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' DAY TO HOUR")
        .fails("Illegal interval literal format '-' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1' DAY TO HOUR")
        .fails("Illegal interval literal format '1' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1:2' DAY TO HOUR")
        .fails("Illegal interval literal format '1:2' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1.2' DAY TO HOUR")
        .fails("Illegal interval literal format '1.2' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1 x' DAY TO HOUR")
        .fails("Illegal interval literal format '1 x' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL ' ' DAY TO HOUR")
        .fails("Illegal interval literal format ' ' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1:2' DAY(2) TO HOUR")
        .fails("Illegal interval literal format '1:2' for "
            + "INTERVAL DAY\\(2\\) TO HOUR");
    f.wholeExpr("INTERVAL 'bogus text' DAY TO HOUR")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL DAY TO HOUR");

    // negative field values
    f.wholeExpr("INTERVAL '--1 1' DAY TO HOUR")
        .fails("Illegal interval literal format '--1 1' for INTERVAL DAY TO HOUR");
    f.wholeExpr("INTERVAL '1 -1' DAY TO HOUR")
        .fails("Illegal interval literal format '1 -1' for INTERVAL DAY TO HOUR");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100 0' DAY TO HOUR")
        .columnType("INTERVAL DAY TO HOUR NOT NULL");
    f.wholeExpr("INTERVAL '100 0' DAY(2) TO HOUR")
        .fails("Interval field value 100 exceeds precision of DAY\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000 0' DAY(3) TO HOUR")
        .fails("Interval field value 1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000 0' DAY(3) TO HOUR")
        .fails("Interval field value -1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648 0' DAY(10) TO HOUR")
        .fails("Interval field value 2,147,483,648 exceeds precision of DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648 0' DAY(10) TO HOUR")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1 24' DAY TO HOUR")
        .fails("Illegal interval literal format '1 24' for INTERVAL DAY TO HOUR.*");

    // precision > maximum
    f.expr("INTERVAL '1 1' ^DAY(11) TO HOUR^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL DAY\\(11\\) TO HOUR");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0 0' ^DAY(0) TO HOUR^")
        .fails("Interval leading field precision '0' out of range for INTERVAL DAY\\(0\\) TO HOUR");
  }

  /**
   * Runs tests for INTERVAL... DAY TO MINUTE that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalDayToMinuteNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL ' :' DAY TO MINUTE")
        .fails("Illegal interval literal format ' :' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1' DAY TO MINUTE")
        .fails("Illegal interval literal format '1' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 2' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 2' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1:2' DAY TO MINUTE")
        .fails("Illegal interval literal format '1:2' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1.2' DAY TO MINUTE")
        .fails("Illegal interval literal format '1.2' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL 'x 1:1' DAY TO MINUTE")
        .fails("Illegal interval literal format 'x 1:1' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 x:1' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 x:1' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 1:x' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 1:x' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 1:2:3' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 1:2:3' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 1:1:1.2' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 1:1:1.2' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 1:2:3' DAY(2) TO MINUTE")
        .fails("Illegal interval literal format '1 1:2:3' for "
            + "INTERVAL DAY\\(2\\) TO MINUTE");
    f.wholeExpr("INTERVAL '1 1' DAY(2) TO MINUTE")
        .fails("Illegal interval literal format '1 1' for "
            + "INTERVAL DAY\\(2\\) TO MINUTE");
    f.wholeExpr("INTERVAL 'bogus text' DAY TO MINUTE")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL DAY TO MINUTE");

    // negative field values
    f.wholeExpr("INTERVAL '--1 1:1' DAY TO MINUTE")
        .fails("Illegal interval literal format '--1 1:1' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 -1:1' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 -1:1' for INTERVAL DAY TO MINUTE");
    f.wholeExpr("INTERVAL '1 1:-1' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 1:-1' for INTERVAL DAY TO MINUTE");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100 0:0' DAY TO MINUTE")
        .columnType("INTERVAL DAY TO MINUTE NOT NULL");
    f.wholeExpr("INTERVAL '100 0:0' DAY(2) TO MINUTE")
        .fails("Interval field value 100 exceeds precision of DAY\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000 0:0' DAY(3) TO MINUTE")
        .fails("Interval field value 1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000 0:0' DAY(3) TO MINUTE")
        .fails("Interval field value -1,000 exceeds precision of DAY\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648 0:0' DAY(10) TO MINUTE")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648 0:0' DAY(10) TO MINUTE")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1 24:1' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 24:1' for "
            + "INTERVAL DAY TO MINUTE.*");
    f.wholeExpr("INTERVAL '1 1:60' DAY TO MINUTE")
        .fails("Illegal interval literal format '1 1:60' for INTERVAL DAY TO MINUTE.*");

    // precision > maximum
    f.expr("INTERVAL '1 1:1' ^DAY(11) TO MINUTE^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL DAY\\(11\\) TO MINUTE");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0 0' ^DAY(0) TO MINUTE^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL DAY\\(0\\) TO MINUTE");
  }

  /**
   * Runs tests for INTERVAL... DAY TO SECOND that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalDayToSecondNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL ' ::' DAY TO SECOND")
        .fails("Illegal interval literal format ' ::' for INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL ' ::.' DAY TO SECOND")
        .fails("Illegal interval literal format ' ::\\.' for INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1' DAY TO SECOND")
        .fails("Illegal interval literal format '1' for INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 2' DAY TO SECOND")
        .fails("Illegal interval literal format '1 2' for INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1:2' DAY TO SECOND")
        .fails("Illegal interval literal format '1:2' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1.2' DAY TO SECOND")
        .fails("Illegal interval literal format '1\\.2' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2:x' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:2:x' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1:2:3' DAY TO SECOND")
        .fails("Illegal interval literal format '1:2:3' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1:1:1.2' DAY TO SECOND")
        .fails("Illegal interval literal format '1:1:1\\.2' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' DAY(2) TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for "
            + "INTERVAL DAY\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL '1 1' DAY(2) TO SECOND")
        .fails("Illegal interval literal format '1 1' for "
            + "INTERVAL DAY\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL 'bogus text' DAY TO SECOND")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '2345 6:7:8901' DAY TO SECOND(4)")
        .fails("Illegal interval literal format '2345 6:7:8901' for "
            + "INTERVAL DAY TO SECOND\\(4\\)");

    // negative field values
    f.wholeExpr("INTERVAL '--1 1:1:1' DAY TO SECOND")
        .fails("Illegal interval literal format '--1 1:1:1' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 -1:1:1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 -1:1:1' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:-1:1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:-1:1' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:1:-1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:1:-1' for "
            + "INTERVAL DAY TO SECOND");
    f.wholeExpr("INTERVAL '1 1:1:1.-1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:1:1.-1' for "
            + "INTERVAL DAY TO SECOND");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100 0' DAY TO SECOND")
        .fails("Illegal interval literal format '100 0' for "
            + "INTERVAL DAY TO SECOND.*");
    f.wholeExpr("INTERVAL '100 0' DAY(2) TO SECOND")
        .fails("Illegal interval literal format '100 0' for "
            + "INTERVAL DAY\\(2\\) TO SECOND.*");
    f.wholeExpr("INTERVAL '1000 0' DAY(3) TO SECOND")
        .fails("Illegal interval literal format '1000 0' for "
            + "INTERVAL DAY\\(3\\) TO SECOND.*");
    f.wholeExpr("INTERVAL '-1000 0' DAY(3) TO SECOND")
        .fails("Illegal interval literal format '-1000 0' for "
            + "INTERVAL DAY\\(3\\) TO SECOND.*");
    f.wholeExpr("INTERVAL '2147483648 1:1:0' DAY(10) TO SECOND")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648 1:1:0' DAY(10) TO SECOND")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "DAY\\(10\\) field.*");
    f.wholeExpr("INTERVAL '2147483648 0' DAY(10) TO SECOND")
        .fails("Illegal interval literal format '2147483648 0' for "
            + "INTERVAL DAY\\(10\\) TO SECOND.*");
    f.wholeExpr("INTERVAL '-2147483648 0' DAY(10) TO SECOND")
        .fails("Illegal interval literal format '-2147483648 0' for "
            + "INTERVAL DAY\\(10\\) TO SECOND.*");
    f.wholeExpr("INTERVAL '1 24:1:1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 24:1:1' for "
            + "INTERVAL DAY TO SECOND.*");
    f.wholeExpr("INTERVAL '1 1:60:1' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:60:1' for "
            + "INTERVAL DAY TO SECOND.*");
    f.wholeExpr("INTERVAL '1 1:1:60' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:1:60' for "
            + "INTERVAL DAY TO SECOND.*");
    f.wholeExpr("INTERVAL '1 1:1:1.0000001' DAY TO SECOND")
        .fails("Illegal interval literal format '1 1:1:1\\.0000001' for "
            + "INTERVAL DAY TO SECOND.*");
    f.wholeExpr("INTERVAL '1 1:1:1.0001' DAY TO SECOND(3)")
        .fails("Illegal interval literal format '1 1:1:1\\.0001' for "
            + "INTERVAL DAY TO SECOND\\(3\\).*");

    // precision > maximum
    f.expr("INTERVAL '1 1' ^DAY(11) TO SECOND^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL DAY\\(11\\) TO SECOND");
    f.expr("INTERVAL '1 1' ^DAY TO SECOND(10)^")
        .fails("Interval fractional second precision '10' out of range for "
            + "INTERVAL DAY TO SECOND\\(10\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0 0:0:0' ^DAY(0) TO SECOND^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL DAY\\(0\\) TO SECOND");
  }

  /**
   * Runs tests for INTERVAL... HOUR that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalHourNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' HOUR")
        .fails("Illegal interval literal format '-' for INTERVAL HOUR.*");
    f.wholeExpr("INTERVAL '1-2' HOUR")
        .fails("Illegal interval literal format '1-2' for INTERVAL HOUR.*");
    f.wholeExpr("INTERVAL '1.2' HOUR")
        .fails("Illegal interval literal format '1.2' for INTERVAL HOUR.*");
    f.wholeExpr("INTERVAL '1 2' HOUR")
        .fails("Illegal interval literal format '1 2' for INTERVAL HOUR.*");
    f.wholeExpr("INTERVAL '1:2' HOUR")
        .fails("Illegal interval literal format '1:2' for INTERVAL HOUR.*");
    f.wholeExpr("INTERVAL '1-2' HOUR(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL HOUR\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' HOUR")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL HOUR.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' HOUR")
        .fails("Illegal interval literal format '--1' for INTERVAL HOUR.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' HOUR")
        .columnType("INTERVAL HOUR NOT NULL");
    f.wholeExpr("INTERVAL '100' HOUR(2)")
        .fails("Interval field value 100 exceeds precision of "
            + "HOUR\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' HOUR(3)")
        .fails("Interval field value 1,000 exceeds precision of "
            + "HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' HOUR(3)")
        .fails("Interval field value -1,000 exceeds precision of "
            + "HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' HOUR(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "HOUR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' HOUR(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "HOUR\\(10\\) field.*");

    // precision > maximum
    f.expr("INTERVAL '1' ^HOUR(11)^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL HOUR\\(11\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^HOUR(0)^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL HOUR\\(0\\)");
  }

  /**
   * Runs tests for INTERVAL... HOUR TO MINUTE that should pass parser but
   * fail validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalHourToMinuteNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL ':' HOUR TO MINUTE")
        .fails("Illegal interval literal format ':' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1:x' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1:x' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1.2' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1.2' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1 2' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1 2' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1:2:3' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1:2:3' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1 2' HOUR(2) TO MINUTE")
        .fails("Illegal interval literal format '1 2' for "
            + "INTERVAL HOUR\\(2\\) TO MINUTE");
    f.wholeExpr("INTERVAL 'bogus text' HOUR TO MINUTE")
        .fails("Illegal interval literal format 'bogus text' for "
            + "INTERVAL HOUR TO MINUTE");

    // negative field values
    f.wholeExpr("INTERVAL '--1:1' HOUR TO MINUTE")
        .fails("Illegal interval literal format '--1:1' for INTERVAL HOUR TO MINUTE");
    f.wholeExpr("INTERVAL '1:-1' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1:-1' for INTERVAL HOUR TO MINUTE");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100:0' HOUR TO MINUTE")
        .columnType("INTERVAL HOUR TO MINUTE NOT NULL");
    f.wholeExpr("INTERVAL '100:0' HOUR(2) TO MINUTE")
        .fails("Interval field value 100 exceeds precision of HOUR\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000:0' HOUR(3) TO MINUTE")
        .fails("Interval field value 1,000 exceeds precision of HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000:0' HOUR(3) TO MINUTE")
        .fails("Interval field value -1,000 exceeds precision of HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648:0' HOUR(10) TO MINUTE")
        .fails("Interval field value 2,147,483,648 exceeds precision of HOUR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648:0' HOUR(10) TO MINUTE")
        .fails("Interval field value -2,147,483,648 exceeds precision of HOUR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1:60' HOUR TO MINUTE")
        .fails("Illegal interval literal format '1:60' for INTERVAL HOUR TO MINUTE.*");

    // precision > maximum
    f.expr("INTERVAL '1:1' ^HOUR(11) TO MINUTE^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL HOUR\\(11\\) TO MINUTE");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0:0' ^HOUR(0) TO MINUTE^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL HOUR\\(0\\) TO MINUTE");
  }

  /**
   * Runs tests for INTERVAL... HOUR TO SECOND that should pass parser but
   * fail validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalHourToSecondNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '::' HOUR TO SECOND")
        .fails("Illegal interval literal format '::' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '::.' HOUR TO SECOND")
        .fails("Illegal interval literal format '::\\.' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1' HOUR TO SECOND")
        .fails("Illegal interval literal format '1' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1 2' HOUR TO SECOND")
        .fails("Illegal interval literal format '1 2' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:2' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:2' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1.2' HOUR TO SECOND")
        .fails("Illegal interval literal format '1\\.2' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' HOUR TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:2:x' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:2:x' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:x:3' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:x:3' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:1:1.x' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:1:1\\.x' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' HOUR(2) TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for INTERVAL HOUR\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL '1 1' HOUR(2) TO SECOND")
        .fails("Illegal interval literal format '1 1' for INTERVAL HOUR\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL 'bogus text' HOUR TO SECOND")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '6:7:8901' HOUR TO SECOND(4)")
        .fails("Illegal interval literal format '6:7:8901' for INTERVAL HOUR TO SECOND\\(4\\)");

    // negative field values
    f.wholeExpr("INTERVAL '--1:1:1' HOUR TO SECOND")
        .fails("Illegal interval literal format '--1:1:1' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:-1:1' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:-1:1' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:1:-1' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:1:-1' for INTERVAL HOUR TO SECOND");
    f.wholeExpr("INTERVAL '1:1:1.-1' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:1:1\\.-1' for INTERVAL HOUR TO SECOND");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100:0:0' HOUR TO SECOND")
        .columnType("INTERVAL HOUR TO SECOND NOT NULL");
    f.wholeExpr("INTERVAL '100:0:0' HOUR(2) TO SECOND")
        .fails("Interval field value 100 exceeds precision of "
            + "HOUR\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000:0:0' HOUR(3) TO SECOND")
        .fails("Interval field value 1,000 exceeds precision of "
            + "HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000:0:0' HOUR(3) TO SECOND")
        .fails("Interval field value -1,000 exceeds precision of "
            + "HOUR\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648:0:0' HOUR(10) TO SECOND")
        .fails("Interval field value 2,147,483,648 exceeds precision of "
            + "HOUR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648:0:0' HOUR(10) TO SECOND")
        .fails("Interval field value -2,147,483,648 exceeds precision of "
            + "HOUR\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1:60:1' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:60:1' for "
            + "INTERVAL HOUR TO SECOND.*");
    f.wholeExpr("INTERVAL '1:1:60' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:1:60' for "
            + "INTERVAL HOUR TO SECOND.*");
    f.wholeExpr("INTERVAL '1:1:1.0000001' HOUR TO SECOND")
        .fails("Illegal interval literal format '1:1:1\\.0000001' for "
            + "INTERVAL HOUR TO SECOND.*");
    f.wholeExpr("INTERVAL '1:1:1.0001' HOUR TO SECOND(3)")
        .fails("Illegal interval literal format '1:1:1\\.0001' for "
            + "INTERVAL HOUR TO SECOND\\(3\\).*");

    // precision > maximum
    f.expr("INTERVAL '1:1:1' ^HOUR(11) TO SECOND^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL HOUR\\(11\\) TO SECOND");
    f.expr("INTERVAL '1:1:1' ^HOUR TO SECOND(10)^")
        .fails("Interval fractional second precision '10' out of range for "
            + "INTERVAL HOUR TO SECOND\\(10\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0:0:0' ^HOUR(0) TO SECOND^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL HOUR\\(0\\) TO SECOND");
  }

  /**
   * Runs tests for INTERVAL... MINUTE that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalMinuteNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL '-' MINUTE")
        .fails("Illegal interval literal format '-' for INTERVAL MINUTE.*");
    f.wholeExpr("INTERVAL '1-2' MINUTE")
        .fails("Illegal interval literal format '1-2' for INTERVAL MINUTE.*");
    f.wholeExpr("INTERVAL '1.2' MINUTE")
        .fails("Illegal interval literal format '1.2' for INTERVAL MINUTE.*");
    f.wholeExpr("INTERVAL '1 2' MINUTE")
        .fails("Illegal interval literal format '1 2' for INTERVAL MINUTE.*");
    f.wholeExpr("INTERVAL '1:2' MINUTE")
        .fails("Illegal interval literal format '1:2' for INTERVAL MINUTE.*");
    f.wholeExpr("INTERVAL '1-2' MINUTE(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL MINUTE\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' MINUTE")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL MINUTE.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' MINUTE")
        .fails("Illegal interval literal format '--1' for INTERVAL MINUTE.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' MINUTE")
        .columnType("INTERVAL MINUTE NOT NULL");
    f.wholeExpr("INTERVAL '100' MINUTE(2)")
        .fails("Interval field value 100 exceeds precision of MINUTE\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' MINUTE(3)")
        .fails("Interval field value 1,000 exceeds precision of MINUTE\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' MINUTE(3)")
        .fails("Interval field value -1,000 exceeds precision of MINUTE\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' MINUTE(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of MINUTE\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' MINUTE(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of MINUTE\\(10\\) field.*");

    // precision > maximum
    f.expr("INTERVAL '1' ^MINUTE(11)^")
        .fails("Interval leading field precision '11' out of range for "
            + "INTERVAL MINUTE\\(11\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^MINUTE(0)^")
        .fails("Interval leading field precision '0' out of range for "
            + "INTERVAL MINUTE\\(0\\)");
  }

  /**
   * Runs tests for INTERVAL... MINUTE TO SECOND that should pass parser but
   * fail validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalMinuteToSecondNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL ':' MINUTE TO SECOND")
        .fails("Illegal interval literal format ':' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL ':.' MINUTE TO SECOND")
        .fails("Illegal interval literal format ':\\.' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1 2' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1 2' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1.2' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1\\.2' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1:x' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:x' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL 'x:3' MINUTE TO SECOND")
        .fails("Illegal interval literal format 'x:3' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1:1.x' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:1\\.x' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1 1:2' MINUTE(2) TO SECOND")
        .fails("Illegal interval literal format '1 1:2' for INTERVAL MINUTE\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL '1 1' MINUTE(2) TO SECOND")
        .fails("Illegal interval literal format '1 1' for INTERVAL MINUTE\\(2\\) TO SECOND");
    f.wholeExpr("INTERVAL 'bogus text' MINUTE TO SECOND")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '7:8901' MINUTE TO SECOND(4)")
        .fails("Illegal interval literal format '7:8901' for INTERVAL MINUTE TO SECOND\\(4\\)");

    // negative field values
    f.wholeExpr("INTERVAL '--1:1' MINUTE TO SECOND")
        .fails("Illegal interval literal format '--1:1' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1:-1' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:-1' for INTERVAL MINUTE TO SECOND");
    f.wholeExpr("INTERVAL '1:1.-1' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:1.-1' for INTERVAL MINUTE TO SECOND");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    //  plus >max value for mid/end fields
    f.wholeExpr("INTERVAL '100:0' MINUTE TO SECOND")
        .columnType("INTERVAL MINUTE TO SECOND NOT NULL");
    f.wholeExpr("INTERVAL '100:0' MINUTE(2) TO SECOND")
        .fails("Interval field value 100 exceeds precision of MINUTE\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000:0' MINUTE(3) TO SECOND")
        .fails("Interval field value 1,000 exceeds precision of MINUTE\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000:0' MINUTE(3) TO SECOND")
        .fails("Interval field value -1,000 exceeds precision of MINUTE\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648:0' MINUTE(10) TO SECOND")
        .fails("Interval field value 2,147,483,648 exceeds precision of MINUTE\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648:0' MINUTE(10) TO SECOND")
        .fails("Interval field value -2,147,483,648 exceeds precision of MINUTE\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1:60' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:60' for"
            + " INTERVAL MINUTE TO SECOND.*");
    f.wholeExpr("INTERVAL '1:1.0000001' MINUTE TO SECOND")
        .fails("Illegal interval literal format '1:1\\.0000001' for"
            + " INTERVAL MINUTE TO SECOND.*");
    f.wholeExpr("INTERVAL '1:1:1.0001' MINUTE TO SECOND(3)")
        .fails("Illegal interval literal format '1:1:1\\.0001' for"
            + " INTERVAL MINUTE TO SECOND\\(3\\).*");

    // precision > maximum
    f.expr("INTERVAL '1:1' ^MINUTE(11) TO SECOND^")
        .fails("Interval leading field precision '11' out of range for"
            + " INTERVAL MINUTE\\(11\\) TO SECOND");
    f.expr("INTERVAL '1:1' ^MINUTE TO SECOND(10)^")
        .fails("Interval fractional second precision '10' out of range for"
            + " INTERVAL MINUTE TO SECOND\\(10\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0:0' ^MINUTE(0) TO SECOND^")
        .fails("Interval leading field precision '0' out of range for"
            + " INTERVAL MINUTE\\(0\\) TO SECOND");
  }

  /**
   * Runs tests for INTERVAL... SECOND that should pass parser but fail
   * validator. A substantially identical set of tests exists in
   * SqlParserTest, and any changes here should be synchronized there.
   * Similarly, any changes to tests here should be echoed appropriately to
   * each of the other 12 subTestIntervalXXXNegative() tests.
   */
  public void subTestIntervalSecondNegative() {
    // Qualifier - field mismatches
    f.wholeExpr("INTERVAL ':' SECOND")
        .fails("Illegal interval literal format ':' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '.' SECOND")
        .fails("Illegal interval literal format '\\.' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1-2' SECOND")
        .fails("Illegal interval literal format '1-2' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1.x' SECOND")
        .fails("Illegal interval literal format '1\\.x' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL 'x.1' SECOND")
        .fails("Illegal interval literal format 'x\\.1' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1 2' SECOND")
        .fails("Illegal interval literal format '1 2' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1:2' SECOND")
        .fails("Illegal interval literal format '1:2' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1-2' SECOND(2)")
        .fails("Illegal interval literal format '1-2' for INTERVAL SECOND\\(2\\)");
    f.wholeExpr("INTERVAL 'bogus text' SECOND")
        .fails("Illegal interval literal format 'bogus text' for INTERVAL SECOND.*");

    // negative field values
    f.wholeExpr("INTERVAL '--1' SECOND")
        .fails("Illegal interval literal format '--1' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1.-1' SECOND")
        .fails("Illegal interval literal format '1.-1' for INTERVAL SECOND.*");

    // Field value out of range
    //  (default, explicit default, alt, neg alt, max, neg max)
    f.wholeExpr("INTERVAL '100' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    f.wholeExpr("INTERVAL '100' SECOND(2)")
        .fails("Interval field value 100 exceeds precision of SECOND\\(2\\) field.*");
    f.wholeExpr("INTERVAL '1000' SECOND(3)")
        .fails("Interval field value 1,000 exceeds precision of SECOND\\(3\\) field.*");
    f.wholeExpr("INTERVAL '-1000' SECOND(3)")
        .fails("Interval field value -1,000 exceeds precision of SECOND\\(3\\) field.*");
    f.wholeExpr("INTERVAL '2147483648' SECOND(10)")
        .fails("Interval field value 2,147,483,648 exceeds precision of SECOND\\(10\\) field.*");
    f.wholeExpr("INTERVAL '-2147483648' SECOND(10)")
        .fails("Interval field value -2,147,483,648 exceeds precision of SECOND\\(10\\) field.*");
    f.wholeExpr("INTERVAL '1.0000001' SECOND")
        .fails("Illegal interval literal format '1\\.0000001' for INTERVAL SECOND.*");
    f.wholeExpr("INTERVAL '1.0000001' SECOND(2)")
        .fails("Illegal interval literal format '1\\.0000001' for INTERVAL SECOND\\(2\\).*");
    f.wholeExpr("INTERVAL '1.0001' SECOND(2, 3)")
        .fails("Illegal interval literal format '1\\.0001' for INTERVAL SECOND\\(2, 3\\).*");
    f.wholeExpr("INTERVAL '1.0000000001' SECOND(2, 9)")
        .fails("Illegal interval literal format '1\\.0000000001' for"
            + " INTERVAL SECOND\\(2, 9\\).*");

    // precision > maximum
    f.expr("INTERVAL '1' ^SECOND(11)^")
        .fails("Interval leading field precision '11' out of range for"
            + " INTERVAL SECOND\\(11\\)");
    f.expr("INTERVAL '1.1' ^SECOND(1, 10)^")
        .fails("Interval fractional second precision '10' out of range for"
            + " INTERVAL SECOND\\(1, 10\\)");

    // precision < minimum allowed)
    // note: parser will catch negative values, here we
    // just need to check for 0
    f.expr("INTERVAL '0' ^SECOND(0)^")
        .fails("Interval leading field precision '0' out of range for"
            + " INTERVAL SECOND\\(0\\)");
  }

  public void subTestMisc() {
    // Miscellaneous
    // fractional value is not OK, even if it is 0
    f.wholeExpr("INTERVAL '1.0' HOUR")
        .fails("Illegal interval literal format '1.0' for INTERVAL HOUR");
    // only seconds are allowed to have a fractional part
    f.expr("INTERVAL '1.0' SECOND")
        .columnType("INTERVAL SECOND NOT NULL");
    // leading zeros do not cause precision to be exceeded
    f.expr("INTERVAL '0999' MONTH(3)")
        .columnType("INTERVAL MONTH(3) NOT NULL");
  }

  /** Fluent interface for binding an expression to create a fixture that can
   * be used to validate, check AST, or check type. */
  public interface Fixture {
    Fixture2 expr(String s);
    Fixture2 wholeExpr(String s);
  }

  /** Fluent interface to validate an expression. */
  public interface Fixture2 {
    /** Checks that the expression is valid in the parser
     * but invalid (with the given error message) in the validator. */
    void fails(String expected);

    /** Checks that the expression is valid in the parser and validator
     * and has the given column type. */
    void columnType(String expectedType);

    /** Checks that the expression parses successfully and produces the given
     * SQL when unparsed. */
    Fixture2 assertParse(String expectedAst);
  }
}