diff --git a/java/calcite-planner/pom.xml b/java/calcite-planner/pom.xml
index 9ec53d8b6..29f2f0d27 100644
--- a/java/calcite-planner/pom.xml
+++ b/java/calcite-planner/pom.xml
@@ -103,6 +103,79 @@ under the License.
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-resources
+ generate-sources
+
+ copy-resources
+
+
+ ${project.build.directory}/codegen
+
+
+ src/main/codegen
+ false
+
+
+
+
+
+
+
+ com.googlecode.fmpp-maven-plugin
+ fmpp-maven-plugin
+ 1.0
+
+
+ org.freemarker
+ freemarker
+ 2.3.32
+
+
+
+
+ generate-fmpp-sources
+ generate-sources
+
+ generate
+
+
+ ${project.build.directory}/codegen/config.fmpp
+ target/generated-sources
+ ${project.build.directory}/codegen/templates
+
+
+
+
+
+
+ org.codehaus.mojo
+ javacc-maven-plugin
+ 2.4
+
+
+ generate-sources
+ javacc
+
+ javacc
+
+
+ ${project.build.directory}/generated-sources/
+
+ **/Parser.jj
+
+
+ 1
+ false
+ ${project.build.directory}/generated-sources/
+
+
+
+
diff --git a/java/calcite-planner/src/main/codegen/config.fmpp b/java/calcite-planner/src/main/codegen/config.fmpp
new file mode 100644
index 000000000..b66528157
--- /dev/null
+++ b/java/calcite-planner/src/main/codegen/config.fmpp
@@ -0,0 +1,467 @@
+# 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.
+
+# Default data declarations for parsers.
+# Each of these may be overridden in a parser's config.fmpp file.
+# In addition, each parser must define "package" and "class".
+data: {
+parser: {
+ package: "org.apache.impala.calcite.parser",
+ class: "ImpalaSqlParserImpl",
+
+ # List of additional classes and packages to import.
+ # Example: "org.apache.calcite.sql.*", "java.util.List".
+ imports: [
+ ]
+
+ # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is
+ # not a reserved keyword, add it to the 'nonReservedKeywords' section.
+ keywords: [
+ ]
+
+ # List of keywords from "keywords" section that are not reserved.
+ nonReservedKeywords: [
+ "A"
+ "ABSENT"
+ "ABSOLUTE"
+ "ACTION"
+ "ADA"
+ "ADD"
+ "ADMIN"
+ "AFTER"
+ "ALWAYS"
+ "APPLY"
+ "ARRAY_AGG"
+ "ARRAY_CONCAT_AGG"
+ "ASC"
+ "ASSERTION"
+ "ASSIGNMENT"
+ "ATTRIBUTE"
+ "ATTRIBUTES"
+ "BEFORE"
+ "BERNOULLI"
+ "BREADTH"
+ "C"
+ "CASCADE"
+ "CATALOG"
+ "CATALOG_NAME"
+ "CENTURY"
+ "CHAIN"
+ "CHARACTERISTICS"
+ "CHARACTERS"
+ "CHARACTER_SET_CATALOG"
+ "CHARACTER_SET_NAME"
+ "CHARACTER_SET_SCHEMA"
+ "CLASS_ORIGIN"
+ "COBOL"
+ "COLLATION"
+ "COLLATION_CATALOG"
+ "COLLATION_NAME"
+ "COLLATION_SCHEMA"
+ "COLUMN_NAME"
+ "COMMAND_FUNCTION"
+ "COMMAND_FUNCTION_CODE"
+ "COMMITTED"
+ "CONDITIONAL"
+ "CONDITION_NUMBER"
+ "CONNECTION"
+ "CONNECTION_NAME"
+ "CONSTRAINT_CATALOG"
+ "CONSTRAINT_NAME"
+ "CONSTRAINTS"
+ "CONSTRAINT_SCHEMA"
+ "CONSTRUCTOR"
+ "CONTAINS_SUBSTR"
+ "CONTINUE"
+ "CURSOR_NAME"
+ "DATA"
+ "DATABASE"
+ "DATE_DIFF"
+ "DATE_TRUNC"
+ "DATETIME_DIFF"
+ "DATETIME_INTERVAL_CODE"
+ "DATETIME_INTERVAL_PRECISION"
+ "DATETIME_TRUNC"
+ "DAYOFWEEK"
+ "DAYOFYEAR"
+ "DAY"
+ "DAYS"
+ "DECADE"
+ "DEFAULTS"
+ "DEFERRABLE"
+ "DEFERRED"
+ "DEFINED"
+ "DEFINER"
+ "DEGREE"
+ "DEPTH"
+ "DERIVED"
+ "DESC"
+ "DESCRIPTION"
+ "DESCRIPTOR"
+ "DIAGNOSTICS"
+ "DISPATCH"
+ "DOMAIN"
+ "DOW"
+ "DOY"
+ "DOT_FORMAT"
+ "DYNAMIC_FUNCTION"
+ "DYNAMIC_FUNCTION_CODE"
+ "ENCODING"
+ "EPOCH"
+ "ERROR"
+ "EXCEPTION"
+ "EXCLUDE"
+ "EXCLUDING"
+ "FINAL"
+ "FIRST"
+ "FOLLOWING"
+ "FORMAT"
+ "FORTRAN"
+ "FOUND"
+ "FRAC_SECOND"
+ "G"
+ "GENERAL"
+ "GENERATED"
+ "GEOMETRY"
+ "GO"
+ "GOTO"
+ "GRANTED"
+ "GROUP_CONCAT"
+ "HIERARCHY"
+ "HOP"
+ "HOURS"
+ "IGNORE"
+ "ILIKE"
+ "IMMEDIATE"
+ "IMMEDIATELY"
+ "IMPLEMENTATION"
+ "INCLUDE"
+ "INCLUDING"
+ "INCREMENT"
+ "INITIALLY"
+ "INPUT"
+ "INSTANCE"
+ "INSTANTIABLE"
+ "INVOKER"
+ "ISODOW"
+ "ISOLATION"
+ "ISOYEAR"
+ "JAVA"
+ "JSON"
+ "K"
+ "KEY"
+ "KEY_MEMBER"
+ "KEY_TYPE"
+ "LABEL"
+ "LAST"
+ "LENGTH"
+ "LEVEL"
+ "LIBRARY"
+ "LOCATOR"
+ "M"
+ "MAP"
+ "MATCHED"
+ "MAXVALUE"
+ "MESSAGE_LENGTH"
+ "MESSAGE_OCTET_LENGTH"
+ "MESSAGE_TEXT"
+ "MICROSECOND"
+ "MILLENNIUM"
+ "MILLISECOND"
+ "MINUTES"
+ "MINVALUE"
+ "MONTH"
+ "MONTHS"
+ "MORE_"
+ "MUMPS"
+ "NAME"
+ "NAMES"
+ "NANOSECOND"
+ "NESTING"
+ "NORMALIZED"
+ "NULLABLE"
+ "NULLS"
+ "NUMBER"
+ "OBJECT"
+ "OCTETS"
+ "OPTION"
+ "OPTIONS"
+ "ORDERING"
+ "ORDINALITY"
+ "OTHERS"
+ "OUTPUT"
+ "OVERRIDING"
+ "PAD"
+ "PARAMETER_MODE"
+ "PARAMETER_NAME"
+ "PARAMETER_ORDINAL_POSITION"
+ "PARAMETER_SPECIFIC_CATALOG"
+ "PARAMETER_SPECIFIC_NAME"
+ "PARAMETER_SPECIFIC_SCHEMA"
+ "PARTIAL"
+ "PASCAL"
+ "PASSING"
+ "PASSTHROUGH"
+ "PAST"
+ "PATH"
+ "PIVOT"
+ "PLACING"
+ "PLAN"
+ "PLI"
+ "PRECEDING"
+ "PRESERVE"
+ "PRIOR"
+ "PRIVILEGES"
+ "PUBLIC"
+ "QUARTER"
+ "QUARTERS"
+ "READ"
+ "RELATIVE"
+ "REPEATABLE"
+ "REPLACE"
+ "RESPECT"
+ "RESTART"
+ "RESTRICT"
+ "RETURNED_CARDINALITY"
+ "RETURNED_LENGTH"
+ "RETURNED_OCTET_LENGTH"
+ "RETURNED_SQLSTATE"
+ "RETURNING"
+ "RLIKE"
+ "ROLE"
+ "ROUTINE"
+ "ROUTINE_CATALOG"
+ "ROUTINE_NAME"
+ "ROUTINE_SCHEMA"
+ "ROW_COUNT"
+ "SCALAR"
+ "SCALE"
+ "SCHEMA"
+ "SCHEMA_NAME"
+ "SCOPE_CATALOGS"
+ "SCOPE_NAME"
+ "SCOPE_SCHEMA"
+ "SECONDS"
+ "SECTION"
+ "SECURITY"
+ "SELF"
+ "SEPARATOR"
+ "SEQUENCE"
+ "SERIALIZABLE"
+ "SERVER"
+ "SERVER_NAME"
+ "SESSION"
+ "SETS"
+ "SIMPLE"
+ "SIZE"
+ "SOURCE"
+ "SPACE"
+ "SPECIFIC_NAME"
+ "SQL_BIGINT"
+ "SQL_BINARY"
+ "SQL_BIT"
+ "SQL_BLOB"
+ "SQL_BOOLEAN"
+ "SQL_CHAR"
+ "SQL_CLOB"
+ "SQL_DATE"
+ "SQL_DECIMAL"
+ "SQL_DOUBLE"
+ "SQL_FLOAT"
+ "SQL_INTEGER"
+ "SQL_INTERVAL_DAY"
+ "SQL_INTERVAL_DAY_TO_HOUR"
+ "SQL_INTERVAL_DAY_TO_MINUTE"
+ "SQL_INTERVAL_DAY_TO_SECOND"
+ "SQL_INTERVAL_HOUR"
+ "SQL_INTERVAL_HOUR_TO_MINUTE"
+ "SQL_INTERVAL_HOUR_TO_SECOND"
+ "SQL_INTERVAL_MINUTE"
+ "SQL_INTERVAL_MINUTE_TO_SECOND"
+ "SQL_INTERVAL_MONTH"
+ "SQL_INTERVAL_SECOND"
+ "SQL_INTERVAL_YEAR"
+ "SQL_INTERVAL_YEAR_TO_MONTH"
+ "SQL_LONGVARBINARY"
+ "SQL_LONGVARCHAR"
+ "SQL_LONGVARNCHAR"
+ "SQL_NCHAR"
+ "SQL_NCLOB"
+ "SQL_NUMERIC"
+ "SQL_NVARCHAR"
+ "SQL_REAL"
+ "SQL_SMALLINT"
+ "SQL_TIME"
+ "SQL_TIMESTAMP"
+ "SQL_TINYINT"
+ "SQL_TSI_DAY"
+ "SQL_TSI_FRAC_SECOND"
+ "SQL_TSI_HOUR"
+ "SQL_TSI_MICROSECOND"
+ "SQL_TSI_MINUTE"
+ "SQL_TSI_MONTH"
+ "SQL_TSI_QUARTER"
+ "SQL_TSI_SECOND"
+ "SQL_TSI_WEEK"
+ "SQL_TSI_YEAR"
+ "SQL_VARBINARY"
+ "SQL_VARCHAR"
+ "STATE"
+ "STATEMENT"
+ "STRING_AGG"
+ "STRUCTURE"
+ "STYLE"
+ "SUBCLASS_ORIGIN"
+ "SUBSTITUTE"
+ "TABLE_NAME"
+ "TEMPORARY"
+ "TIES"
+ "TIME_DIFF"
+ "TIME_TRUNC"
+ "TIMESTAMPADD"
+ "TIMESTAMPDIFF"
+ "TIMESTAMP_DIFF"
+ "TIMESTAMP_TRUNC"
+ "TOP_LEVEL_COUNT"
+ "TRANSACTION"
+ "TRANSACTIONS_ACTIVE"
+ "TRANSACTIONS_COMMITTED"
+ "TRANSACTIONS_ROLLED_BACK"
+ "TRANSFORM"
+ "TRANSFORMS"
+ "TRIGGER_CATALOG"
+ "TRIGGER_NAME"
+ "TRIGGER_SCHEMA"
+ "TUMBLE"
+ "TYPE"
+ "UNBOUNDED"
+ "UNCOMMITTED"
+ "UNCONDITIONAL"
+ "UNDER"
+ "UNPIVOT"
+ "UNNAMED"
+ "USAGE"
+ "USER_DEFINED_TYPE_CATALOG"
+ "USER_DEFINED_TYPE_CODE"
+ "USER_DEFINED_TYPE_NAME"
+ "USER_DEFINED_TYPE_SCHEMA"
+ "UTF16"
+ "UTF32"
+ "UTF8"
+ "VERSION"
+ "VIEW"
+ "WEEK"
+ "WEEKS"
+ "WORK"
+ "WRAPPER"
+ "WRITE"
+ "XML"
+ "YEAR"
+ "YEARS"
+ "ZONE"
+ ]
+
+ # List of non-reserved keywords to add;
+ # items in this list become non-reserved.
+ nonReservedKeywordsToAdd: [
+ ]
+
+ # List of non-reserved keywords to remove;
+ # items in this list become reserved.
+ nonReservedKeywordsToRemove: [
+ ]
+
+ # List of additional join types. Each is a method with no arguments.
+ # Example: "LeftSemiJoin".
+ joinTypes: [
+ ]
+
+ # List of methods for parsing custom SQL statements.
+ # Return type of method implementation should be 'SqlNode'.
+ # Example: "SqlShowDatabases()", "SqlShowTables()".
+ statementParserMethods: [
+ ]
+
+ # List of methods for parsing custom literals.
+ # Return type of method implementation should be "SqlNode".
+ # Example: ParseJsonLiteral().
+ literalParserMethods: [
+ ]
+
+ # List of methods for parsing custom data types.
+ # Return type of method implementation should be "SqlTypeNameSpec".
+ # Example: SqlParseTimeStampZ().
+ dataTypeParserMethods: [
+ ]
+
+ # List of methods for parsing builtin function calls.
+ # Return type of method implementation should be "SqlNode".
+ # Example: "DateTimeConstructorCall()".
+ builtinFunctionCallMethods: [
+ ]
+
+ # List of methods for parsing extensions to "ALTER " calls.
+ # Each must accept arguments "(SqlParserPos pos, String scope)".
+ # Example: "SqlAlterTable".
+ alterStatementParserMethods: [
+ ]
+
+ # List of methods for parsing extensions to "CREATE [OR REPLACE]" calls.
+ # Each must accept arguments "(SqlParserPos pos, boolean replace)".
+ # Example: "SqlCreateForeignSchema".
+ createStatementParserMethods: [
+ ]
+
+ # List of methods for parsing extensions to "DROP" calls.
+ # Each must accept arguments "(SqlParserPos pos)".
+ # Example: "SqlDropSchema".
+ dropStatementParserMethods: [
+ ]
+
+ # List of methods for parsing extensions to "TRUNCATE" calls.
+ # Each must accept arguments "(SqlParserPos pos)".
+ # Example: "SqlTruncate".
+ truncateStatementParserMethods: [
+ ]
+
+ # Binary operators tokens.
+ # Example: "< INFIX_CAST: \"::\" >".
+ binaryOperatorsTokens: [
+ ]
+
+ # Binary operators initialization.
+ # Example: "InfixCast".
+ extraBinaryExpressions: [
+ ]
+
+ # List of files in @includes directory that have parser method
+ # implementations for parsing custom SQL statements, literals or types
+ # given as part of "statementParserMethods", "literalParserMethods" or
+ # "dataTypeParserMethods".
+ # Example: "parserImpls.ftl".
+ implementationFiles: [
+ ]
+
+ # Custom identifier token.
+ # Example: "< IDENTIFIER: (|)+ >".
+ customIdentifierToken: ""
+
+ includePosixOperators: false
+ includeCompoundIdentifier: true
+ includeBraces: true
+ includeAdditionalDeclarations: false
+ includeParsingStringLiteralAsArrayLiteral: false
+}
+}
diff --git a/java/calcite-planner/src/main/codegen/templates/Parser.jj b/java/calcite-planner/src/main/codegen/templates/Parser.jj
new file mode 100644
index 000000000..8dddb1a99
--- /dev/null
+++ b/java/calcite-planner/src/main/codegen/templates/Parser.jj
@@ -0,0 +1,9071 @@
+/*
+ * 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.
+ */
+<@pp.dropOutputFile />
+
+<@pp.changeOutputFile name="javacc/Parser.jj" />
+
+options {
+ STATIC = false;
+ IGNORE_CASE = true;
+ UNICODE_INPUT = true;
+}
+
+
+PARSER_BEGIN(${parser.class})
+
+package ${parser.package};
+
+<#list (parser.imports!default.parser.imports) as importStr>
+import ${importStr};
+#list>
+
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.TimeUnit;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.runtime.CalciteContextException;
+import org.apache.calcite.sql.JoinConditionType;
+import org.apache.calcite.sql.JoinType;
+import org.apache.calcite.sql.SqlAlter;
+import org.apache.calcite.sql.SqlBasicTypeNameSpec;
+import org.apache.calcite.sql.SqlBinaryOperator;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlCharStringLiteral;
+import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.SqlCollectionTypeNameSpec;
+import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.sql.SqlDelete;
+import org.apache.calcite.sql.SqlDescribeSchema;
+import org.apache.calcite.sql.SqlDescribeTable;
+import org.apache.calcite.sql.SqlDynamicParam;
+import org.apache.calcite.sql.SqlExplain;
+import org.apache.calcite.sql.SqlExplainFormat;
+import org.apache.calcite.sql.SqlExplainLevel;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlHint;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlInsert;
+import org.apache.calcite.sql.SqlInsertKeyword;
+import org.apache.calcite.sql.SqlIntervalQualifier;
+import org.apache.calcite.sql.SqlJdbcDataTypeName;
+import org.apache.calcite.sql.SqlJdbcFunctionCall;
+import org.apache.calcite.sql.SqlJoin;
+import org.apache.calcite.sql.SqlJsonConstructorNullClause;
+import org.apache.calcite.sql.SqlJsonEncoding;
+import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
+import org.apache.calcite.sql.SqlJsonEmptyOrError;
+import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
+import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
+import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
+import org.apache.calcite.sql.SqlJsonValueReturning;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlMatchRecognize;
+import org.apache.calcite.sql.SqlMerge;
+import org.apache.calcite.sql.SqlMapTypeNameSpec;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlNumericLiteral;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlOrderBy;
+import org.apache.calcite.sql.SqlPivot;
+import org.apache.calcite.sql.SqlPostfixOperator;
+import org.apache.calcite.sql.SqlPrefixOperator;
+import org.apache.calcite.sql.SqlRowTypeNameSpec;
+import org.apache.calcite.sql.SqlSampleSpec;
+import org.apache.calcite.sql.SqlSelect;
+import org.apache.calcite.sql.SqlSelectKeyword;
+import org.apache.calcite.sql.SqlSetOption;
+import org.apache.calcite.sql.SqlSnapshot;
+import org.apache.calcite.sql.SqlTableRef;
+import org.apache.calcite.sql.SqlTypeNameSpec;
+import org.apache.calcite.sql.SqlUnnestOperator;
+import org.apache.calcite.sql.SqlUnpivot;
+import org.apache.calcite.sql.SqlUpdate;
+import org.apache.calcite.sql.SqlUserDefinedTypeNameSpec;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWindow;
+import org.apache.calcite.sql.SqlWith;
+import org.apache.calcite.sql.SqlWithItem;
+import org.apache.calcite.sql.fun.SqlCase;
+import org.apache.calcite.sql.fun.SqlInternalOperators;
+import org.apache.calcite.sql.fun.SqlLibraryOperators;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.fun.SqlTrimFunction;
+import org.apache.calcite.sql.parser.Span;
+import org.apache.calcite.sql.parser.SqlAbstractParserImpl;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.parser.SqlParserImplFactory;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.parser.SqlParserUtil;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
+import org.apache.calcite.util.Glossary;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.SourceStringReader;
+import org.apache.calcite.util.Util;
+import org.apache.calcite.util.trace.CalciteTrace;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.slf4j.Logger;
+
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * SQL parser, generated from Parser.jj by JavaCC.
+ *
+ *
The public wrapper for this parser is {@link SqlParser}.
+ */
+public class ${parser.class} extends SqlAbstractParserImpl
+{
+ private static final Logger LOGGER = CalciteTrace.getParserTracer();
+
+ // Can't use quoted literal because of a bug in how JavaCC translates
+ // backslash-backslash.
+ private static final char BACKSLASH = 0x5c;
+ private static final char DOUBLE_QUOTE = 0x22;
+ private static final String DQ = DOUBLE_QUOTE + "";
+ private static final String DQDQ = DQ + DQ;
+ private static final SqlLiteral LITERAL_ZERO =
+ SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO);
+ private static final SqlLiteral LITERAL_ONE =
+ SqlLiteral.createExactNumeric("1", SqlParserPos.ZERO);
+ private static final SqlLiteral LITERAL_MINUS_ONE =
+ SqlLiteral.createExactNumeric("-1", SqlParserPos.ZERO);
+ private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L);
+
+ private static Metadata metadata;
+
+ private Casing unquotedCasing;
+ private Casing quotedCasing;
+ private int identifierMaxLength;
+ private SqlConformance conformance;
+
+ /**
+ * {@link SqlParserImplFactory} implementation for creating parser.
+ */
+ public static final SqlParserImplFactory FACTORY = new SqlParserImplFactory() {
+ public SqlAbstractParserImpl getParser(Reader reader) {
+ final ${parser.class} parser = new ${parser.class}(reader);
+ if (reader instanceof SourceStringReader) {
+ final String sql =
+ ((SourceStringReader) reader).getSourceString();
+ parser.setOriginalSql(sql);
+ }
+ return parser;
+ }
+ };
+
+ public SqlParseException normalizeException(Throwable ex) {
+ try {
+ if (ex instanceof ParseException) {
+ ex = cleanupParseException((ParseException) ex);
+ }
+ return convertException(ex);
+ } catch (ParseException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public Metadata getMetadata() {
+ synchronized (${parser.class}.class) {
+ if (metadata == null) {
+ metadata = new MetadataImpl(
+ new ${parser.class}(new java.io.StringReader("")));
+ }
+ return metadata;
+ }
+ }
+
+ public void setTabSize(int tabSize) {
+ jj_input_stream.setTabSize(tabSize);
+ }
+
+ public void switchTo(SqlAbstractParserImpl.LexicalState state) {
+ final int stateOrdinal =
+ Arrays.asList(${parser.class}TokenManager.lexStateNames)
+ .indexOf(state.name());
+ token_source.SwitchTo(stateOrdinal);
+ }
+
+ public void setQuotedCasing(Casing quotedCasing) {
+ this.quotedCasing = quotedCasing;
+ }
+
+ public void setUnquotedCasing(Casing unquotedCasing) {
+ this.unquotedCasing = unquotedCasing;
+ }
+
+ public void setIdentifierMaxLength(int identifierMaxLength) {
+ this.identifierMaxLength = identifierMaxLength;
+ }
+
+ public void setConformance(SqlConformance conformance) {
+ this.conformance = conformance;
+ }
+
+ public SqlNode parseSqlExpressionEof() throws Exception {
+ return SqlExpressionEof();
+ }
+
+ public SqlNode parseSqlStmtEof() throws Exception {
+ return SqlStmtEof();
+ }
+
+ public SqlNodeList parseSqlStmtList() throws Exception {
+ return SqlStmtList();
+ }
+
+ public SqlNode parseArray() throws SqlParseException {
+ switchTo(LexicalState.BQID);
+ try {
+ return ArrayLiteral();
+ } catch (ParseException ex) {
+ throw normalizeException(ex);
+ } catch (TokenMgrError ex) {
+ throw normalizeException(ex);
+ }
+ }
+
+ private SqlNode extend(SqlNode table, SqlNodeList extendList) {
+ return SqlStdOperatorTable.EXTEND.createCall(
+ Span.of(table, extendList).pos(), table, extendList);
+ }
+
+ /** Adds a warning that a token such as "HOURS" was used,
+ * whereas the SQL standard only allows "HOUR".
+ *
+ *
Currently, we silently add an exception to a list of warnings. In
+ * future, we may have better compliance checking, for example a strict
+ * compliance mode that throws if any non-standard features are used. */
+ private TimeUnit warn(TimeUnit timeUnit) throws ParseException {
+ final String token = getToken(0).image.toUpperCase(Locale.ROOT);
+ warnings.add(
+ SqlUtil.newContextException(getPos(),
+ RESOURCE.nonStandardFeatureUsed(token)));
+ return timeUnit;
+ }
+}
+
+PARSER_END(${parser.class})
+
+
+/***************************************
+ * Utility Codes for Semantic Analysis *
+ ***************************************/
+
+/* For Debug */
+JAVACODE
+void debug_message1() {
+ LOGGER.info("{} , {}", getToken(0).image, getToken(1).image);
+}
+
+JAVACODE String unquotedIdentifier() {
+ return SqlParserUtil.toCase(getToken(0).image, unquotedCasing);
+}
+
+/**
+ * Allows parser to be extended with new types of table references. The
+ * default implementation of this production is empty.
+ */
+SqlNode ExtendedTableRef() :
+{
+}
+{
+ UnusedExtension()
+ {
+ return null;
+ }
+}
+
+/**
+ * Allows an OVER clause following a table expression as an extension to
+ * standard SQL syntax. The default implementation of this production is empty.
+ */
+SqlNode TableOverOpt() :
+{
+}
+{
+ {
+ return null;
+ }
+}
+
+/*
+ * Parses dialect-specific keywords immediately following the SELECT keyword.
+ */
+void SqlSelectKeywords(List keywords) :
+{}
+{
+ E()
+}
+
+/*
+ * Parses dialect-specific keywords immediately following the INSERT keyword.
+ */
+void SqlInsertKeywords(List keywords) :
+{}
+{
+ E()
+}
+
+/*
+* Parse Floor/Ceil function parameters
+*/
+SqlNode FloorCeilOptions(Span s, boolean floorFlag) :
+{
+ SqlNode node;
+}
+{
+ node = StandardFloorCeilOptions(s, floorFlag) {
+ return node;
+ }
+}
+
+/*
+// This file contains the heart of a parser for SQL SELECT statements.
+// code can be shared between various parsers (for example, a DDL parser and a
+// DML parser) but is not a standalone JavaCC file. You need to prepend a
+// parser declaration (such as that in Parser.jj).
+*/
+
+/* Epsilon */
+JAVACODE
+void E() {}
+
+/** @Deprecated */
+JAVACODE List startList(Object o)
+{
+ List list = new ArrayList();
+ list.add(o);
+ return list;
+}
+
+/*
+ * NOTE jvs 6-Feb-2004: The straightforward way to implement the SQL grammar is
+ * to keep query expressions (SELECT, UNION, etc) separate from row expressions
+ * (+, LIKE, etc). However, this is not possible with an LL(k) parser, because
+ * both kinds of expressions allow parenthesization, so no fixed amount of left
+ * context is ever good enough. A sub-query can be a leaf in a row expression,
+ * and can include operators like UNION, so it's not even possible to use a
+ * syntactic lookahead rule like "look past an indefinite number of parentheses
+ * until you see SELECT, VALUES, or TABLE" (since at that point we still
+ * don't know whether we're parsing a sub-query like ((select ...) + x)
+ * vs. (select ... union select ...).
+ *
+ * The somewhat messy solution is to unify the two kinds of expression,
+ * and to enforce syntax rules using parameterized context. This
+ * is the purpose of the ExprContext parameter. It is passed to
+ * most expression productions, which check the expressions encountered
+ * against the context for correctness. When a query
+ * element like SELECT is encountered, the production calls
+ * checkQueryExpression, which will throw an exception if
+ * a row expression was expected instead. When a row expression like
+ * IN is encountered, the production calls checkNonQueryExpression
+ * instead. It is very important to understand how this works
+ * when modifying the grammar.
+ *
+ * The commingling of expressions results in some bogus ambiguities which are
+ * resolved with LOOKAHEAD hints. The worst example is comma. SQL allows both
+ * (WHERE x IN (1,2)) and (WHERE x IN (select ...)). This means when we parse
+ * the right-hand-side of an IN, we have to allow any kind of expression inside
+ * the parentheses. Now consider the expression "WHERE x IN(SELECT a FROM b
+ * GROUP BY c,d)". When the parser gets to "c,d" it doesn't know whether the
+ * comma indicates the end of the GROUP BY or the end of one item in an IN
+ * list. Luckily, we know that select and comma-list are mutually exclusive
+ * within IN, so we use maximal munch for the GROUP BY comma. However, this
+ * usage of hints could easily mask unintended ambiguities resulting from
+ * future changes to the grammar, making it very brittle.
+ */
+
+JAVACODE protected SqlParserPos getPos()
+{
+ return new SqlParserPos(
+ token.beginLine,
+ token.beginColumn,
+ token.endLine,
+ token.endColumn);
+}
+
+/** Starts a span at the current position. */
+JAVACODE Span span()
+{
+ return Span.of(getPos());
+}
+
+JAVACODE void checkQueryExpression(ExprContext exprContext)
+{
+ switch (exprContext) {
+ case ACCEPT_NON_QUERY:
+ case ACCEPT_SUB_QUERY:
+ case ACCEPT_CURSOR:
+ throw SqlUtil.newContextException(getPos(),
+ RESOURCE.illegalQueryExpression());
+ }
+}
+
+JAVACODE void checkNonQueryExpression(ExprContext exprContext)
+{
+ switch (exprContext) {
+ case ACCEPT_QUERY:
+ throw SqlUtil.newContextException(getPos(),
+ RESOURCE.illegalNonQueryExpression());
+ }
+}
+
+JAVACODE SqlNode checkNotJoin(SqlNode e)
+{
+ if (e instanceof SqlJoin) {
+ throw SqlUtil.newContextException(e.getParserPosition(),
+ RESOURCE.illegalJoinExpression());
+ }
+ return e;
+}
+
+/**
+ * Converts a ParseException (local to this particular instantiation
+ * of the parser) into a SqlParseException (common to all parsers).
+ */
+JAVACODE SqlParseException convertException(Throwable ex)
+{
+ if (ex instanceof SqlParseException) {
+ return (SqlParseException) ex;
+ }
+ SqlParserPos pos = null;
+ int[][] expectedTokenSequences = null;
+ String[] tokenImage = null;
+ if (ex instanceof ParseException) {
+ ParseException pex = (ParseException) ex;
+ expectedTokenSequences = pex.expectedTokenSequences;
+ tokenImage = pex.tokenImage;
+ if (pex.currentToken != null) {
+ final Token token = pex.currentToken.next;
+ // Checks token.image.equals("1") to avoid recursive call.
+ // The SqlAbstractParserImpl#MetadataImpl constructor uses constant "1" to
+ // throw intentionally to collect the expected tokens.
+ if (!token.image.equals("1")
+ && getMetadata().isKeyword(token.image)
+ && SqlParserUtil.allowsIdentifier(tokenImage, expectedTokenSequences)) {
+ // If the next token is a keyword, reformat the error message as:
+
+ // Incorrect syntax near the keyword '{keyword}' at line {line_number},
+ // column {column_number}.
+ final String expecting = ex.getMessage()
+ .substring(ex.getMessage().indexOf("Was expecting"));
+ final String errorMsg = String.format("Incorrect syntax near the keyword '%s' "
+ + "at line %d, column %d.\n%s",
+ token.image,
+ token.beginLine,
+ token.beginColumn,
+ expecting);
+ // Replace the ParseException with explicit error message.
+ ex = new ParseException(errorMsg);
+ }
+ pos = new SqlParserPos(
+ token.beginLine,
+ token.beginColumn,
+ token.endLine,
+ token.endColumn);
+ }
+ } else if (ex instanceof TokenMgrError) {
+ expectedTokenSequences = null;
+ tokenImage = null;
+ // Example:
+ // Lexical error at line 3, column 24. Encountered "#" after "a".
+ final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
+ "(?s)Lexical error at line ([0-9]+), column ([0-9]+).*");
+ java.util.regex.Matcher matcher = pattern.matcher(ex.getMessage());
+ if (matcher.matches()) {
+ int line = Integer.parseInt(matcher.group(1));
+ int column = Integer.parseInt(matcher.group(2));
+ pos = new SqlParserPos(line, column, line, column);
+ }
+ } else if (ex instanceof CalciteContextException) {
+ // CalciteContextException is the standard wrapper for exceptions
+ // produced by the validator, but in the parser, the standard is
+ // SqlParseException; so, strip it away. In case you were wondering,
+ // the CalciteContextException appears because the parser
+ // occasionally calls into validator-style code such as
+ // SqlSpecialOperator.reduceExpr.
+ CalciteContextException ece =
+ (CalciteContextException) ex;
+ pos = new SqlParserPos(
+ ece.getPosLine(),
+ ece.getPosColumn(),
+ ece.getEndPosLine(),
+ ece.getEndPosColumn());
+ ex = ece.getCause();
+ }
+
+ return new SqlParseException(
+ ex.getMessage(), pos, expectedTokenSequences, tokenImage, ex);
+}
+
+/**
+ * Removes or transforms misleading information from a parse exception.
+ *
+ * @param e dirty excn
+ *
+ * @return clean excn
+ */
+JAVACODE ParseException cleanupParseException(ParseException ex)
+{
+ if (ex.expectedTokenSequences == null) {
+ return ex;
+ }
+ int iIdentifier = Arrays.asList(ex.tokenImage).indexOf("");
+
+ // Find all sequences in the error which contain identifier. For
+ // example,
+ // {}
+ // {A}
+ // {B, C}
+ // {D, }
+ // {D, A}
+ // {D, B}
+ //
+ // would yield
+ // {}
+ // {D}
+ final List prefixList = new ArrayList();
+ for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
+ int[] seq = ex.expectedTokenSequences[i];
+ int j = seq.length - 1;
+ int i1 = seq[j];
+ if (i1 == iIdentifier) {
+ int[] prefix = new int[j];
+ System.arraycopy(seq, 0, prefix, 0, j);
+ prefixList.add(prefix);
+ }
+ }
+
+ if (prefixList.isEmpty()) {
+ return ex;
+ }
+
+ int[][] prefixes = (int[][])
+ prefixList.toArray(new int[prefixList.size()][]);
+
+ // Since was one of the possible productions,
+ // we know that the parser will also have included all
+ // of the non-reserved keywords (which are treated as
+ // identifiers in non-keyword contexts). So, now we need
+ // to clean those out, since they're totally irrelevant.
+
+ final List list = new ArrayList();
+ Metadata metadata = getMetadata();
+ for (int i = 0; i < ex.expectedTokenSequences.length; ++i) {
+ int [] seq = ex.expectedTokenSequences[i];
+ String tokenImage = ex.tokenImage[seq[seq.length - 1]];
+ String token = SqlParserUtil.getTokenVal(tokenImage);
+ if (token == null || !metadata.isNonReservedKeyword(token)) {
+ list.add(seq);
+ continue;
+ }
+ boolean match = matchesPrefix(seq, prefixes);
+ if (!match) {
+ list.add(seq);
+ }
+ }
+
+ ex.expectedTokenSequences =
+ (int [][]) list.toArray(new int [list.size()][]);
+ return ex;
+}
+
+JAVACODE boolean matchesPrefix(int[] seq, int[][] prefixes)
+{
+ nextPrefix:
+ for (int[] prefix : prefixes) {
+ if (seq.length == prefix.length + 1) {
+ for (int k = 0; k < prefix.length; k++) {
+ if (prefix[k] != seq[k]) {
+ continue nextPrefix;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/*****************************************
+ * Syntactical Descriptions *
+ *****************************************/
+
+SqlNode ExprOrJoinOrOrderedQuery(ExprContext exprContext) :
+{
+ SqlNode e;
+ final List