| /* |
| * 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. |
| */ |
| |
| // To have Drill use this parser grammar instead of Calcite's (normally |
| // in order to exclude incompatibilities that have been introduced |
| // upstream), rename this file to Parser.jj, otherwise use the name |
| // Parser.jj.disabled. Currently this parser grammar is in effect because of |
| // CALCITE-5469 which should be resolved by CALCITE-5579 after which it can be |
| // disabled. Also see the calcite-parser-grammar profile in |
| // exec/java-exec/pom.xml. |
| |
| <@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.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.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. |
| * |
| * <p>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 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". |
| * |
| * <p>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<SqlLiteral> keywords) : |
| {} |
| { |
| E() |
| } |
| |
| /* |
| * Parses dialect-specific keywords immediately following the INSERT keyword. |
| */ |
| void SqlInsertKeywords(List<SqlLiteral> 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("<IDENTIFIER>"); |
| |
| // Find all sequences in the error which contain identifier. For |
| // example, |
| // {<IDENTIFIER>} |
| // {A} |
| // {B, C} |
| // {D, <IDENTIFIER>} |
| // {D, A} |
| // {D, B} |
| // |
| // would yield |
| // {} |
| // {D} |
| final List<int[]> prefixList = new ArrayList<int[]>(); |
| 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 <IDENTIFIER> 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<int[]> list = new ArrayList<int[]>(); |
| 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<Object> list = new ArrayList<Object>(); |
| } |
| { |
| // Lookhead to distinguish between "TABLE emp" (which will be |
| // matched by ExplicitTable() via Query()) |
| // and "TABLE fun(args)" (which will be matched by TableRef()) |
| ( |
| LOOKAHEAD(2) |
| e = Query(exprContext) |
| e = OrderByLimitOpt(e) |
| { return e; } |
| | |
| e = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN) |
| ( e = JoinTable(e) )* |
| { list.add(e); } |
| ( AddSetOpQuery(list, exprContext) )* |
| { return SqlParserUtil.toTree(list); } |
| ) |
| } |
| |
| /** |
| * Parses either a row expression or a query expression with an optional |
| * ORDER BY. |
| * |
| * <p>Postgres syntax for limit: |
| * |
| * <blockquote><pre> |
| * [ LIMIT { count | ALL } ] |
| * [ OFFSET start ]</pre> |
| * </blockquote> |
| * |
| * <p>Trino syntax for limit: |
| * |
| * <blockquote><pre> |
| * [ OFFSET start ] |
| * [ LIMIT { count | ALL } ]</pre> |
| * </blockquote> |
| * |
| * <p>MySQL syntax for limit: |
| * |
| * <blockquote><pre> |
| * [ LIMIT { count | start, count } ]</pre> |
| * </blockquote> |
| * |
| * <p>SQL:2008 syntax for limit: |
| * |
| * <blockquote><pre> |
| * [ OFFSET start { ROW | ROWS } ] |
| * [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]</pre> |
| * </blockquote> |
| */ |
| SqlNode OrderedQueryOrExpr(ExprContext exprContext) : |
| { |
| SqlNode e; |
| } |
| { |
| e = QueryOrExpr(exprContext) |
| e = OrderByLimitOpt(e) |
| { return e; } |
| } |
| |
| /** Reads optional "ORDER BY", "LIMIT", "OFFSET", "FETCH" following a query, |
| * {@code e}. If any of them are present, adds them to the query; |
| * otherwise returns the query unchanged. |
| * Throws if they are present and {@code e} is not a query. */ |
| SqlNode OrderByLimitOpt(SqlNode e) : |
| { |
| final SqlNodeList orderBy; |
| final Span s = Span.of(); |
| SqlNode[] offsetFetch = {null, null}; |
| } |
| { |
| ( |
| // use the syntactic type of the expression we just parsed |
| // to decide whether ORDER BY makes sense |
| orderBy = OrderBy(e.isA(SqlKind.QUERY)) |
| | { orderBy = null; } |
| ) |
| [ |
| LimitClause(s, offsetFetch) |
| [ OffsetClause(s, offsetFetch) ] |
| | |
| OffsetClause(s, offsetFetch) |
| [ |
| LimitClause(s, offsetFetch) { |
| if (!this.conformance.isOffsetLimitAllowed()) { |
| throw SqlUtil.newContextException(s.end(this), |
| RESOURCE.offsetLimitNotAllowed()); |
| } |
| } |
| | |
| FetchClause(offsetFetch) |
| ] |
| | |
| FetchClause(offsetFetch) |
| ] |
| { |
| if (orderBy != null || offsetFetch[0] != null || offsetFetch[1] != null) { |
| return new SqlOrderBy(getPos(), e, |
| Util.first(orderBy, SqlNodeList.EMPTY), |
| offsetFetch[0], offsetFetch[1]); |
| } |
| return e; |
| } |
| } |
| |
| /** |
| * Parses an OFFSET clause in an ORDER BY expression. |
| */ |
| void OffsetClause(Span s, SqlNode[] offsetFetch) : |
| { |
| } |
| { |
| // ROW or ROWS is required in SQL:2008 but we make it optional |
| // because it is not present in Postgres-style syntax. |
| <OFFSET> { s.add(this); } |
| offsetFetch[0] = UnsignedNumericLiteralOrParam() |
| [ <ROW> | <ROWS> ] |
| } |
| |
| /** |
| * Parses a FETCH clause in an ORDER BY expression. |
| */ |
| void FetchClause(SqlNode[] offsetFetch) : |
| { |
| } |
| { |
| // SQL:2008-style syntax. "OFFSET ... FETCH ...". |
| // If you specify both LIMIT and FETCH, FETCH wins. |
| <FETCH> ( <FIRST> | <NEXT> ) offsetFetch[1] = UnsignedNumericLiteralOrParam() |
| ( <ROW> | <ROWS> ) <ONLY> |
| } |
| |
| /** |
| * Parses a LIMIT clause in an ORDER BY expression. |
| */ |
| void LimitClause(Span s, SqlNode[] offsetFetch) : |
| { |
| } |
| { |
| // Postgres-style syntax. "LIMIT ... OFFSET ..." |
| <LIMIT> { s.add(this); } |
| ( |
| // MySQL-style syntax. "LIMIT start, count" |
| LOOKAHEAD(2) |
| offsetFetch[0] = UnsignedNumericLiteralOrParam() |
| <COMMA> offsetFetch[1] = UnsignedNumericLiteralOrParam() { |
| if (!this.conformance.isLimitStartCountAllowed()) { |
| throw SqlUtil.newContextException(s.end(this), |
| RESOURCE.limitStartCountNotAllowed()); |
| } |
| } |
| | |
| offsetFetch[1] = UnsignedNumericLiteralOrParam() |
| | |
| <ALL> |
| ) |
| } |
| |
| /** |
| * Parses a leaf in a query expression (SELECT, VALUES or TABLE). |
| */ |
| SqlNode LeafQuery(ExprContext exprContext) : |
| { |
| SqlNode e; |
| } |
| { |
| { |
| // ensure a query is legal in this context |
| checkQueryExpression(exprContext); |
| } |
| e = SqlSelect() { return e; } |
| | |
| e = TableConstructor() { return e; } |
| | |
| e = ExplicitTable(getPos()) { return e; } |
| } |
| |
| /** |
| * Parses a parenthesized query or single row expression. |
| * Depending on {@code exprContext}, may also accept a join. |
| */ |
| SqlNode ParenthesizedExpression(ExprContext exprContext) : |
| { |
| SqlNode e; |
| } |
| { |
| <LPAREN> |
| { |
| // we've now seen left paren, so queries inside should |
| // be allowed as sub-queries |
| switch (exprContext) { |
| case ACCEPT_SUB_QUERY: |
| exprContext = ExprContext.ACCEPT_NONCURSOR; |
| break; |
| case ACCEPT_CURSOR: |
| exprContext = ExprContext.ACCEPT_ALL; |
| break; |
| } |
| } |
| e = ExprOrJoinOrOrderedQuery(exprContext) |
| <RPAREN> |
| { |
| exprContext.throwIfNotCompatible(e); |
| return e; |
| } |
| } |
| |
| /** |
| * Parses a parenthesized query or comma-list of row expressions. |
| * |
| * <p>REVIEW jvs 8-Feb-2004: There's a small hole in this production. It can be |
| * used to construct something like |
| * |
| * <blockquote><pre> |
| * WHERE x IN (select count(*) from t where c=d,5)</pre> |
| * </blockquote> |
| * |
| * <p>which should be illegal. The above is interpreted as equivalent to |
| * |
| * <blockquote><pre> |
| * WHERE x IN ((select count(*) from t where c=d),5)</pre> |
| * </blockquote> |
| * |
| * <p>which is a legal use of a sub-query. The only way to fix the hole is to |
| * be able to remember whether a subexpression was parenthesized or not, which |
| * means preserving parentheses in the SqlNode tree. This is probably |
| * desirable anyway for use in purely syntactic parsing applications (e.g. SQL |
| * pretty-printer). However, if this is done, it's important to also make |
| * isA() on the paren node call down to its operand so that we can |
| * always correctly discriminate a query from a row expression. |
| */ |
| SqlNodeList ParenthesizedQueryOrCommaList( |
| ExprContext exprContext) : |
| { |
| SqlNode e; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| ExprContext firstExprContext = exprContext; |
| final Span s; |
| } |
| { |
| <LPAREN> |
| { |
| // we've now seen left paren, so a query by itself should |
| // be interpreted as a sub-query |
| s = span(); |
| switch (exprContext) { |
| case ACCEPT_SUB_QUERY: |
| firstExprContext = ExprContext.ACCEPT_NONCURSOR; |
| break; |
| case ACCEPT_CURSOR: |
| firstExprContext = ExprContext.ACCEPT_ALL; |
| break; |
| } |
| } |
| e = OrderedQueryOrExpr(firstExprContext) { list.add(e); } |
| ( |
| <COMMA> |
| { |
| // a comma-list can't appear where only a query is expected |
| checkNonQueryExpression(exprContext); |
| } |
| AddExpression(list, exprContext) |
| )* |
| <RPAREN> |
| { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| /** As ParenthesizedQueryOrCommaList, but allows DEFAULT |
| * in place of any of the expressions. For example, |
| * {@code (x, DEFAULT, null, DEFAULT)}. */ |
| SqlNodeList ParenthesizedQueryOrCommaListWithDefault( |
| ExprContext exprContext) : |
| { |
| SqlNode e; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| ExprContext firstExprContext = exprContext; |
| final Span s; |
| } |
| { |
| <LPAREN> |
| { |
| // we've now seen left paren, so a query by itself should |
| // be interpreted as a sub-query |
| s = span(); |
| switch (exprContext) { |
| case ACCEPT_SUB_QUERY: |
| firstExprContext = ExprContext.ACCEPT_NONCURSOR; |
| break; |
| case ACCEPT_CURSOR: |
| firstExprContext = ExprContext.ACCEPT_ALL; |
| break; |
| } |
| } |
| ( |
| e = OrderedQueryOrExpr(firstExprContext) { list.add(e); } |
| | |
| e = Default() { list.add(e); } |
| ) |
| ( |
| <COMMA> |
| { |
| // a comma-list can't appear where only a query is expected |
| checkNonQueryExpression(exprContext); |
| } |
| ( |
| e = Expression(exprContext) { list.add(e); } |
| | |
| e = Default() { list.add(e); } |
| ) |
| )* |
| <RPAREN> |
| { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| /** |
| * Parses function parameter lists. |
| * If the list starts with DISTINCT or ALL, it is discarded. |
| */ |
| List<SqlNode> UnquantifiedFunctionParameterList(ExprContext exprContext) : |
| { |
| final List<SqlNode> args; |
| } |
| { |
| args = FunctionParameterList(exprContext) { |
| args.remove(0); // remove DISTINCT or ALL, if present |
| return args; |
| } |
| } |
| |
| /** |
| * Parses function parameter lists including DISTINCT keyword recognition, |
| * DEFAULT, and named argument assignment. |
| */ |
| List<SqlNode> FunctionParameterList(ExprContext exprContext) : |
| { |
| final SqlLiteral qualifier; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| <LPAREN> |
| ( |
| qualifier = AllOrDistinct() { list.add(qualifier); } |
| | |
| { list.add(null); } |
| ) |
| AddArg0(list, exprContext) |
| ( |
| <COMMA> { |
| // a comma-list can't appear where only a query is expected |
| checkNonQueryExpression(exprContext); |
| } |
| AddArg(list, exprContext) |
| )* |
| <RPAREN> |
| { |
| return list; |
| } |
| } |
| |
| SqlLiteral AllOrDistinct() : |
| { |
| } |
| { |
| <DISTINCT> { return SqlSelectKeyword.DISTINCT.symbol(getPos()); } |
| | |
| <ALL> { return SqlSelectKeyword.ALL.symbol(getPos()); } |
| } |
| |
| void AddArg0(List<SqlNode> list, ExprContext exprContext) : |
| { |
| final SqlIdentifier name; |
| SqlNode e; |
| final ExprContext firstExprContext; |
| { |
| // we've now seen left paren, so queries inside should |
| // be allowed as sub-queries |
| switch (exprContext) { |
| case ACCEPT_SUB_QUERY: |
| firstExprContext = ExprContext.ACCEPT_NONCURSOR; |
| break; |
| case ACCEPT_CURSOR: |
| firstExprContext = ExprContext.ACCEPT_ALL; |
| break; |
| default: |
| firstExprContext = exprContext; |
| break; |
| } |
| } |
| } |
| { |
| ( |
| LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> |
| | { name = null; } |
| ) |
| ( |
| e = Default() |
| | |
| LOOKAHEAD(3) |
| e = TableParam() |
| | |
| e = PartitionedQueryOrQueryOrExpr(firstExprContext) |
| ) |
| { |
| if (name != null) { |
| e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( |
| Span.of(name, e).pos(), e, name); |
| } |
| list.add(e); |
| } |
| } |
| |
| void AddArg(List<SqlNode> list, ExprContext exprContext) : |
| { |
| final SqlIdentifier name; |
| SqlNode e; |
| } |
| { |
| ( |
| LOOKAHEAD(2) name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT> |
| | { name = null; } |
| ) |
| ( |
| e = Default() |
| | |
| e = Expression(exprContext) |
| | |
| e = TableParam() |
| ) |
| { |
| if (name != null) { |
| e = SqlStdOperatorTable.ARGUMENT_ASSIGNMENT.createCall( |
| Span.of(name, e).pos(), e, name); |
| } |
| list.add(e); |
| } |
| } |
| |
| SqlNode Default() : {} |
| { |
| <DEFAULT_> { |
| return SqlStdOperatorTable.DEFAULT.createCall(getPos()); |
| } |
| } |
| |
| /** |
| * Parses a query (SELECT, UNION, INTERSECT, EXCEPT, VALUES, TABLE) followed by |
| * the end-of-file symbol. |
| */ |
| SqlNode SqlQueryEof() : |
| { |
| SqlNode query; |
| } |
| { |
| query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| <EOF> |
| { return query; } |
| } |
| |
| /** |
| * Parses a list of SQL statements separated by semicolon. |
| * The semicolon is required between statements, but is |
| * optional at the end. |
| */ |
| SqlNodeList SqlStmtList() : |
| { |
| final List<SqlNode> stmtList = new ArrayList<SqlNode>(); |
| SqlNode stmt; |
| } |
| { |
| stmt = SqlStmt() { |
| stmtList.add(stmt); |
| } |
| ( |
| <SEMICOLON> |
| [ |
| stmt = SqlStmt() { |
| stmtList.add(stmt); |
| } |
| ] |
| )* |
| <EOF> |
| { |
| return new SqlNodeList(stmtList, Span.of(stmtList).pos()); |
| } |
| } |
| |
| /** |
| * Parses an SQL statement. |
| */ |
| SqlNode SqlStmt() : |
| { |
| SqlNode stmt; |
| } |
| { |
| ( |
| <#-- Add methods to parse additional statements here --> |
| <#list (parser.statementParserMethods!default.parser.statementParserMethods) as method> |
| LOOKAHEAD(2) stmt = ${method} |
| | |
| </#list> |
| stmt = SqlSetOption(Span.of(), null) |
| | |
| stmt = SqlAlter() |
| | |
| <#if (parser.createStatementParserMethods!default.parser.createStatementParserMethods)?size != 0> |
| stmt = SqlCreate() |
| | |
| </#if> |
| <#if (parser.dropStatementParserMethods!default.parser.dropStatementParserMethods)?size != 0> |
| stmt = SqlDrop() |
| | |
| </#if> |
| stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| | |
| stmt = SqlExplain() |
| | |
| stmt = SqlDescribe() |
| | |
| stmt = SqlInsert() |
| | |
| stmt = SqlDelete() |
| | |
| stmt = SqlUpdate() |
| | |
| stmt = SqlMerge() |
| | |
| stmt = SqlProcedureCall() |
| ) |
| { |
| return stmt; |
| } |
| } |
| |
| /** |
| * Parses an SQL statement followed by the end-of-file symbol. |
| */ |
| SqlNode SqlStmtEof() : |
| { |
| SqlNode stmt; |
| } |
| { |
| stmt = SqlStmt() <EOF> |
| { |
| return stmt; |
| } |
| } |
| |
| <#-- Add implementations of additional parser statement calls here --> |
| <#list (parser.implementationFiles!default.parser.implementationFiles) as file> |
| <#include "/@includes/"+file /> |
| </#list> |
| |
| SqlNodeList ParenthesizedKeyValueOptionCommaList() : |
| { |
| final Span s; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| { s = span(); } |
| <LPAREN> |
| AddKeyValueOption(list) |
| ( |
| <COMMA> |
| AddKeyValueOption(list) |
| )* |
| <RPAREN> { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| /** |
| * Parses an option with format key=val whose key is a simple identifier or string literal |
| * and value is a string literal. |
| */ |
| void AddKeyValueOption(List<SqlNode> list) : |
| { |
| final SqlNode key; |
| final SqlNode value; |
| } |
| { |
| ( |
| key = SimpleIdentifier() |
| | |
| key = StringLiteral() |
| ) |
| <EQ> |
| value = StringLiteral() { |
| list.add(key); |
| list.add(value); |
| } |
| } |
| |
| /** Parses an option value (either a string or a numeric) and adds to a list. */ |
| void AddOptionValue(List<SqlNode> list) : |
| { |
| final SqlNode value; |
| } |
| { |
| ( |
| value = NumericLiteral() { list.add(value); } |
| | |
| value = StringLiteral() { list.add(value); } |
| ) |
| } |
| |
| /** |
| * Parses a literal list separated by comma. The literal is either a string or a numeric. |
| */ |
| SqlNodeList ParenthesizedLiteralOptionCommaList() : |
| { |
| final Span s; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| { s = span(); } |
| <LPAREN> |
| AddOptionValue(list) ( <COMMA> AddOptionValue(list) )* |
| <RPAREN> { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| void AddHint(List<SqlNode> hints) : |
| { |
| final SqlIdentifier hintName; |
| final SqlNodeList hintOptions; |
| final SqlHint.HintOptionFormat optionFormat; |
| } |
| { |
| hintName = SimpleIdentifier() |
| ( |
| LOOKAHEAD(5) |
| hintOptions = ParenthesizedKeyValueOptionCommaList() { |
| optionFormat = SqlHint.HintOptionFormat.KV_LIST; |
| } |
| | |
| LOOKAHEAD(3) |
| hintOptions = ParenthesizedSimpleIdentifierList() { |
| optionFormat = SqlHint.HintOptionFormat.ID_LIST; |
| } |
| | |
| LOOKAHEAD(3) |
| hintOptions = ParenthesizedLiteralOptionCommaList() { |
| optionFormat = SqlHint.HintOptionFormat.LITERAL_LIST; |
| } |
| | |
| LOOKAHEAD(2) |
| [<LPAREN> <RPAREN>] |
| { |
| hintOptions = SqlNodeList.EMPTY; |
| optionFormat = SqlHint.HintOptionFormat.EMPTY; |
| } |
| ) |
| { |
| hints.add( |
| new SqlHint(Span.of(hintOptions).end(this), hintName, hintOptions, |
| optionFormat)); |
| } |
| } |
| |
| /** Parses hints following a table reference, |
| * and returns the wrapped table reference. */ |
| SqlNode TableHints(SqlIdentifier tableName) : |
| { |
| final List<SqlNode> hints = new ArrayList<SqlNode>(); |
| } |
| { |
| <HINT_BEG> AddHint(hints) ( <COMMA> AddHint(hints) )* <COMMENT_END> { |
| final SqlParserPos pos = Span.of(tableName).addAll(hints).end(this); |
| final SqlNodeList hintList = new SqlNodeList(hints, pos); |
| return new SqlTableRef(pos, tableName, hintList); |
| } |
| } |
| |
| /** |
| * Parses a leaf SELECT expression without ORDER BY. |
| */ |
| SqlSelect SqlSelect() : |
| { |
| final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>(); |
| final SqlLiteral keyword; |
| final SqlNodeList keywordList; |
| final List<SqlNode> selectList = new ArrayList<SqlNode>(); |
| final SqlNode fromClause; |
| final SqlNode where; |
| final SqlNodeList groupBy; |
| final SqlNode having; |
| final SqlNodeList windowDecls; |
| final SqlNode qualify; |
| final List<SqlNode> hints = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| <SELECT> { s = span(); } |
| [ <HINT_BEG> AddHint(hints) ( <COMMA> AddHint(hints) )* <COMMENT_END> ] |
| SqlSelectKeywords(keywords) |
| ( |
| <STREAM> { |
| keywords.add(SqlSelectKeyword.STREAM.symbol(getPos())); |
| } |
| )? |
| ( |
| keyword = AllOrDistinct() { keywords.add(keyword); } |
| )? |
| { |
| keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos()); |
| } |
| AddSelectItem(selectList) |
| ( <COMMA> AddSelectItem(selectList) )* |
| ( |
| <FROM> fromClause = FromClause() |
| ( where = Where() | { where = null; } ) |
| ( groupBy = GroupBy() | { groupBy = null; } ) |
| ( having = Having() | { having = null; } ) |
| ( windowDecls = Window() | { windowDecls = null; } ) |
| ( qualify = Qualify() | { qualify = null; } ) |
| | |
| E() { |
| fromClause = null; |
| where = null; |
| groupBy = null; |
| having = null; |
| windowDecls = null; |
| qualify = null; |
| } |
| ) |
| { |
| return new SqlSelect(s.end(this), keywordList, |
| new SqlNodeList(selectList, Span.of(selectList).pos()), |
| fromClause, where, groupBy, having, windowDecls, qualify, |
| null, null, null, new SqlNodeList(hints, getPos())); |
| } |
| } |
| |
| /* |
| * Abstract production: |
| * |
| * void SqlSelectKeywords(List keywords) |
| * |
| * Parses dialect-specific keywords immediately following the SELECT keyword. |
| */ |
| |
| /** |
| * Parses an EXPLAIN PLAN statement. |
| */ |
| SqlNode SqlExplain() : |
| { |
| SqlNode stmt; |
| SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES; |
| SqlExplain.Depth depth; |
| final SqlExplainFormat format; |
| } |
| { |
| <EXPLAIN> <PLAN> |
| [ detailLevel = ExplainDetailLevel() ] |
| depth = ExplainDepth() |
| ( |
| LOOKAHEAD(2) |
| <AS> <XML> { format = SqlExplainFormat.XML; } |
| | |
| LOOKAHEAD(2) |
| <AS> <JSON> { format = SqlExplainFormat.JSON; } |
| | |
| <AS> <DOT_FORMAT> { format = SqlExplainFormat.DOT; } |
| | |
| { format = SqlExplainFormat.TEXT; } |
| ) |
| <FOR> stmt = SqlQueryOrDml() { |
| return new SqlExplain(getPos(), |
| stmt, |
| detailLevel.symbol(SqlParserPos.ZERO), |
| depth.symbol(SqlParserPos.ZERO), |
| format.symbol(SqlParserPos.ZERO), |
| nDynamicParams); |
| } |
| } |
| |
| /** Parses a query (SELECT or VALUES) |
| * or DML statement (INSERT, UPDATE, DELETE, MERGE). */ |
| SqlNode SqlQueryOrDml() : |
| { |
| SqlNode stmt; |
| } |
| { |
| ( |
| stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| | |
| stmt = SqlInsert() |
| | |
| stmt = SqlDelete() |
| | |
| stmt = SqlUpdate() |
| | |
| stmt = SqlMerge() |
| ) { return stmt; } |
| } |
| |
| /** |
| * Parses WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION modifier for |
| * EXPLAIN PLAN. |
| */ |
| SqlExplain.Depth ExplainDepth() : |
| { |
| } |
| { |
| ( |
| LOOKAHEAD(2) |
| <WITH> <TYPE> |
| { |
| return SqlExplain.Depth.TYPE; |
| } |
| | |
| <WITH> <IMPLEMENTATION> |
| { |
| return SqlExplain.Depth.PHYSICAL; |
| } |
| | |
| <WITHOUT> <IMPLEMENTATION> |
| { |
| return SqlExplain.Depth.LOGICAL; |
| } |
| | |
| { |
| return SqlExplain.Depth.PHYSICAL; |
| } |
| |
| ) |
| } |
| |
| /** |
| * Parses INCLUDING ALL ATTRIBUTES modifier for EXPLAIN PLAN. |
| */ |
| SqlExplainLevel ExplainDetailLevel() : |
| { |
| SqlExplainLevel level = SqlExplainLevel.EXPPLAN_ATTRIBUTES; |
| } |
| { |
| ( |
| <EXCLUDING> <ATTRIBUTES> |
| { |
| level = SqlExplainLevel.NO_ATTRIBUTES; |
| } |
| | |
| <INCLUDING> |
| [ <ALL> { level = SqlExplainLevel.ALL_ATTRIBUTES; } ] |
| <ATTRIBUTES> |
| { |
| } |
| ) |
| { |
| return level; |
| } |
| } |
| |
| /** |
| * Parses a DESCRIBE statement. |
| */ |
| SqlNode SqlDescribe() : |
| { |
| final Span s; |
| final SqlIdentifier table; |
| final SqlIdentifier column; |
| final SqlIdentifier id; |
| final SqlNode stmt; |
| } |
| { |
| <DESCRIBE> { s = span(); } |
| ( |
| LOOKAHEAD(2) (<DATABASE> | <CATALOG> | <SCHEMA>) |
| id = CompoundIdentifier() { |
| // DESCRIBE DATABASE and DESCRIBE CATALOG currently do the same as |
| // DESCRIBE SCHEMA but should be different. See |
| // [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT |
| return new SqlDescribeSchema(s.end(id), id); |
| } |
| | |
| // Use syntactic lookahead to determine whether a table name is coming. |
| // We do not allow SimpleIdentifier() because that includes <STATEMENT>. |
| LOOKAHEAD( <TABLE> |
| | <IDENTIFIER> |
| | <HYPHENATED_IDENTIFIER> |
| | <QUOTED_IDENTIFIER> |
| | <BACK_QUOTED_IDENTIFIER> |
| | <BIG_QUERY_BACK_QUOTED_IDENTIFIER> |
| | <BRACKET_QUOTED_IDENTIFIER> ) |
| (<TABLE>)? |
| table = CompoundIdentifier() |
| ( column = SimpleIdentifier() | { column = null; } ) |
| { |
| return new SqlDescribeTable(s.add(table).addIf(column).pos(), |
| table, column); |
| } |
| | |
| (LOOKAHEAD(1) <STATEMENT>)? |
| stmt = SqlQueryOrDml() { |
| // DESCRIBE STATEMENT currently does the same as EXPLAIN. See |
| // [CALCITE-1221] Implement DESCRIBE DATABASE, CATALOG, STATEMENT |
| final SqlExplainLevel detailLevel = SqlExplainLevel.EXPPLAN_ATTRIBUTES; |
| final SqlExplain.Depth depth = SqlExplain.Depth.PHYSICAL; |
| final SqlExplainFormat format = SqlExplainFormat.TEXT; |
| return new SqlExplain(s.end(stmt), |
| stmt, |
| detailLevel.symbol(SqlParserPos.ZERO), |
| depth.symbol(SqlParserPos.ZERO), |
| format.symbol(SqlParserPos.ZERO), |
| nDynamicParams); |
| } |
| ) |
| } |
| |
| /** |
| * Parses a CALL statement. |
| */ |
| SqlNode SqlProcedureCall() : |
| { |
| final Span s; |
| SqlNode routineCall; |
| } |
| { |
| <CALL> { |
| s = span(); |
| } |
| routineCall = NamedRoutineCall( |
| SqlFunctionCategory.USER_DEFINED_PROCEDURE, |
| ExprContext.ACCEPT_SUB_QUERY) |
| { |
| return SqlStdOperatorTable.PROCEDURE_CALL.createCall( |
| s.end(routineCall), routineCall); |
| } |
| } |
| |
| SqlNode NamedRoutineCall( |
| SqlFunctionCategory routineType, |
| ExprContext exprContext) : |
| { |
| final SqlIdentifier name; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| name = CompoundIdentifier() { |
| s = span(); |
| } |
| <LPAREN> |
| [ |
| AddArg0(list, exprContext) |
| ( |
| <COMMA> { |
| // a comma-list can't appear where only a query is expected |
| checkNonQueryExpression(exprContext); |
| } |
| AddArg(list, exprContext) |
| )* |
| ] |
| <RPAREN> |
| { |
| return createCall(name, s.end(this), routineType, null, list); |
| } |
| } |
| |
| /** |
| * Table parameter of a table function. |
| * The input table with set semantics may be partitioned/ordered on one or more columns. |
| */ |
| SqlNode TableParam() : |
| { |
| final Span s; |
| final SqlNodeList partitionList; |
| final SqlNodeList orderList; |
| SqlNode tableRef; |
| } |
| { |
| { s = span(); } |
| tableRef = ExplicitTable(getPos()) |
| ( |
| <PARTITION> <BY> |
| partitionList = SimpleIdentifierOrList() |
| | { partitionList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| orderList = OrderByOfSetSemanticsTable() |
| | { orderList = SqlNodeList.EMPTY; } |
| ) |
| { return CreateSetSemanticsTableIfNeeded(s, tableRef, partitionList, orderList); } |
| } |
| |
| SqlNode PartitionedQueryOrQueryOrExpr(ExprContext exprContext) : |
| { |
| SqlNode e; |
| } |
| { |
| e = OrderedQueryOrExpr(exprContext) |
| e = PartitionedByAndOrderBy(e) |
| |
| { return e; } |
| } |
| |
| SqlNode PartitionedByAndOrderBy(SqlNode e) : |
| { |
| final Span s; |
| final SqlNodeList partitionList; |
| final SqlNodeList orderList; |
| } |
| { |
| { s = span(); } |
| ( |
| <PARTITION> <BY> |
| partitionList = SimpleIdentifierOrList() |
| | { partitionList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| orderList = OrderByOfSetSemanticsTable() |
| | { orderList = SqlNodeList.EMPTY; } |
| ) |
| { return CreateSetSemanticsTableIfNeeded(s, e, partitionList, orderList); } |
| } |
| |
| SqlNodeList OrderByOfSetSemanticsTable() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| <ORDER> |
| { s = span(); } |
| <BY> |
| ( |
| LOOKAHEAD(2) |
| <LPAREN> AddOrderItem(list) |
| ( |
| // NOTE jvs 6-Feb-2004: See comments at top of file for why |
| // hint is necessary here. |
| LOOKAHEAD(2) <COMMA> AddOrderItem(list) |
| )* |
| <RPAREN> { |
| return new SqlNodeList(list, s.addAll(list).pos()); |
| } |
| | |
| AddOrderItem(list) |
| { |
| return new SqlNodeList(list, s.addAll(list).pos()); |
| } |
| ) |
| } |
| |
| SqlNode CreateSetSemanticsTableIfNeeded( |
| final Span s, |
| final SqlNode e, |
| final SqlNodeList partitionList, |
| final SqlNodeList orderList) : |
| { |
| |
| } |
| { |
| |
| { |
| if (partitionList.isEmpty() && orderList.isEmpty()) { |
| return e; |
| } else { |
| return SqlStdOperatorTable.SET_SEMANTICS_TABLE.createCall( |
| s.pos(), e, partitionList, orderList); |
| } |
| } |
| } |
| |
| /** |
| * Parses an INSERT statement. |
| */ |
| SqlNode SqlInsert() : |
| { |
| final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>(); |
| final SqlNodeList keywordList; |
| final SqlIdentifier tableName; |
| SqlNode tableRef; |
| SqlNode source; |
| final SqlNodeList columnList; |
| final Span s; |
| final Pair<SqlNodeList, SqlNodeList> p; |
| } |
| { |
| ( |
| <INSERT> |
| | |
| <UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); } |
| ) |
| { s = span(); } |
| SqlInsertKeywords(keywords) { |
| keywordList = new SqlNodeList(keywords, s.addAll(keywords).pos()); |
| } |
| <INTO> tableName = CompoundTableIdentifier() |
| ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) |
| [ LOOKAHEAD(5) tableRef = ExtendTable(tableRef) ] |
| ( |
| LOOKAHEAD(2) |
| p = ParenthesizedCompoundIdentifierList() { |
| if (p.right.size() > 0) { |
| tableRef = extend(tableRef, p.right); |
| } |
| if (p.left.size() > 0) { |
| columnList = p.left; |
| } else { |
| columnList = null; |
| } |
| } |
| | { columnList = null; } |
| ) |
| source = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) { |
| return new SqlInsert(s.end(source), keywordList, tableRef, source, |
| columnList); |
| } |
| } |
| |
| /* |
| * Abstract production: |
| * |
| * void SqlInsertKeywords(List keywords) |
| * |
| * Parses dialect-specific keywords immediately following the INSERT keyword. |
| */ |
| |
| /** |
| * Parses a DELETE statement. |
| */ |
| SqlNode SqlDelete() : |
| { |
| final SqlIdentifier tableName; |
| SqlNode tableRef; |
| final SqlIdentifier alias; |
| final SqlNode where; |
| final Span s; |
| } |
| { |
| <DELETE> { |
| s = span(); |
| } |
| <FROM> tableName = CompoundTableIdentifier() |
| ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) |
| [ tableRef = ExtendTable(tableRef) ] |
| ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } ) |
| ( where = Where() | { where = null; } ) |
| { |
| return new SqlDelete(s.add(tableRef).addIf(alias).addIf(where).pos(), |
| tableRef, where, null, alias); |
| } |
| } |
| |
| /** |
| * Parses an UPDATE statement. |
| */ |
| SqlNode SqlUpdate() : |
| { |
| final SqlIdentifier tableName; |
| SqlNode tableRef; |
| final SqlIdentifier alias; |
| final SqlNode where; |
| final SqlNodeList sourceExpressionList; |
| final SqlNodeList targetColumnList; |
| SqlIdentifier id; |
| final Span s; |
| } |
| { |
| <UPDATE> { |
| s = span(); |
| targetColumnList = new SqlNodeList(s.pos()); |
| sourceExpressionList = new SqlNodeList(s.pos()); |
| } |
| tableName = CompoundTableIdentifier() |
| ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) |
| [ tableRef = ExtendTable(tableRef) ] |
| ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } ) |
| <SET> id = SimpleIdentifier() { |
| targetColumnList.add(id); |
| } |
| // TODO: support DEFAULT also |
| <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <COMMA> |
| id = SimpleIdentifier() { targetColumnList.add(id); } |
| <EQ> AddExpression(sourceExpressionList, ExprContext.ACCEPT_SUB_QUERY) |
| )* |
| ( where = Where() | { where = null; } ) |
| { |
| final SqlParserPos pos = s.addAll(targetColumnList) |
| .addAll(sourceExpressionList).addIf(where).pos(); |
| return new SqlUpdate(pos, tableRef, targetColumnList, |
| sourceExpressionList, where, null, alias); |
| } |
| } |
| |
| /** |
| * Parses a MERGE statement. |
| */ |
| SqlNode SqlMerge() : |
| { |
| final SqlIdentifier tableName; |
| SqlNode tableRef; |
| final SqlIdentifier alias; |
| final SqlNode sourceTableRef; |
| final SqlNode condition; |
| final SqlUpdate updateCall; |
| final SqlInsert insertCall; |
| final Span s; |
| } |
| { |
| <MERGE> { s = span(); } <INTO> tableName = CompoundTableIdentifier() |
| ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) |
| [ tableRef = ExtendTable(tableRef) ] |
| ( [ <AS> ] alias = SimpleIdentifier() | { alias = null; } ) |
| <USING> sourceTableRef = TableRef() |
| <ON> condition = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| LOOKAHEAD(2) |
| updateCall = WhenMatchedClause(tableRef, alias) |
| ( insertCall = WhenNotMatchedClause(tableRef) | { insertCall = null; } ) |
| | |
| { updateCall = null; } |
| insertCall = WhenNotMatchedClause(tableRef) |
| ) |
| { |
| final SqlParserPos pos = s.addIf(updateCall).addIf(insertCall).pos(); |
| return new SqlMerge(pos, tableRef, condition, sourceTableRef, |
| updateCall, insertCall, null, alias); |
| } |
| } |
| |
| SqlUpdate WhenMatchedClause(SqlNode table, SqlIdentifier alias) : |
| { |
| SqlIdentifier id; |
| final Span s; |
| final SqlNodeList updateColumnList = new SqlNodeList(SqlParserPos.ZERO); |
| final SqlNodeList updateExprList = new SqlNodeList(SqlParserPos.ZERO); |
| } |
| { |
| <WHEN> { s = span(); } <MATCHED> <THEN> |
| <UPDATE> <SET> id = CompoundIdentifier() { |
| updateColumnList.add(id); |
| } |
| <EQ> AddExpression(updateExprList, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <COMMA> |
| id = CompoundIdentifier() { |
| updateColumnList.add(id); |
| } |
| <EQ> AddExpression(updateExprList, ExprContext.ACCEPT_SUB_QUERY) |
| )* |
| { |
| return new SqlUpdate(s.addAll(updateExprList).pos(), table, |
| updateColumnList, updateExprList, null, null, alias); |
| } |
| } |
| |
| SqlInsert WhenNotMatchedClause(SqlNode table) : |
| { |
| final Span insertSpan, valuesSpan; |
| final List<SqlLiteral> keywords = new ArrayList<SqlLiteral>(); |
| final SqlNodeList keywordList; |
| final SqlNodeList insertColumnList; |
| SqlNode rowConstructor; |
| SqlNode insertValues; |
| } |
| { |
| <WHEN> <NOT> <MATCHED> <THEN> <INSERT> { |
| insertSpan = span(); |
| } |
| SqlInsertKeywords(keywords) { |
| keywordList = new SqlNodeList(keywords, insertSpan.end(this)); |
| } |
| ( |
| LOOKAHEAD(2) |
| insertColumnList = ParenthesizedSimpleIdentifierList() |
| | { insertColumnList = null; } |
| ) |
| ( |
| <LPAREN> |
| <VALUES> { valuesSpan = span(); } rowConstructor = RowConstructor() |
| <RPAREN> |
| | |
| <VALUES> { valuesSpan = span(); } rowConstructor = RowConstructor() |
| ) |
| { |
| // TODO zfong 5/26/06: note that extra parentheses are accepted above |
| // around the VALUES clause as a hack for unparse, but this is |
| // actually invalid SQL; should fix unparse |
| insertValues = SqlStdOperatorTable.VALUES.createCall( |
| valuesSpan.end(this), rowConstructor); |
| return new SqlInsert(insertSpan.end(this), keywordList, |
| table, insertValues, insertColumnList); |
| } |
| } |
| |
| /** |
| * Parses one item in a select list. |
| */ |
| void AddSelectItem(List<SqlNode> list) : |
| { |
| final SqlNode e; |
| final SqlIdentifier id; |
| } |
| { |
| e = SelectExpression() |
| ( |
| [ <AS> ] |
| ( |
| id = SimpleIdentifier() |
| | |
| // Mute the warning about ambiguity between alias and continued |
| // string literal. |
| LOOKAHEAD(1) |
| id = SimpleIdentifierFromStringLiteral() |
| ) |
| { list.add(SqlStdOperatorTable.AS.createCall(span().end(e), e, id)); } |
| | { list.add(e); } |
| ) |
| } |
| |
| /** |
| * Parses one unaliased expression in a select list. |
| */ |
| SqlNode SelectExpression() : |
| { |
| SqlNode e; |
| } |
| { |
| <STAR> { |
| return SqlIdentifier.star(getPos()); |
| } |
| | |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| return e; |
| } |
| } |
| |
| SqlLiteral Natural() : |
| { |
| } |
| { |
| <NATURAL> { return SqlLiteral.createBoolean(true, getPos()); } |
| | |
| { return SqlLiteral.createBoolean(false, getPos()); } |
| } |
| |
| SqlLiteral JoinType() : |
| { |
| JoinType joinType; |
| } |
| { |
| ( |
| LOOKAHEAD(3) // required for "LEFT SEMI JOIN" in Babel |
| <#list (parser.joinTypes!default.parser.joinTypes) as method> |
| joinType = ${method}() |
| | |
| </#list> |
| <JOIN> { joinType = JoinType.INNER; } |
| | |
| <INNER> <JOIN> { joinType = JoinType.INNER; } |
| | |
| <LEFT> [ <OUTER> ] <JOIN> { joinType = JoinType.LEFT; } |
| | |
| <RIGHT> [ <OUTER> ] <JOIN> { joinType = JoinType.RIGHT; } |
| | |
| <FULL> [ <OUTER> ] <JOIN> { joinType = JoinType.FULL; } |
| | |
| <CROSS> <JOIN> { joinType = JoinType.CROSS; } |
| ) |
| { |
| return joinType.symbol(getPos()); |
| } |
| } |
| |
| /** |
| * Parses the FROM clause for a SELECT. |
| * |
| * <p>FROM is mandatory in standard SQL, optional in dialects such as MySQL, |
| * PostgreSQL. The parser allows SELECT without FROM, but the validator fails |
| * if conformance is, say, STRICT_2003. |
| */ |
| SqlNode FromClause() : |
| { |
| SqlNode e, e2; |
| SqlLiteral joinType; |
| } |
| { |
| e = Join() |
| ( |
| // Comma joins should only occur at top-level in the FROM clause. |
| // Valid: |
| // * FROM a, b |
| // * FROM (a CROSS JOIN b), c |
| // Not valid: |
| // * FROM a CROSS JOIN (b, c) |
| LOOKAHEAD(1) |
| <COMMA> { joinType = JoinType.COMMA.symbol(getPos()); } |
| e2 = Join() { |
| e = new SqlJoin(joinType.getParserPosition(), |
| e, |
| SqlLiteral.createBoolean(false, joinType.getParserPosition()), |
| joinType, |
| e2, |
| JoinConditionType.NONE.symbol(SqlParserPos.ZERO), |
| null); |
| } |
| )* |
| { return e; } |
| } |
| |
| SqlNode Join() : |
| { |
| SqlNode e; |
| } |
| { |
| e = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN) |
| ( |
| LOOKAHEAD(2) |
| e = JoinTable(e) |
| )* |
| { |
| return e; |
| } |
| } |
| |
| /** Matches "LEFT JOIN t ON ...", "RIGHT JOIN t USING ...", "JOIN t". */ |
| SqlNode JoinTable(SqlNode e) : |
| { |
| SqlNode e2, condition; |
| final SqlLiteral natural, joinType, on, using; |
| SqlNodeList list; |
| } |
| { |
| // LOOKAHEAD(3) is needed here rather than a LOOKAHEAD(2) because JavaCC |
| // calculates minimum lookahead count incorrectly for choice that contains |
| // zero size child. For instance, with the generated code, |
| // "LOOKAHEAD(2, Natural(), JoinType())" |
| // returns true immediately if it sees a single "<CROSS>" token. Where we |
| // expect the lookahead succeeds after "<CROSS> <APPLY>". |
| // |
| // For more information about the issue, |
| // see https://github.com/javacc/javacc/issues/86 |
| // |
| // We allow CROSS JOIN (joinType = CROSS_JOIN) to have a join condition, |
| // even though that is not valid SQL; the validator will catch it. |
| LOOKAHEAD(3) |
| natural = Natural() |
| joinType = JoinType() |
| e2 = TableRef1(ExprContext.ACCEPT_QUERY_OR_JOIN) |
| ( |
| <ON> { on = JoinConditionType.ON.symbol(getPos()); } |
| condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| return new SqlJoin(joinType.getParserPosition(), |
| e, |
| natural, |
| joinType, |
| e2, |
| on, |
| condition); |
| } |
| | |
| <USING> { using = JoinConditionType.USING.symbol(getPos()); } |
| list = ParenthesizedSimpleIdentifierList() { |
| return new SqlJoin(joinType.getParserPosition(), |
| e, |
| natural, |
| joinType, |
| e2, |
| using, |
| new SqlNodeList(list, Span.of(using).end(this))); |
| } |
| | |
| { |
| return new SqlJoin(joinType.getParserPosition(), |
| e, |
| natural, |
| joinType, |
| e2, |
| JoinConditionType.NONE.symbol(joinType.getParserPosition()), |
| null); |
| } |
| ) |
| | |
| <CROSS> { joinType = JoinType.CROSS.symbol(getPos()); } <APPLY> |
| e2 = TableRef2(true) { |
| if (!this.conformance.isApplyAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed()); |
| } |
| return new SqlJoin(joinType.getParserPosition(), |
| e, |
| SqlLiteral.createBoolean(false, joinType.getParserPosition()), |
| joinType, |
| e2, |
| JoinConditionType.NONE.symbol(SqlParserPos.ZERO), |
| null); |
| } |
| | |
| <OUTER> { joinType = JoinType.LEFT.symbol(getPos()); } <APPLY> |
| e2 = TableRef2(true) { |
| if (!this.conformance.isApplyAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.applyNotAllowed()); |
| } |
| return new SqlJoin(joinType.getParserPosition(), |
| e, |
| SqlLiteral.createBoolean(false, joinType.getParserPosition()), |
| joinType, |
| e2, |
| JoinConditionType.ON.symbol(SqlParserPos.ZERO), |
| SqlLiteral.createBoolean(true, joinType.getParserPosition())); |
| } |
| } |
| |
| /** |
| * Parses a table reference in a FROM clause, not lateral unless LATERAL |
| * is explicitly specified. |
| */ |
| SqlNode TableRef() : |
| { |
| final SqlNode e; |
| } |
| { |
| e = TableRef3(ExprContext.ACCEPT_QUERY, false) { return e; } |
| } |
| |
| SqlNode TableRef1(ExprContext exprContext) : |
| { |
| final SqlNode e; |
| } |
| { |
| e = TableRef3(exprContext, false) { return e; } |
| } |
| |
| /** |
| * Parses a table reference in a FROM clause. |
| */ |
| SqlNode TableRef2(boolean lateral) : |
| { |
| final SqlNode e; |
| } |
| { |
| e = TableRef3(ExprContext.ACCEPT_QUERY, lateral) { return e; } |
| } |
| |
| SqlNode TableRef3(ExprContext exprContext, boolean lateral) : |
| { |
| final SqlIdentifier tableName; |
| SqlNode tableRef; |
| final SqlIdentifier alias; |
| final Span s; |
| SqlNodeList args; |
| final SqlNodeList columnAliasList; |
| SqlUnnestOperator unnestOp = SqlStdOperatorTable.UNNEST; |
| } |
| { |
| ( |
| LOOKAHEAD(2) |
| tableName = CompoundTableIdentifier() |
| ( tableRef = TableHints(tableName) | { tableRef = tableName; } ) |
| [ tableRef = ExtendTable(tableRef) ] |
| tableRef = Over(tableRef) |
| [ tableRef = Snapshot(tableRef) ] |
| [ tableRef = MatchRecognize(tableRef) ] |
| | |
| LOOKAHEAD(2) |
| [ <LATERAL> { lateral = true; } ] |
| tableRef = ParenthesizedExpression(exprContext) |
| tableRef = Over(tableRef) |
| tableRef = addLateral(tableRef, lateral) |
| [ tableRef = MatchRecognize(tableRef) ] |
| | |
| <UNNEST> { s = span(); } |
| args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY) |
| [ |
| <WITH> <ORDINALITY> { |
| unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY; |
| } |
| ] |
| { |
| tableRef = unnestOp.createCall(s.end(this), (List<SqlNode>) args); |
| } |
| | |
| [ <LATERAL> { lateral = true; } ] |
| tableRef = TableFunctionCall() |
| tableRef = addLateral(tableRef, lateral) |
| | |
| tableRef = ExtendedTableRef() |
| ) |
| [ |
| LOOKAHEAD(2) |
| tableRef = Pivot(tableRef) |
| ] |
| [ |
| LOOKAHEAD(2) |
| tableRef = Unpivot(tableRef) |
| ] |
| [ |
| [ <AS> ] alias = SimpleIdentifier() |
| ( |
| columnAliasList = ParenthesizedSimpleIdentifierList() |
| | { columnAliasList = null; } |
| ) |
| { |
| // Standard SQL (and Postgres) allow applying "AS alias" to a JOIN, |
| // e.g. "FROM (a CROSS JOIN b) AS c". The new alias obscures the |
| // internal aliases, and columns cannot be referenced if they are |
| // not unique. TODO: Support this behavior; see |
| // [CALCITE-5168] Allow AS after parenthesized JOIN |
| checkNotJoin(tableRef); |
| if (columnAliasList == null) { |
| tableRef = SqlStdOperatorTable.AS.createCall( |
| Span.of(tableRef).end(this), tableRef, alias); |
| } else { |
| List<SqlNode> idList = new ArrayList<SqlNode>(); |
| idList.add(tableRef); |
| idList.add(alias); |
| idList.addAll(columnAliasList.getList()); |
| tableRef = SqlStdOperatorTable.AS.createCall( |
| Span.of(tableRef).end(this), idList); |
| } |
| } |
| ] |
| [ tableRef = Tablesample(tableRef) ] |
| { return tableRef; } |
| } |
| |
| SqlNode Tablesample(SqlNode tableRef) : |
| { |
| final Span s; |
| final SqlNode sample; |
| final boolean isBernoulli; |
| final SqlNumericLiteral samplePercentage; |
| boolean isRepeatable = false; |
| int repeatableSeed = 0; |
| } |
| { |
| <TABLESAMPLE> { s = span(); checkNotJoin(tableRef); } |
| ( |
| <SUBSTITUTE> <LPAREN> sample = StringLiteral() <RPAREN> |
| { |
| String sampleName = |
| SqlLiteral.unchain(sample).getValueAs(String.class); |
| SqlSampleSpec sampleSpec = SqlSampleSpec.createNamed(sampleName); |
| final SqlLiteral sampleLiteral = |
| SqlLiteral.createSample(sampleSpec, s.end(this)); |
| tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall( |
| s.add(tableRef).end(this), tableRef, sampleLiteral); |
| } |
| | |
| ( |
| <BERNOULLI> { isBernoulli = true; } |
| | |
| <SYSTEM> { isBernoulli = false; } |
| ) |
| <LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN> |
| [ |
| <REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN> |
| { |
| isRepeatable = true; |
| } |
| ] |
| { |
| final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L); |
| BigDecimal rate = samplePercentage.bigDecimalValue(); |
| if (rate.compareTo(BigDecimal.ZERO) < 0 |
| || rate.compareTo(ONE_HUNDRED) > 0) |
| { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.invalidSampleSize()); |
| } |
| |
| // Treat TABLESAMPLE(0) and TABLESAMPLE(100) as no table |
| // sampling at all. Not strictly correct: TABLESAMPLE(0) |
| // should produce no output, but it simplifies implementation |
| // to know that some amount of sampling will occur. |
| // In practice values less than ~1E-43% are treated as 0.0 and |
| // values greater than ~99.999997% are treated as 1.0 |
| float fRate = rate.divide(ONE_HUNDRED).floatValue(); |
| if (fRate > 0.0f && fRate < 1.0f) { |
| SqlSampleSpec tableSampleSpec = |
| isRepeatable |
| ? SqlSampleSpec.createTableSample( |
| isBernoulli, fRate, repeatableSeed) |
| : SqlSampleSpec.createTableSample(isBernoulli, fRate); |
| |
| SqlLiteral tableSampleLiteral = |
| SqlLiteral.createSample(tableSampleSpec, s.end(this)); |
| tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall( |
| s.end(this), tableRef, tableSampleLiteral); |
| } |
| } |
| ) |
| { return tableRef; } |
| } |
| |
| /** Wraps a table reference in a call to EXTEND if an optional "EXTEND" clause |
| * is present. */ |
| SqlNode ExtendTable(SqlNode tableRef) : |
| { |
| final SqlNodeList extendList; |
| } |
| { |
| [ <EXTEND> ] |
| extendList = ExtendList() { |
| return extend(tableRef, extendList); |
| } |
| } |
| |
| SqlNodeList ExtendList() : |
| { |
| final Span s; |
| List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| <LPAREN> { s = span(); } |
| AddColumnType(list) |
| ( |
| <COMMA> AddColumnType(list) |
| )* |
| <RPAREN> { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| void AddColumnType(List<SqlNode> list) : |
| { |
| final SqlIdentifier name; |
| final SqlDataTypeSpec type; |
| final boolean nullable; |
| } |
| { |
| name = CompoundIdentifier() |
| type = DataType() |
| nullable = NotNullOpt() |
| { |
| list.add(name); |
| list.add(type.withNullable(nullable, getPos())); |
| } |
| } |
| |
| /** |
| * Parses a compound identifier with optional type. |
| */ |
| void AddCompoundIdentifierType(List<SqlNode> list, List<SqlNode> extendList) : |
| { |
| final SqlIdentifier name; |
| final SqlDataTypeSpec type; |
| final boolean nullable; |
| } |
| { |
| name = CompoundIdentifier() |
| ( |
| type = DataType() |
| nullable = NotNullOpt() |
| | |
| { type = null; nullable = true; } |
| ) |
| { |
| if (type != null) { |
| if (!this.conformance.allowExtend()) { |
| throw SqlUtil.newContextException(type.getParserPosition(), |
| RESOURCE.extendNotAllowed()); |
| } |
| extendList.add(name); |
| extendList.add(type.withNullable(nullable, getPos())); |
| } |
| list.add(name); |
| } |
| } |
| |
| SqlNode TableFunctionCall() : |
| { |
| final Span s; |
| final SqlNode call; |
| SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION; |
| } |
| { |
| <TABLE> { s = span(); } <LPAREN> |
| [ |
| <SPECIFIC> |
| { |
| funcType = SqlFunctionCategory.USER_DEFINED_TABLE_SPECIFIC_FUNCTION; |
| } |
| ] |
| call = NamedRoutineCall(funcType, ExprContext.ACCEPT_CURSOR) |
| <RPAREN> |
| { |
| return SqlStdOperatorTable.COLLECTION_TABLE.createCall(s.end(this), call); |
| } |
| } |
| |
| /** |
| * Abstract production: |
| * SqlNode ExtendedTableRef() |
| * |
| * <p>Allows parser to be extended with new types of table references. The |
| * default implementation of this production is empty. |
| */ |
| |
| /* |
| * Abstract production: |
| * |
| * SqlNode TableOverOpt() |
| * |
| * Allows an OVER clause following a table expression as an extension to |
| * standard SQL syntax. The default implementation of this production is empty. |
| */ |
| |
| /** |
| * Parses an explicit TABLE t reference. |
| */ |
| SqlNode ExplicitTable(SqlParserPos pos) : |
| { |
| SqlNode tableRef; |
| } |
| { |
| <TABLE> tableRef = CompoundIdentifier() |
| { |
| return SqlStdOperatorTable.EXPLICIT_TABLE.createCall(pos, tableRef); |
| } |
| } |
| |
| /** |
| * Parses a VALUES leaf query expression. |
| */ |
| SqlNode TableConstructor() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| ( |
| <VALUES> { s = span(); } |
| | |
| <VALUE> |
| { |
| s = span(); |
| if (!this.conformance.isValueAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.valueNotAllowed()); |
| } |
| } |
| ) |
| AddRowConstructor(list) |
| ( |
| LOOKAHEAD(2) |
| <COMMA> AddRowConstructor(list) |
| )* |
| { |
| return SqlStdOperatorTable.VALUES.createCall(s.end(this), list); |
| } |
| } |
| |
| /** Parses a row constructor and adds it to a list. */ |
| void AddRowConstructor(List<SqlNode> list) : |
| { |
| SqlNode e; |
| } |
| { |
| e = RowConstructor() { list.add(e); } |
| } |
| |
| /** |
| * Parses a row constructor in the context of a VALUES expression. |
| */ |
| SqlNode RowConstructor() : |
| { |
| final SqlNodeList valueList; |
| final SqlNode value; |
| final Span s; |
| } |
| { |
| // hints are necessary here due to common LPAREN prefixes |
| ( |
| // TODO jvs 8-Feb-2004: extra parentheses are accepted here as a hack |
| // for unparse, but this is actually invalid SQL; should |
| // fix unparse |
| LOOKAHEAD(3) |
| <LPAREN> { s = span(); } |
| <ROW> |
| valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR) |
| <RPAREN> { s.add(this); } |
| | |
| LOOKAHEAD(3) |
| ( |
| <ROW> { s = span(); } |
| | |
| { s = Span.of(); } |
| ) |
| valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR) |
| | |
| value = Expression(ExprContext.ACCEPT_NONCURSOR) |
| { |
| // NOTE: A bare value here is standard SQL syntax, believe it or |
| // not. Taken together with multi-row table constructors, it leads |
| // to very easy mistakes if you forget the parentheses on a |
| // single-row constructor. This is also the reason for the |
| // LOOKAHEAD in TableConstructor(). It would be so much more |
| // reasonable to require parentheses. Sigh. |
| s = Span.of(value); |
| valueList = new SqlNodeList(ImmutableList.of(value), |
| value.getParserPosition()); |
| } |
| ) |
| { |
| // REVIEW jvs 8-Feb-2004: Should we discriminate between scalar |
| // sub-queries inside of ROW and row sub-queries? The standard does, |
| // but the distinction seems to be purely syntactic. |
| return SqlStdOperatorTable.ROW.createCall(s.end(valueList), |
| (List<SqlNode>) valueList); |
| } |
| } |
| |
| /** Parses a WHERE clause for SELECT, DELETE, and UPDATE. */ |
| SqlNode Where() : |
| { |
| SqlNode condition; |
| } |
| { |
| <WHERE> condition = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| return condition; |
| } |
| } |
| |
| /** Parses a GROUP BY clause for SELECT. */ |
| SqlNodeList GroupBy() : |
| { |
| final List<SqlNode> list; |
| final boolean distinct; |
| final Span s; |
| } |
| { |
| <GROUP> { s = span(); } |
| <BY> |
| ( |
| <DISTINCT> { distinct = true; } |
| | <ALL> { distinct = false; } |
| | { distinct = false; } |
| ) |
| list = GroupingElementList() { |
| final SqlParserPos pos = s.end(this); |
| final List<SqlNode> list2 = distinct |
| ? ImmutableList.of( |
| SqlInternalOperators.GROUP_BY_DISTINCT.createCall(pos, list)) |
| : list; |
| return new SqlNodeList(list2, pos); |
| } |
| } |
| |
| List<SqlNode> GroupingElementList() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| AddGroupingElement(list) |
| ( LOOKAHEAD(2) <COMMA> AddGroupingElement(list) )* |
| { return list; } |
| } |
| |
| void AddGroupingElement(List<SqlNode> list) : |
| { |
| final List<SqlNode> subList; |
| final SqlNodeList nodes; |
| final Span s; |
| } |
| { |
| LOOKAHEAD(2) |
| <GROUPING> { s = span(); } |
| <SETS> <LPAREN> subList = GroupingElementList() <RPAREN> { |
| list.add( |
| SqlStdOperatorTable.GROUPING_SETS.createCall(s.end(this), subList)); |
| } |
| | <ROLLUP> { s = span(); } |
| <LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| list.add( |
| SqlStdOperatorTable.ROLLUP.createCall(s.end(this), nodes.getList())); |
| } |
| | <CUBE> { s = span(); } |
| <LPAREN> nodes = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| list.add( |
| SqlStdOperatorTable.CUBE.createCall(s.end(this), nodes.getList())); |
| } |
| | LOOKAHEAD(3) |
| <LPAREN> { s = span(); } <RPAREN> { |
| list.add(new SqlNodeList(s.end(this))); |
| } |
| | AddExpression(list, ExprContext.ACCEPT_SUB_QUERY) |
| } |
| |
| /** |
| * Parses a list of expressions separated by commas. |
| */ |
| SqlNodeList ExpressionCommaList( |
| final Span s, |
| ExprContext exprContext) : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| AddExpressions(list, exprContext) { |
| return new SqlNodeList(list, s.addAll(list).pos()); |
| } |
| } |
| |
| /** |
| * Parses a list of expressions separated by commas, |
| * appending expressions to a given list. |
| */ |
| void AddExpressions(List<SqlNode> list, ExprContext exprContext) : |
| { |
| } |
| { |
| AddExpression(list, exprContext) |
| ( |
| // NOTE jvs 6-Feb-2004: See comments at top of file for why |
| // hint is necessary here. |
| LOOKAHEAD(2) |
| <COMMA> AddExpression(list, ExprContext.ACCEPT_SUB_QUERY) |
| )* |
| } |
| |
| /** Parses a HAVING clause for SELECT. */ |
| SqlNode Having() : |
| { |
| SqlNode e; |
| } |
| { |
| <HAVING> e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; } |
| } |
| |
| /** Parses a WINDOW clause for SELECT. */ |
| SqlNodeList Window() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| <WINDOW> { s = span(); } |
| AddWindowSpec(list) |
| ( |
| LOOKAHEAD(2) |
| <COMMA> AddWindowSpec(list) |
| )* |
| { |
| return new SqlNodeList(list, s.addAll(list).pos()); |
| } |
| } |
| |
| void AddWindowSpec(List<SqlNode> list) : |
| { |
| final SqlIdentifier id; |
| final SqlWindow e; |
| } |
| { |
| id = SimpleIdentifier() <AS> e = WindowSpecification() { |
| e.setDeclName(id); |
| list.add(e); |
| } |
| } |
| |
| /** |
| * Parses a window specification. |
| */ |
| SqlWindow WindowSpecification() : |
| { |
| final SqlIdentifier id; |
| final SqlNodeList partitionList; |
| final SqlNodeList orderList; |
| final SqlLiteral isRows; |
| final SqlNode lowerBound, upperBound; |
| final Span s, s1, s2; |
| final SqlLiteral allowPartial; |
| } |
| { |
| <LPAREN> { s = span(); } |
| ( |
| id = SimpleIdentifier() |
| | { id = null; } |
| ) |
| ( |
| <PARTITION> { s1 = span(); } |
| <BY> |
| partitionList = ExpressionCommaList(s1, ExprContext.ACCEPT_NON_QUERY) |
| | { partitionList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| orderList = OrderBy(true) |
| | { orderList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| ( |
| <ROWS> { isRows = SqlLiteral.createBoolean(true, getPos()); } |
| | |
| <RANGE> { isRows = SqlLiteral.createBoolean(false, getPos()); } |
| ) |
| ( |
| <BETWEEN> lowerBound = WindowRange() |
| <AND> upperBound = WindowRange() |
| | |
| lowerBound = WindowRange() |
| { upperBound = null; } |
| ) |
| | |
| { |
| isRows = SqlLiteral.createBoolean(false, SqlParserPos.ZERO); |
| lowerBound = upperBound = null; |
| } |
| ) |
| ( |
| <ALLOW> { s2 = span(); } <PARTIAL> { |
| allowPartial = SqlLiteral.createBoolean(true, s2.end(this)); |
| } |
| | |
| <DISALLOW> { s2 = span(); } <PARTIAL> { |
| allowPartial = SqlLiteral.createBoolean(false, s2.end(this)); |
| } |
| | { allowPartial = null; } |
| ) |
| <RPAREN> |
| { |
| return SqlWindow.create(null, id, partitionList, orderList, |
| isRows, lowerBound, upperBound, allowPartial, s.end(this)); |
| } |
| } |
| |
| SqlNode WindowRange() : |
| { |
| final SqlNode e; |
| final Span s; |
| } |
| { |
| LOOKAHEAD(2) |
| <CURRENT> { s = span(); } <ROW> { |
| return SqlWindow.createCurrentRow(s.end(this)); |
| } |
| | |
| LOOKAHEAD(2) |
| <UNBOUNDED> { s = span(); } |
| ( |
| <PRECEDING> { |
| return SqlWindow.createUnboundedPreceding(s.end(this)); |
| } |
| | |
| <FOLLOWING> { |
| return SqlWindow.createUnboundedFollowing(s.end(this)); |
| } |
| ) |
| | |
| e = Expression(ExprContext.ACCEPT_NON_QUERY) |
| ( |
| <PRECEDING> { |
| return SqlWindow.createPreceding(e, getPos()); |
| } |
| | |
| <FOLLOWING> { |
| return SqlWindow.createFollowing(e, getPos()); |
| } |
| ) |
| } |
| |
| /** Parses a QUALIFY clause for SELECT. */ |
| SqlNode Qualify() : |
| { |
| SqlNode e; |
| } |
| { |
| <QUALIFY> e = Expression(ExprContext.ACCEPT_SUB_QUERY) { return e; } |
| } |
| |
| /** |
| * Parses an ORDER BY clause. |
| */ |
| SqlNodeList OrderBy(boolean accept) : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| <ORDER> { |
| s = span(); |
| if (!accept) { |
| // Someone told us ORDER BY wasn't allowed here. So why |
| // did they bother calling us? To get the correct |
| // parser position for error reporting. |
| throw SqlUtil.newContextException(s.pos(), RESOURCE.illegalOrderBy()); |
| } |
| } |
| <BY> AddOrderItem(list) |
| ( |
| // NOTE jvs 6-Feb-2004: See comments at top of file for why |
| // hint is necessary here. |
| LOOKAHEAD(2) <COMMA> AddOrderItem(list) |
| )* |
| { |
| return new SqlNodeList(list, s.addAll(list).pos()); |
| } |
| } |
| |
| /** |
| * Parses one item in an ORDER BY clause, and adds it to a list. |
| */ |
| void AddOrderItem(List<SqlNode> list) : |
| { |
| SqlNode e; |
| } |
| { |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <ASC> |
| | <DESC> { |
| e = SqlStdOperatorTable.DESC.createCall(getPos(), e); |
| } |
| )? |
| ( |
| LOOKAHEAD(2) |
| <NULLS> <FIRST> { |
| e = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), e); |
| } |
| | |
| <NULLS> <LAST> { |
| e = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), e); |
| } |
| )? |
| { |
| list.add(e); |
| } |
| } |
| |
| /** Wraps a table reference in a call to OVER if an optional "OVER" clause |
| * is present (if the dialect supports OVER for table expressions). */ |
| SqlNode Over(SqlNode tableRef) : |
| { |
| final SqlNode over; |
| } |
| { |
| over = TableOverOpt() { |
| if (over != null) { |
| return SqlStdOperatorTable.OVER.createCall( |
| getPos(), checkNotJoin(tableRef), over); |
| } else { |
| return tableRef; |
| } |
| } |
| } |
| |
| /** Wraps a table reference in a call to LATERAL if {@code lateral} is true. */ |
| JAVACODE SqlNode addLateral(SqlNode tableRef, boolean lateral) { |
| return lateral |
| ? SqlStdOperatorTable.LATERAL.createCall(getPos(), |
| checkNotJoin(tableRef)) |
| : tableRef; |
| } |
| |
| /** |
| * Parses a FOR SYSTEM_TIME clause following a table expression. |
| */ |
| SqlSnapshot Snapshot(SqlNode tableRef) : |
| { |
| final Span s; |
| final SqlNode e; |
| } |
| { |
| { s = span(); } <FOR> <SYSTEM_TIME> <AS> <OF> |
| // Syntax for temporal table in |
| // standard SQL 2011 IWD 9075-2:201?(E) 7.6 <table reference> |
| // supports grammar as following: |
| // 1. datetime literal |
| // 2. datetime value function, i.e. CURRENT_TIMESTAMP |
| // 3. datetime term in 1 or 2 +(or -) interval term |
| |
| // We extend to support column reference, use Expression |
| // to simplify the parsing code. |
| e = Expression(ExprContext.ACCEPT_NON_QUERY) { |
| return new SqlSnapshot(s.end(this), tableRef, e); |
| } |
| } |
| |
| /** Parses a PIVOT clause following a table expression. */ |
| SqlNode Pivot(SqlNode tableRef) : |
| { |
| final Span s; |
| final Span s2; |
| final List<SqlNode> aggList = new ArrayList<SqlNode>(); |
| final List<SqlNode> valueList = new ArrayList<SqlNode>(); |
| final SqlNodeList axisList; |
| final SqlNodeList inList; |
| } |
| { |
| <PIVOT> { s = span(); checkNotJoin(tableRef); } |
| <LPAREN> |
| AddPivotAgg(aggList) ( <COMMA> AddPivotAgg(aggList) )* |
| <FOR> axisList = SimpleIdentifierOrList() |
| <IN> <LPAREN> { s2 = span(); } |
| [ AddPivotValue(valueList) ( <COMMA> AddPivotValue(valueList) )* ] |
| <RPAREN> { |
| inList = new SqlNodeList(valueList, s2.end(this)); |
| } |
| <RPAREN> |
| { |
| return new SqlPivot(s.end(this), tableRef, |
| new SqlNodeList(aggList, SqlParserPos.sum(aggList)), |
| axisList, inList); |
| } |
| } |
| |
| void AddPivotAgg(List<SqlNode> list) : |
| { |
| final SqlNode e; |
| final SqlIdentifier alias; |
| } |
| { |
| e = NamedFunctionCall() |
| ( |
| // Because babel put FOR into non-reserved keyword set. |
| LOOKAHEAD({getToken(1).kind != COMMA && getToken(1).kind != FOR}) |
| [ <AS> ] alias = SimpleIdentifier() { |
| list.add( |
| SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e, |
| alias)); |
| } |
| | |
| { list.add(e); } |
| ) |
| } |
| |
| void AddPivotValue(List<SqlNode> list) : |
| { |
| final SqlNode e; |
| final SqlNodeList tuple; |
| final SqlIdentifier alias; |
| } |
| { |
| e = RowConstructor() { tuple = SqlParserUtil.stripRow(e); } |
| ( |
| [ <AS> ] alias = SimpleIdentifier() { |
| list.add( |
| SqlStdOperatorTable.AS.createCall(Span.of(tuple).end(this), |
| tuple, alias)); |
| } |
| | |
| { list.add(tuple); } |
| ) |
| } |
| |
| /** Parses an UNPIVOT clause following a table expression. */ |
| SqlNode Unpivot(SqlNode tableRef) : |
| { |
| final Span s; |
| final boolean includeNulls; |
| final SqlNodeList measureList; |
| final SqlNodeList axisList; |
| final Span s2; |
| final List<SqlNode> values = new ArrayList<SqlNode>(); |
| final SqlNodeList inList; |
| } |
| { |
| <UNPIVOT> { s = span(); checkNotJoin(tableRef); } |
| ( |
| <INCLUDE> <NULLS> { includeNulls = true; } |
| | <EXCLUDE> <NULLS> { includeNulls = false; } |
| | { includeNulls = false; } |
| ) |
| <LPAREN> |
| measureList = SimpleIdentifierOrList() |
| <FOR> axisList = SimpleIdentifierOrList() |
| <IN> |
| <LPAREN> { s2 = span(); } |
| AddUnpivotValue(values) ( <COMMA> AddUnpivotValue(values) )* |
| <RPAREN> |
| { inList = new SqlNodeList(values, s2.end(this)); } |
| <RPAREN> { |
| return new SqlUnpivot(s.end(this), tableRef, includeNulls, measureList, |
| axisList, inList); |
| } |
| } |
| |
| void AddUnpivotValue(List<SqlNode> list) : |
| { |
| final SqlNodeList columnList; |
| final SqlNode values; |
| } |
| { |
| columnList = SimpleIdentifierOrList() |
| ( |
| <AS> values = RowConstructor() { |
| final SqlNodeList valueList = SqlParserUtil.stripRow(values); |
| list.add( |
| SqlStdOperatorTable.AS.createCall(Span.of(columnList).end(this), |
| columnList, valueList)); |
| } |
| | |
| { list.add(columnList); } |
| ) |
| } |
| |
| /** |
| * Parses a MATCH_RECOGNIZE clause following a table expression. |
| */ |
| SqlMatchRecognize MatchRecognize(SqlNode tableRef) : |
| { |
| final Span s, s0, s1, s2; |
| final SqlNodeList measureList; |
| final SqlNodeList partitionList; |
| final SqlNodeList orderList; |
| final SqlNode pattern; |
| final SqlLiteral interval; |
| final SqlNodeList patternDefList; |
| final SqlNode after; |
| final SqlNode var; |
| final SqlLiteral rowsPerMatch; |
| final SqlNodeList subsetList; |
| final SqlLiteral isStrictStarts; |
| final SqlLiteral isStrictEnds; |
| } |
| { |
| <MATCH_RECOGNIZE> { s = span(); checkNotJoin(tableRef); } <LPAREN> |
| ( |
| <PARTITION> { s2 = span(); } <BY> |
| partitionList = ExpressionCommaList(s2, ExprContext.ACCEPT_NON_QUERY) |
| | |
| { partitionList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| orderList = OrderBy(true) |
| | |
| { orderList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| <MEASURES> |
| measureList = MeasureColumnCommaList(span()) |
| | |
| { measureList = SqlNodeList.EMPTY; } |
| ) |
| ( |
| <ONE> { s0 = span(); } <ROW> <PER> <MATCH> { |
| rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ONE_ROW.symbol(s0.end(this)); |
| } |
| | |
| <ALL> { s0 = span(); } <ROWS> <PER> <MATCH> { |
| rowsPerMatch = SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS.symbol(s0.end(this)); |
| } |
| | { rowsPerMatch = null; } |
| ) |
| ( |
| <AFTER> { s1 = span(); } <MATCH> <SKIP_> |
| ( |
| <TO> |
| ( |
| LOOKAHEAD(2) |
| <NEXT> <ROW> { |
| after = SqlMatchRecognize.AfterOption.SKIP_TO_NEXT_ROW |
| .symbol(s1.end(this)); |
| } |
| | |
| LOOKAHEAD(2) |
| <FIRST> var = SimpleIdentifier() { |
| after = SqlMatchRecognize.SKIP_TO_FIRST.createCall( |
| s1.end(var), var); |
| } |
| | |
| // This "LOOKAHEAD({true})" is a workaround for Babel. |
| // Because of babel parser uses option "LOOKAHEAD=2" globally, |
| // JavaCC generates something like "LOOKAHEAD(2, [<LAST>] SimpleIdentifier())" |
| // here. But the correct LOOKAHEAD should be |
| // "LOOKAHEAD(2, [ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ] |
| // SimpleIdentifier())" which have the syntactic lookahead for <LAST> considered. |
| // |
| // Overall LOOKAHEAD({true}) is even better as this is the last branch in the |
| // choice. |
| LOOKAHEAD({true}) |
| [ LOOKAHEAD(2, <LAST> SimpleIdentifier()) <LAST> ] var = SimpleIdentifier() { |
| after = SqlMatchRecognize.SKIP_TO_LAST.createCall( |
| s1.end(var), var); |
| } |
| ) |
| | |
| <PAST> <LAST> <ROW> { |
| after = SqlMatchRecognize.AfterOption.SKIP_PAST_LAST_ROW |
| .symbol(s1.end(this)); |
| } |
| ) |
| | { after = null; } |
| ) |
| <PATTERN> |
| <LPAREN> |
| ( |
| <CARET> { isStrictStarts = SqlLiteral.createBoolean(true, getPos()); } |
| | { isStrictStarts = SqlLiteral.createBoolean(false, getPos()); } |
| ) |
| pattern = PatternExpression() |
| ( |
| <DOLLAR> { isStrictEnds = SqlLiteral.createBoolean(true, getPos()); } |
| | { isStrictEnds = SqlLiteral.createBoolean(false, getPos()); } |
| ) |
| <RPAREN> |
| ( |
| <WITHIN> interval = IntervalLiteral() |
| | { interval = null; } |
| ) |
| ( |
| <SUBSET> subsetList = SubsetDefinitionCommaList(span()) |
| | { subsetList = SqlNodeList.EMPTY; } |
| ) |
| <DEFINE> |
| patternDefList = PatternDefinitionCommaList(span()) |
| <RPAREN> { |
| return new SqlMatchRecognize(s.end(this), tableRef, |
| pattern, isStrictStarts, isStrictEnds, patternDefList, measureList, |
| after, subsetList, rowsPerMatch, partitionList, orderList, interval); |
| } |
| } |
| |
| SqlNodeList MeasureColumnCommaList(Span s) : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| AddMeasureColumn(list) |
| ( <COMMA> AddMeasureColumn(list) )* |
| { return new SqlNodeList(list, s.addAll(list).pos()); } |
| } |
| |
| void AddMeasureColumn(List<SqlNode> list) : |
| { |
| final SqlNode e; |
| final SqlIdentifier alias; |
| } |
| { |
| e = Expression(ExprContext.ACCEPT_NON_QUERY) |
| <AS> |
| alias = SimpleIdentifier() { |
| list.add(SqlStdOperatorTable.AS.createCall(Span.of(e).end(this), e, alias)); |
| } |
| } |
| |
| SqlNode PatternExpression() : |
| { |
| SqlNode left; |
| SqlNode right; |
| } |
| { |
| left = PatternTerm() |
| ( |
| <VERTICAL_BAR> |
| right = PatternTerm() { |
| left = SqlStdOperatorTable.PATTERN_ALTER.createCall( |
| Span.of(left).end(right), left, right); |
| } |
| )* |
| { |
| return left; |
| } |
| } |
| |
| SqlNode PatternTerm() : |
| { |
| SqlNode left; |
| SqlNode right; |
| } |
| { |
| left = PatternFactor() |
| ( |
| right = PatternFactor() { |
| left = SqlStdOperatorTable.PATTERN_CONCAT.createCall( |
| Span.of(left).end(right), left, right); |
| } |
| )* |
| { |
| return left; |
| } |
| } |
| |
| SqlNode PatternFactor() : |
| { |
| final SqlNode e; |
| final SqlNode extra; |
| final SqlLiteral startNum; |
| final SqlLiteral endNum; |
| final SqlLiteral reluctant; |
| } |
| { |
| e = PatternPrimary() |
| ( |
| LOOKAHEAD(1) |
| ( |
| <STAR> { |
| startNum = LITERAL_ZERO; |
| endNum = LITERAL_MINUS_ONE; |
| } |
| | |
| <PLUS> { |
| startNum = LITERAL_ONE; |
| endNum = LITERAL_MINUS_ONE; |
| } |
| | |
| <HOOK> { |
| startNum = LITERAL_ZERO; |
| endNum = LITERAL_ONE; |
| } |
| | |
| <LBRACE> |
| ( |
| startNum = UnsignedNumericLiteral() |
| ( |
| <COMMA> |
| ( |
| endNum = UnsignedNumericLiteral() |
| | |
| { endNum = LITERAL_MINUS_ONE; } |
| ) |
| | |
| { endNum = startNum; } |
| ) |
| <RBRACE> |
| | |
| <COMMA> |
| endNum = UnsignedNumericLiteral() |
| <RBRACE> |
| { startNum = LITERAL_MINUS_ONE; } |
| | |
| <MINUS> extra = PatternExpression() <MINUS> <RBRACE> { |
| return SqlStdOperatorTable.PATTERN_CONCAT.createCall( |
| Span.of(e).end(this), e, |
| SqlStdOperatorTable.PATTERN_EXCLUDE.createCall( |
| Span.of(extra).end(this), extra)); |
| } |
| ) |
| ) |
| ( |
| <HOOK> { |
| reluctant = SqlLiteral.createBoolean( |
| startNum.intValue(true) != endNum.intValue(true), |
| SqlParserPos.ZERO); |
| } |
| | |
| { reluctant = SqlLiteral.createBoolean(false, SqlParserPos.ZERO); } |
| ) |
| | |
| { return e; } |
| ) |
| { |
| return SqlStdOperatorTable.PATTERN_QUANTIFIER.createCall( |
| span().end(e), e, startNum, endNum, reluctant); |
| } |
| } |
| |
| SqlNode PatternPrimary() : |
| { |
| final Span s; |
| SqlNode e; |
| final List<SqlNode> list; |
| } |
| { |
| e = SimpleIdentifier() { return e; } |
| | |
| <LPAREN> e = PatternExpression() <RPAREN> { return e; } |
| | |
| <LBRACE> { s = span(); } |
| <MINUS> e = PatternExpression() |
| <MINUS> <RBRACE> { |
| return SqlStdOperatorTable.PATTERN_EXCLUDE.createCall(s.end(this), e); |
| } |
| | |
| ( |
| <PERMUTE> { s = span(); list = new ArrayList<SqlNode>(); } |
| <LPAREN> |
| e = PatternExpression() { list.add(e); } |
| ( <COMMA> e = PatternExpression() { list.add(e); } )* |
| <RPAREN> { |
| return SqlStdOperatorTable.PATTERN_PERMUTE.createCall( |
| s.end(this), list); |
| } |
| ) |
| } |
| |
| SqlNodeList SubsetDefinitionCommaList(Span s) : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| AddSubsetDefinition(list) |
| ( <COMMA> AddSubsetDefinition(list) )* |
| { return new SqlNodeList(list, s.addAll(list).pos()); } |
| } |
| |
| void AddSubsetDefinition(List<SqlNode> list) : |
| { |
| final SqlNode var; |
| final SqlNodeList varList; |
| } |
| { |
| var = SimpleIdentifier() |
| <EQ> |
| <LPAREN> |
| varList = ExpressionCommaList(span(), ExprContext.ACCEPT_NON_QUERY) |
| <RPAREN> { |
| list.add( |
| SqlStdOperatorTable.EQUALS.createCall(span().end(var), var, |
| varList)); |
| } |
| } |
| |
| SqlNodeList PatternDefinitionCommaList(Span s) : |
| { |
| SqlNode e; |
| final List<SqlNode> eList = new ArrayList<SqlNode>(); |
| } |
| { |
| e = PatternDefinition() { |
| eList.add(e); |
| } |
| ( |
| <COMMA> |
| e = PatternDefinition() { |
| eList.add(e); |
| } |
| )* |
| { |
| return new SqlNodeList(eList, s.addAll(eList).pos()); |
| } |
| } |
| |
| SqlNode PatternDefinition() : |
| { |
| final SqlNode var; |
| final SqlNode e; |
| } |
| { |
| var = SimpleIdentifier() |
| <AS> |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| return SqlStdOperatorTable.AS.createCall(Span.of(var, e).pos(), e, var); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Expressions |
| |
| /** |
| * Parses a SQL expression (such as might occur in a WHERE clause) followed by |
| * the end-of-file symbol. |
| */ |
| SqlNode SqlExpressionEof() : |
| { |
| SqlNode e; |
| } |
| { |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) (<EOF>) |
| { |
| return e; |
| } |
| } |
| |
| /** |
| * Parses either a row expression or a query expression without ORDER BY. |
| * |
| * <p>Examples of valid queries: |
| * <ul> |
| * <li>{@code SELECT c FROM t} |
| * <li>{@code SELECT c} (valid in some dialects) |
| * <li>{@code SELECT c FROM t UNION SELECT c2 FROM t2} |
| * <li>{@code WITH q AS (SELECT 1) SELECT * FROM q} |
| * <li>{@code VALUES (1, 2)} |
| * <li>{@code TABLE t} |
| * </ul> |
| * |
| * <p>Non-examples: |
| * <ul> |
| * <li>{@code emp CROSS JOIN dept} |
| * <li>{@code SELECT c FROM t ORDER BY c} |
| * <li>{@code (SELECT c FROM t)} |
| * </ul> |
| */ |
| SqlNode QueryOrExpr(ExprContext exprContext) : |
| { |
| SqlNodeList withList = null; |
| final SqlNode e; |
| final List<Object> list = new ArrayList<Object>(); |
| } |
| { |
| [ withList = WithList() ] |
| e = LeafQueryOrExpr(exprContext) { list.add(e); } |
| ( AddSetOpQuery(list, exprContext) )* |
| { return addWith(withList, SqlParserUtil.toTree(list)); } |
| } |
| |
| SqlNode Query(ExprContext exprContext) : |
| { |
| SqlNodeList withList = null; |
| final SqlNode e; |
| final List<Object> list = new ArrayList<Object>(); |
| } |
| { |
| [ withList = WithList() ] |
| e = LeafQuery(exprContext) { list.add(e); } |
| ( AddSetOpQuery(list, exprContext) )* |
| { return addWith(withList, SqlParserUtil.toTree(list)); } |
| } |
| |
| JAVACODE SqlNode addWith(SqlNodeList withList, SqlNode e) { |
| return withList == null |
| ? e |
| : new SqlWith(withList.getParserPosition(), withList, e); |
| } |
| |
| /** Parses a set operator (e.g. UNION or INTERSECT) |
| * followed by a query or expression, |
| * and adds both to {@code list}. */ |
| void AddSetOpQueryOrExpr(List<Object> list, ExprContext exprContext) : |
| { |
| final SqlOperator op; |
| final SqlParserPos pos; |
| final SqlNode e; |
| } |
| { |
| { |
| if (list.size() == 1 && !((SqlNode) list.get(0)).isA(SqlKind.QUERY)) { |
| // whoops, expression we just parsed wasn't a query, |
| // but we're about to see something like UNION, so |
| // force an exception retroactively |
| checkNonQueryExpression(ExprContext.ACCEPT_QUERY); |
| } |
| } |
| op = BinaryQueryOperator() { |
| // ensure a query is legal in this context |
| pos = getPos(); |
| checkQueryExpression(exprContext); |
| } |
| e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY) { |
| list.add(new SqlParserUtil.ToTreeListItem(op, pos)); |
| list.add(e); |
| } |
| } |
| |
| /** Parses a set operator (e.g. UNION or INTERSECT) |
| * followed by a query, |
| * and adds both to {@code list}. */ |
| void AddSetOpQuery(List<Object> list, ExprContext exprContext) : |
| { |
| final SqlOperator op; |
| final SqlParserPos pos; |
| final SqlNode e; |
| } |
| { |
| { |
| if (list.size() == 1 && !((SqlNode) list.get(0)).isA(SqlKind.QUERY)) { |
| // whoops, expression we just parsed wasn't a query, |
| // but we're about to see something like UNION, so |
| // force an exception retroactively |
| checkNonQueryExpression(ExprContext.ACCEPT_QUERY); |
| } |
| } |
| op = BinaryQueryOperator() { |
| // ensure a query is legal in this context |
| pos = getPos(); |
| checkQueryExpression(exprContext); |
| } |
| e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY) { |
| list.add(new SqlParserUtil.ToTreeListItem(op, pos)); |
| list.add(e); |
| } |
| } |
| |
| SqlNodeList WithList() : |
| { |
| final Span s; |
| final List<SqlWithItem> list = new ArrayList<SqlWithItem>(); |
| } |
| { |
| <WITH> { s = span(); } |
| AddWithItem(list) ( <COMMA> AddWithItem(list) )* |
| { return new SqlNodeList(list, s.end(this)); } |
| } |
| |
| void AddWithItem(List<SqlWithItem> list) : |
| { |
| final SqlIdentifier id; |
| final SqlNodeList columnList; |
| final SqlNode definition; |
| } |
| { |
| id = SimpleIdentifier() |
| ( columnList = ParenthesizedSimpleIdentifierList() | { columnList = null; } ) |
| <AS> |
| definition = ParenthesizedExpression(ExprContext.ACCEPT_QUERY) |
| { list.add(new SqlWithItem(id.getParserPosition(), id, columnList, definition)); } |
| } |
| |
| /** |
| * Parses either a row expression, a leaf query expression, or |
| * a parenthesized expression of any kind. |
| */ |
| SqlNode LeafQueryOrExpr(ExprContext exprContext) : |
| { |
| SqlNode e; |
| } |
| { |
| e = LeafQuery(exprContext) { return e; } |
| | |
| e = Expression(exprContext) { return e; } |
| } |
| |
| /** As {@link #Expression} but appends to a list. */ |
| void AddExpression(List<SqlNode> list, ExprContext exprContext) : |
| { |
| final SqlNode e; |
| } |
| { |
| e = Expression(exprContext) { list.add(e); } |
| } |
| |
| /** |
| * Parses a row expression or a parenthesized expression of any kind. |
| */ |
| SqlNode Expression(ExprContext exprContext) : |
| { |
| final List<Object> list; |
| } |
| { |
| list = Expression2(exprContext) { return SqlParserUtil.toTree(list); } |
| } |
| |
| void AddExpression2b(List<Object> list, ExprContext exprContext) : |
| { |
| SqlNode e; |
| SqlOperator op; |
| SqlNode ext; |
| } |
| { |
| ( |
| LOOKAHEAD(1) |
| op = PrefixRowOperator() { |
| checkNonQueryExpression(exprContext); |
| list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); |
| } |
| )* |
| e = Expression3(exprContext) { |
| list.add(e); |
| } |
| ( |
| LOOKAHEAD(2) <DOT> |
| ext = RowExpressionExtension() { |
| list.add( |
| new SqlParserUtil.ToTreeListItem( |
| SqlStdOperatorTable.DOT, getPos())); |
| list.add(ext); |
| } |
| )* |
| } |
| |
| /** |
| * Parses a binary row expression, or a parenthesized expression of any |
| * kind. |
| * |
| * <p>The result is as a flat list of operators and operands. The top-level |
| * call to get an expression should call {@link #Expression}, but lower-level |
| * calls should call this, to give the parser the opportunity to associate |
| * operator calls. |
| * |
| * <p>For example 'a = b like c = d' should come out '((a = b) like c) = d' |
| * because LIKE and '=' have the same precedence, but tends to come out as '(a |
| * = b) like (c = d)' because (a = b) and (c = d) are parsed as separate |
| * expressions. |
| */ |
| List<Object> Expression2(ExprContext exprContext) : |
| { |
| final List<Object> list = new ArrayList(); |
| List<Object> list2; |
| final List<Object> list3 = new ArrayList(); |
| SqlNodeList nodeList; |
| SqlNode e; |
| SqlOperator op; |
| SqlIdentifier p; |
| final Span s = span(); |
| } |
| { |
| AddExpression2b(list, exprContext) |
| ( |
| LOOKAHEAD(2) |
| ( |
| LOOKAHEAD(2) |
| ( |
| // Special case for "IN", because RHS of "IN" is the only place |
| // that an expression-list is allowed ("exp IN (exp1, exp2)"). |
| LOOKAHEAD(2) { |
| checkNonQueryExpression(exprContext); |
| } |
| ( |
| <NOT> <IN> { op = SqlStdOperatorTable.NOT_IN; } |
| | |
| <IN> { op = SqlStdOperatorTable.IN; } |
| | |
| { final SqlKind k; } |
| k = comp() |
| ( |
| <SOME> { op = SqlStdOperatorTable.some(k); } |
| | |
| <ANY> { op = SqlStdOperatorTable.some(k); } |
| | |
| <ALL> { op = SqlStdOperatorTable.all(k); } |
| ) |
| ) |
| { s.clear().add(this); } |
| nodeList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR) |
| { |
| list.add(new SqlParserUtil.ToTreeListItem(op, s.pos())); |
| s.add(nodeList); |
| // special case for stuff like IN (s1 UNION s2) |
| if (nodeList.size() == 1) { |
| SqlNode item = nodeList.get(0); |
| if (item.isA(SqlKind.QUERY)) { |
| list.add(item); |
| } else { |
| list.add(nodeList); |
| } |
| } else { |
| list.add(nodeList); |
| } |
| } |
| | |
| LOOKAHEAD(2) { |
| checkNonQueryExpression(exprContext); |
| } |
| ( |
| <NOT> <BETWEEN> { |
| op = SqlStdOperatorTable.NOT_BETWEEN; |
| s.clear().add(this); |
| } |
| [ |
| <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_NOT_BETWEEN; } |
| | |
| <ASYMMETRIC> |
| ] |
| | |
| <BETWEEN> |
| { |
| op = SqlStdOperatorTable.BETWEEN; |
| s.clear().add(this); |
| } |
| [ |
| <SYMMETRIC> { op = SqlStdOperatorTable.SYMMETRIC_BETWEEN; } |
| | |
| <ASYMMETRIC> |
| ] |
| ) |
| AddExpression2b(list3, ExprContext.ACCEPT_SUB_QUERY) { |
| list.add(new SqlParserUtil.ToTreeListItem(op, s.pos())); |
| list.addAll(list3); |
| list3.clear(); |
| } |
| | |
| LOOKAHEAD(2) { |
| checkNonQueryExpression(exprContext); |
| s.clear().add(this); |
| } |
| ( |
| ( |
| <NOT> |
| ( |
| <LIKE> { op = SqlStdOperatorTable.NOT_LIKE; } |
| | |
| <ILIKE> { op = SqlLibraryOperators.NOT_ILIKE; } |
| | |
| <RLIKE> { op = SqlLibraryOperators.NOT_RLIKE; } |
| | |
| <SIMILAR> <TO> { op = SqlStdOperatorTable.NOT_SIMILAR_TO; } |
| ) |
| | |
| <LIKE> { op = SqlStdOperatorTable.LIKE; } |
| | |
| <ILIKE> { op = SqlLibraryOperators.ILIKE; } |
| | |
| <RLIKE> { op = SqlLibraryOperators.RLIKE; } |
| | |
| <SIMILAR> <TO> { op = SqlStdOperatorTable.SIMILAR_TO; } |
| ) |
| <#if (parser.includePosixOperators!default.parser.includePosixOperators)> |
| | |
| <NEGATE> <TILDE> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE; } |
| [ <STAR> { op = SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE; } ] |
| | |
| <TILDE> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE; } |
| [ <STAR> { op = SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE; } ] |
| </#if> |
| ) |
| list2 = Expression2(ExprContext.ACCEPT_SUB_QUERY) { |
| list.add(new SqlParserUtil.ToTreeListItem(op, s.pos())); |
| list.addAll(list2); |
| } |
| [ |
| LOOKAHEAD(2) |
| <ESCAPE> e = Expression3(ExprContext.ACCEPT_SUB_QUERY) { |
| s.clear().add(this); |
| list.add( |
| new SqlParserUtil.ToTreeListItem( |
| SqlStdOperatorTable.ESCAPE, s.pos())); |
| list.add(e); |
| } |
| ] |
| | |
| <#list (parser.extraBinaryExpressions!default.parser.extraBinaryExpressions) as extra > |
| ${extra}(list, exprContext, s) |
| | |
| </#list> |
| LOOKAHEAD(3) op = BinaryRowOperator() { |
| checkNonQueryExpression(exprContext); |
| list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); |
| } |
| AddExpression2b(list, ExprContext.ACCEPT_SUB_QUERY) |
| | |
| <LBRACKET> |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| <RBRACKET> { |
| list.add( |
| new SqlParserUtil.ToTreeListItem( |
| SqlStdOperatorTable.ITEM, getPos())); |
| list.add(e); |
| } |
| ( |
| LOOKAHEAD(2) <DOT> |
| p = SimpleIdentifier() { |
| list.add( |
| new SqlParserUtil.ToTreeListItem( |
| SqlStdOperatorTable.DOT, getPos())); |
| list.add(p); |
| } |
| )* |
| | |
| { |
| checkNonQueryExpression(exprContext); |
| } |
| op = PostfixRowOperator() { |
| list.add(new SqlParserUtil.ToTreeListItem(op, getPos())); |
| } |
| ) |
| )+ |
| { |
| return list; |
| } |
| | |
| { |
| return list; |
| } |
| ) |
| } |
| |
| /** Parses a comparison operator inside a SOME / ALL predicate. */ |
| SqlKind comp() : |
| { |
| } |
| { |
| <LT> { return SqlKind.LESS_THAN; } |
| | |
| <LE> { return SqlKind.LESS_THAN_OR_EQUAL; } |
| | |
| <GT> { return SqlKind.GREATER_THAN; } |
| | |
| <GE> { return SqlKind.GREATER_THAN_OR_EQUAL; } |
| | |
| <EQ> { return SqlKind.EQUALS; } |
| | |
| <NE> { return SqlKind.NOT_EQUALS; } |
| | |
| <NE2> { |
| if (!this.conformance.isBangEqualAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.bangEqualNotAllowed()); |
| } |
| return SqlKind.NOT_EQUALS; |
| } |
| } |
| |
| /** |
| * Parses a unary row expression, or a parenthesized expression of any |
| * kind. |
| */ |
| SqlNode Expression3(ExprContext exprContext) : |
| { |
| final SqlNode e; |
| final SqlNodeList list; |
| final SqlNodeList list1; |
| final Span s; |
| final Span rowSpan; |
| } |
| { |
| LOOKAHEAD(2) |
| e = AtomicRowExpression() |
| { |
| checkNonQueryExpression(exprContext); |
| return e; |
| } |
| | |
| e = CursorExpression(exprContext) { return e; } |
| | |
| LOOKAHEAD(3) |
| <ROW> { |
| s = span(); |
| } |
| list = ParenthesizedQueryOrCommaList(exprContext) { |
| if (exprContext != ExprContext.ACCEPT_ALL |
| && exprContext != ExprContext.ACCEPT_CURSOR |
| && !this.conformance.allowExplicitRowValueConstructor()) |
| { |
| throw SqlUtil.newContextException(s.end(list), |
| RESOURCE.illegalRowExpression()); |
| } |
| return SqlStdOperatorTable.ROW.createCall(list); |
| } |
| | |
| ( |
| <ROW> { rowSpan = span(); } |
| | { rowSpan = null; } |
| ) |
| list1 = ParenthesizedQueryOrCommaList(exprContext) { |
| if (rowSpan != null) { |
| // interpret as row constructor |
| return SqlStdOperatorTable.ROW.createCall(rowSpan.end(list1), |
| (List<SqlNode>) list1); |
| } |
| } |
| [ |
| LOOKAHEAD(2) |
| /* TODO: |
| ( |
| op = periodOperator() |
| list2 = ParenthesizedQueryOrCommaList(exprContext) |
| { |
| if (list1.size() != 2 || list2.size() != 2) { |
| throw SqlUtil.newContextException( |
| list1.getParserPosition().plus( |
| list2.getParserPosition()), |
| RESOURCE.illegalOverlaps()); |
| } |
| for (SqlNode node : list2) { |
| list1.add(node); |
| } |
| return op.createCall( |
| list1.getParserPosition().plus(list2.getParserPosition()), |
| list1.toArray()); |
| } |
| ) |
| | |
| */ |
| ( |
| e = IntervalQualifier() |
| { |
| if ((list1.size() == 1) |
| && list1.get(0) instanceof SqlCall) |
| { |
| final SqlCall call = (SqlCall) list1.get(0); |
| if (call.getKind() == SqlKind.MINUS |
| && call.operandCount() == 2) { |
| return SqlStdOperatorTable.MINUS_DATE.createCall( |
| Span.of(list1).end(this), call.operand(0), |
| call.operand(1), e); |
| } |
| } |
| throw SqlUtil.newContextException(span().end(list1), |
| RESOURCE.illegalMinusDate()); |
| } |
| ) |
| ] |
| { |
| if (list1.size() == 1) { |
| // interpret as single value or query |
| return list1.get(0); |
| } else { |
| // interpret as row constructor |
| return SqlStdOperatorTable.ROW.createCall(span().end(list1), |
| (List<SqlNode>) list1); |
| } |
| } |
| } |
| |
| SqlOperator periodOperator() : |
| { |
| } |
| { |
| <OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; } |
| | |
| LOOKAHEAD(2) |
| <IMMEDIATELY> <PRECEDES> { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; } |
| | |
| <PRECEDES> { return SqlStdOperatorTable.PRECEDES; } |
| | |
| <IMMEDIATELY> <SUCCEEDS> { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; } |
| | |
| <SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; } |
| | |
| <EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; } |
| } |
| |
| /** |
| * Parses a COLLATE clause |
| */ |
| SqlCollation CollateClause() : |
| { |
| } |
| { |
| <COLLATE> <COLLATION_ID> |
| { |
| return new SqlCollation( |
| getToken(0).image, SqlCollation.Coercibility.EXPLICIT); |
| } |
| } |
| |
| /** |
| * Numeric literal or parameter; used in LIMIT, OFFSET and FETCH clauses. |
| */ |
| SqlNode UnsignedNumericLiteralOrParam() : |
| { |
| final SqlNode e; |
| } |
| { |
| ( |
| e = UnsignedNumericLiteral() |
| | |
| e = DynamicParam() |
| ) |
| { return e; } |
| } |
| |
| /** |
| * Parses a row expression extension, it can be either an identifier, |
| * or a call to a named function. |
| */ |
| SqlNode RowExpressionExtension() : |
| { |
| final SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION; |
| final SqlIdentifier p; |
| final Span s; |
| final List<SqlNode> args; |
| final SqlLiteral quantifier; |
| } |
| { |
| p = SimpleIdentifier() |
| ( |
| LOOKAHEAD( <LPAREN> ) { s = span(); } |
| ( |
| LOOKAHEAD(2) <LPAREN> <STAR> { |
| quantifier = null; |
| args = ImmutableList.of(SqlIdentifier.star(getPos())); |
| } |
| <RPAREN> |
| | |
| LOOKAHEAD(2) <LPAREN> <RPAREN> { |
| quantifier = null; |
| args = ImmutableList.of(); |
| } |
| | |
| args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) { |
| quantifier = (SqlLiteral) args.get(0); |
| args.remove(0); |
| } |
| ) |
| { return createCall(p, s.end(this), funcType, quantifier, args); } |
| | |
| { return p; } |
| ) |
| } |
| |
| /** |
| * Parses a call to the STRING_AGG aggregate function (or to an aggregate |
| * function with similar syntax: ARRAY_AGG, ARRAY_CONCAT_AGG, GROUP_CONCAT). |
| */ |
| SqlCall StringAggFunctionCall() : |
| { |
| final Span s, s2; |
| final SqlOperator op; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final SqlLiteral qualifier; |
| final SqlNodeList orderBy; |
| final Pair<SqlParserPos, SqlOperator> nullTreatment; |
| final SqlNode separator; |
| } |
| { |
| ( |
| <ARRAY_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_AGG; } |
| | <ARRAY_CONCAT_AGG> { s = span(); op = SqlLibraryOperators.ARRAY_CONCAT_AGG; } |
| | <GROUP_CONCAT> { s = span(); op = SqlLibraryOperators.GROUP_CONCAT; } |
| | <STRING_AGG> { s = span(); op = SqlLibraryOperators.STRING_AGG; } |
| ) |
| <LPAREN> |
| ( |
| qualifier = AllOrDistinct() |
| | { qualifier = null; } |
| ) |
| AddArg(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <COMMA> { |
| // a comma-list can't appear where only a query is expected |
| // TODO: the following line is a no-op; remove it? |
| checkNonQueryExpression(ExprContext.ACCEPT_SUB_QUERY); |
| } |
| AddArg(args, ExprContext.ACCEPT_SUB_QUERY) |
| )* |
| ( |
| nullTreatment = NullTreatment() |
| | { nullTreatment = null; } |
| ) |
| [ |
| orderBy = OrderBy(true) { |
| args.add(orderBy); |
| } |
| ] |
| [ |
| <SEPARATOR> { s2 = span(); } separator = StringLiteral() { |
| args.add(SqlInternalOperators.SEPARATOR.createCall(s2.end(this), separator)); |
| } |
| ] |
| <RPAREN> |
| { |
| SqlCall call = op.createCall(qualifier, s.end(this), args); |
| if (nullTreatment != null) { |
| // Wrap in RESPECT_NULLS or IGNORE_NULLS. |
| call = nullTreatment.right.createCall(nullTreatment.left, call); |
| } |
| return call; |
| } |
| } |
| |
| /** |
| * Parses an atomic row expression. |
| */ |
| SqlNode AtomicRowExpression() : |
| { |
| final SqlNode e; |
| } |
| { |
| ( |
| LOOKAHEAD(2) |
| e = LiteralOrIntervalExpression() |
| | |
| e = DynamicParam() |
| | |
| LOOKAHEAD(2) |
| e = BuiltinFunctionCall() |
| | |
| e = JdbcFunctionCall() |
| | |
| e = MultisetConstructor() |
| | |
| e = ArrayConstructor() |
| | |
| LOOKAHEAD(3) |
| e = MapConstructor() |
| | |
| e = PeriodConstructor() |
| | |
| // NOTE jvs 18-Jan-2005: use syntactic lookahead to discriminate |
| // compound identifiers from function calls in which the function |
| // name is a compound identifier |
| LOOKAHEAD( [<SPECIFIC>] FunctionName() <LPAREN>) |
| e = NamedFunctionCall() |
| | |
| e = ContextVariable() |
| | |
| e = CompoundIdentifier() |
| | |
| e = NewSpecification() |
| | |
| e = CaseExpression() |
| | |
| e = SequenceExpression() |
| ) |
| { return e; } |
| } |
| |
| SqlNode CaseExpression() : |
| { |
| final Span whenSpan = Span.of(); |
| final Span thenSpan = Span.of(); |
| final Span s; |
| SqlNode e; |
| final SqlNode caseIdentifier; |
| final SqlNode elseClause; |
| final List<SqlNode> whenList = new ArrayList<SqlNode>(); |
| final List<SqlNode> thenList = new ArrayList<SqlNode>(); |
| } |
| { |
| <CASE> { s = span(); } |
| ( |
| caseIdentifier = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| | { caseIdentifier = null; } |
| ) |
| ( |
| <WHEN> { whenSpan.add(this); } |
| e = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) { |
| if (((SqlNodeList) e).size() == 1) { |
| e = ((SqlNodeList) e).get(0); |
| } |
| whenList.add(e); |
| } |
| <THEN> { thenSpan.add(this); } |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| thenList.add(e); |
| } |
| )+ |
| ( |
| <ELSE> elseClause = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| | { elseClause = null; } |
| ) |
| <END> { |
| return SqlCase.createSwitched(s.end(this), caseIdentifier, |
| new SqlNodeList(whenList, whenSpan.addAll(whenList).pos()), |
| new SqlNodeList(thenList, thenSpan.addAll(thenList).pos()), |
| elseClause); |
| } |
| } |
| |
| SqlCall SequenceExpression() : |
| { |
| final Span s; |
| final SqlOperator f; |
| final SqlNode sequenceRef; |
| } |
| { |
| ( |
| <NEXT> { f = SqlStdOperatorTable.NEXT_VALUE; s = span(); } |
| | |
| LOOKAHEAD(3) |
| <CURRENT> { f = SqlStdOperatorTable.CURRENT_VALUE; s = span(); } |
| ) |
| <VALUE> <FOR> sequenceRef = CompoundIdentifier() { |
| return f.createCall(s.end(sequenceRef), sequenceRef); |
| } |
| } |
| |
| /** |
| * Parses "SET <NAME> = VALUE" or "RESET <NAME>", without a leading |
| * "ALTER <SCOPE>". |
| */ |
| SqlSetOption SqlSetOption(Span s, String scope) : |
| { |
| SqlIdentifier name; |
| final SqlNode val; |
| } |
| { |
| ( |
| <SET> { |
| s.add(this); |
| } |
| name = CompoundIdentifier() |
| <EQ> |
| ( |
| val = Literal() |
| | |
| val = SimpleIdentifier() |
| | |
| <ON> { |
| // OFF is handled by SimpleIdentifier, ON handled here. |
| val = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT), |
| getPos()); |
| } |
| ) |
| { |
| return new SqlSetOption(s.end(val), scope, name, val); |
| } |
| | |
| <RESET> { |
| s.add(this); |
| } |
| ( |
| name = CompoundIdentifier() |
| | |
| <ALL> { |
| name = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT), |
| getPos()); |
| } |
| ) |
| { |
| return new SqlSetOption(s.end(name), scope, name, null); |
| } |
| ) |
| } |
| |
| /** |
| * Parses an expression for setting or resetting an option in SQL, such as QUOTED_IDENTIFIERS, |
| * or explain plan level (physical/logical). |
| */ |
| SqlAlter SqlAlter() : |
| { |
| final Span s; |
| final String scope; |
| final SqlAlter alterNode; |
| } |
| { |
| <ALTER> { s = span(); } |
| scope = Scope() |
| ( |
| <#-- additional literal parser methods are included here --> |
| <#list (parser.alterStatementParserMethods!default.parser.alterStatementParserMethods) as method> |
| alterNode = ${method}(s, scope) |
| | |
| </#list> |
| |
| alterNode = SqlSetOption(s, scope) |
| ) |
| { |
| return alterNode; |
| } |
| } |
| |
| String Scope() : |
| { |
| } |
| { |
| ( <SYSTEM> | <SESSION> ) { return token.image.toUpperCase(Locale.ROOT); } |
| } |
| |
| <#if (parser.createStatementParserMethods!default.parser.createStatementParserMethods)?size != 0> |
| /** |
| * Parses a CREATE statement. |
| */ |
| SqlCreate SqlCreate() : |
| { |
| final Span s; |
| boolean replace = false; |
| final SqlCreate create; |
| } |
| { |
| <CREATE> { s = span(); } |
| [ |
| <OR> <REPLACE> { |
| replace = true; |
| } |
| ] |
| ( |
| <#-- additional literal parser methods are included here --> |
| <#list (parser.createStatementParserMethods!default.parser.createStatementParserMethods) as method> |
| create = ${method}(s, replace) |
| <#sep>| LOOKAHEAD(2) </#sep> |
| </#list> |
| ) |
| { |
| return create; |
| } |
| } |
| </#if> |
| |
| <#if (parser.dropStatementParserMethods!default.parser.dropStatementParserMethods)?size != 0> |
| /** |
| * Parses a DROP statement. |
| */ |
| SqlDrop SqlDrop() : |
| { |
| final Span s; |
| boolean replace = false; |
| final SqlDrop drop; |
| } |
| { |
| <DROP> { s = span(); } |
| ( |
| <#-- additional literal parser methods are included here --> |
| <#list (parser.dropStatementParserMethods!default.parser.dropStatementParserMethods) as method> |
| drop = ${method}(s, replace) |
| <#sep>|</#sep> |
| </#list> |
| ) |
| { |
| return drop; |
| } |
| } |
| </#if> |
| |
| /** |
| * Parses a literal expression, allowing continued string literals. |
| * Usually returns an SqlLiteral, but a continued string literal |
| * is an SqlCall expression, which concatenates 2 or more string |
| * literals; the validator reduces this. |
| * |
| * <p>If the context allows both literals and expressions, |
| * use {@link #LiteralOrIntervalExpression}, which requires less |
| * lookahead. |
| */ |
| SqlNode Literal() : |
| { |
| SqlNode e; |
| } |
| { |
| ( |
| e = NonIntervalLiteral() |
| | |
| e = IntervalLiteral() |
| ) |
| { return e; } |
| } |
| |
| /** Parses a literal that is not an interval literal. */ |
| SqlNode NonIntervalLiteral() : |
| { |
| final SqlNode e; |
| } |
| { |
| ( |
| e = NumericLiteral() |
| | |
| e = StringLiteral() |
| | |
| e = SpecialLiteral() |
| | |
| e = DateTimeLiteral() |
| <#-- additional literal parser methods are included here --> |
| <#list (parser.literalParserMethods!default.parser.literalParserMethods) as method> |
| | |
| e = ${method} |
| </#list> |
| ) |
| { |
| return e; |
| } |
| } |
| |
| /** Parses a literal or an interval expression. |
| * |
| * <p>We include them in the same production because it is difficult to |
| * distinguish interval literals from interval expression (both of which |
| * start with the {@code INTERVAL} keyword); this way, we can use less |
| * LOOKAHEAD. */ |
| SqlNode LiteralOrIntervalExpression() : |
| { |
| final SqlNode e; |
| } |
| { |
| ( |
| e = IntervalLiteralOrExpression() |
| | |
| e = NonIntervalLiteral() |
| ) |
| { return e; } |
| } |
| |
| /** Parses a unsigned numeric literal */ |
| SqlNumericLiteral UnsignedNumericLiteral() : |
| { |
| } |
| { |
| <UNSIGNED_INTEGER_LITERAL> { |
| return SqlLiteral.createExactNumeric(token.image, getPos()); |
| } |
| | |
| <DECIMAL_NUMERIC_LITERAL> { |
| return SqlLiteral.createExactNumeric(token.image, getPos()); |
| } |
| | |
| <APPROX_NUMERIC_LITERAL> { |
| return SqlLiteral.createApproxNumeric(token.image, getPos()); |
| } |
| } |
| |
| /** Parses a numeric literal (can be signed) */ |
| SqlLiteral NumericLiteral() : |
| { |
| final SqlNumericLiteral num; |
| final Span s; |
| } |
| { |
| <PLUS> num = UnsignedNumericLiteral() { |
| return num; |
| } |
| | |
| <MINUS> { s = span(); } num = UnsignedNumericLiteral() { |
| return SqlLiteral.createNegative(num, s.end(this)); |
| } |
| | |
| num = UnsignedNumericLiteral() { |
| return num; |
| } |
| } |
| |
| /** Parse a special literal keyword */ |
| SqlLiteral SpecialLiteral() : |
| { |
| } |
| { |
| <TRUE> { return SqlLiteral.createBoolean(true, getPos()); } |
| | |
| <FALSE> { return SqlLiteral.createBoolean(false, getPos()); } |
| | |
| <UNKNOWN> { return SqlLiteral.createUnknown(getPos()); } |
| | |
| <NULL> { return SqlLiteral.createNull(getPos()); } |
| } |
| |
| /** |
| * Parses a string literal. The literal may be continued onto several |
| * lines. For a simple literal, the result is an SqlLiteral. For a continued |
| * literal, the result is an SqlCall expression, which concatenates 2 or more |
| * string literals; the validator reduces this. |
| * |
| * @see SqlLiteral#unchain(SqlNode) |
| * @see SqlLiteral#stringValue(SqlNode) |
| * |
| * @return a literal expression |
| */ |
| SqlNode StringLiteral() : |
| { |
| String p; |
| final List<SqlLiteral> frags; |
| char unicodeEscapeChar = 0; |
| String charSet = null; |
| SqlCharStringLiteral literal; |
| } |
| { |
| // A continued string literal consists of a head fragment and one or more |
| // tail fragments. Since comments may occur between the fragments, and |
| // comments are special tokens, each fragment is a token. But since spaces |
| // or comments may not occur between the prefix and the first quote, the |
| // head fragment, with any prefix, is one token. |
| |
| <BINARY_STRING_LITERAL> |
| { |
| frags = new ArrayList<SqlLiteral>(); |
| try { |
| p = SqlParserUtil.trim(token.image, "xX'"); |
| frags.add(SqlLiteral.createBinaryString(p, getPos())); |
| } catch (NumberFormatException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.illegalBinaryString(token.image)); |
| } |
| } |
| ( |
| // The grammar is ambiguous when a continued literals and a character |
| // string alias are both possible. For example, in |
| // SELECT x'01'\n'ab' |
| // we prefer that 'ab' continues the literal, and is not an alias. |
| // The following LOOKAHEAD mutes the warning about ambiguity. |
| LOOKAHEAD(1) |
| <QUOTED_STRING> |
| { |
| try { |
| p = SqlParserUtil.trim(token.image, "'"); // no embedded quotes |
| frags.add(SqlLiteral.createBinaryString(p, getPos())); |
| } catch (NumberFormatException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.illegalBinaryString(token.image)); |
| } |
| } |
| )* |
| { |
| assert !frags.isEmpty(); |
| if (frags.size() == 1) { |
| return frags.get(0); // just the head fragment |
| } else { |
| SqlParserPos pos2 = SqlParserPos.sum(frags); |
| return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, frags); |
| } |
| } |
| | |
| ( |
| <PREFIXED_STRING_LITERAL> |
| { charSet = SqlParserUtil.getCharacterSet(token.image); } |
| | <QUOTED_STRING> |
| | <UNICODE_STRING_LITERAL> { |
| // TODO jvs 2-Feb-2009: support the explicit specification of |
| // a character set for Unicode string literals, per SQL:2003 |
| unicodeEscapeChar = BACKSLASH; |
| charSet = "UTF16"; |
| } |
| ) |
| { |
| frags = new ArrayList<SqlLiteral>(); |
| p = SqlParserUtil.parseString(token.image); |
| try { |
| literal = SqlLiteral.createCharString(p, charSet, getPos()); |
| frags.add(literal); |
| } catch (java.nio.charset.UnsupportedCharsetException e) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unknownCharacterSet(charSet)); |
| } |
| } |
| ( |
| // The grammar is ambiguous when a continued literals and a character |
| // string alias are both possible. For example, in |
| // SELECT 'taxi'\n'cab' |
| // we prefer that 'cab' continues the literal, and is not an alias. |
| // The following LOOKAHEAD mutes the warning about ambiguity. |
| LOOKAHEAD(1) |
| <QUOTED_STRING> |
| { |
| p = SqlParserUtil.parseString(token.image); |
| try { |
| literal = SqlLiteral.createCharString(p, charSet, getPos()); |
| frags.add(literal); |
| } catch (java.nio.charset.UnsupportedCharsetException e) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unknownCharacterSet(charSet)); |
| } |
| } |
| )* |
| [ |
| <UESCAPE> <QUOTED_STRING> |
| { |
| if (unicodeEscapeChar == 0) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unicodeEscapeUnexpected()); |
| } |
| String s = SqlParserUtil.parseString(token.image); |
| unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s); |
| } |
| ] |
| { |
| assert !frags.isEmpty(); |
| if (frags.size() == 1) { |
| // just the head fragment |
| SqlLiteral lit = (SqlLiteral) frags.get(0); |
| return lit.unescapeUnicode(unicodeEscapeChar); |
| } else { |
| SqlNode[] rands = (SqlNode[]) frags.toArray(new SqlNode[0]); |
| for (int i = 0; i < rands.length; ++i) { |
| rands[i] = ((SqlLiteral) rands[i]).unescapeUnicode( |
| unicodeEscapeChar); |
| } |
| SqlParserPos pos2 = SqlParserPos.sum(rands); |
| return SqlStdOperatorTable.LITERAL_CHAIN.createCall(pos2, rands); |
| } |
| } |
| | |
| <C_STYLE_ESCAPED_STRING_LITERAL> |
| { |
| try { |
| p = SqlParserUtil.parseCString(getToken(0).image); |
| } catch (SqlParserUtil.MalformedUnicodeEscape e) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unicodeEscapeMalformed(e.i)); |
| } |
| return SqlLiteral.createCharString(p, "UTF16", getPos()); |
| } |
| | |
| <BIG_QUERY_DOUBLE_QUOTED_STRING> |
| { |
| p = SqlParserUtil.stripQuotes(getToken(0).image, DQ, DQ, "\\\"", |
| Casing.UNCHANGED); |
| try { |
| return SqlLiteral.createCharString(p, charSet, getPos()); |
| } catch (java.nio.charset.UnsupportedCharsetException e) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unknownCharacterSet(charSet)); |
| } |
| } |
| | |
| <BIG_QUERY_QUOTED_STRING> |
| { |
| p = SqlParserUtil.stripQuotes(getToken(0).image, "'", "'", "\\'", |
| Casing.UNCHANGED); |
| try { |
| return SqlLiteral.createCharString(p, charSet, getPos()); |
| } catch (java.nio.charset.UnsupportedCharsetException e) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.unknownCharacterSet(charSet)); |
| } |
| } |
| } |
| |
| /** Parses a character literal. |
| * Matches a single-quoted string, such as 'foo'; |
| * on BigQuery also matches a double-quoted string, such as "foo". |
| * Returns the value of the string with quotes removed. */ |
| String SimpleStringLiteral() : |
| { |
| } |
| { |
| <QUOTED_STRING> { |
| return SqlParserUtil.parseString(token.image); |
| } |
| | |
| <BIG_QUERY_QUOTED_STRING> { |
| return SqlParserUtil.stripQuotes(token.image, "'", "'", "\\'", Casing.UNCHANGED); |
| } |
| | |
| <BIG_QUERY_DOUBLE_QUOTED_STRING> { |
| return SqlParserUtil.stripQuotes(token.image, DQ, DQ, "\\\"", Casing.UNCHANGED); |
| } |
| } |
| |
| /** |
| * Parses a date/time literal. |
| */ |
| SqlLiteral DateTimeLiteral() : |
| { |
| final String p; |
| final Span s; |
| } |
| { |
| <LBRACE_D> <QUOTED_STRING> { |
| p = SqlParserUtil.parseString(token.image); |
| } |
| <RBRACE> { |
| return SqlParserUtil.parseDateLiteral(p, getPos()); |
| } |
| | |
| <LBRACE_T> <QUOTED_STRING> { |
| p = SqlParserUtil.parseString(token.image); |
| } |
| <RBRACE> { |
| return SqlParserUtil.parseTimeLiteral(p, getPos()); |
| } |
| | |
| <LBRACE_TS> { s = span(); } <QUOTED_STRING> { |
| p = SqlParserUtil.parseString(token.image); |
| } |
| <RBRACE> { |
| return SqlParserUtil.parseTimestampLiteral(p, s.end(this)); |
| } |
| | |
| <DATE> { s = span(); } p = SimpleStringLiteral() { |
| return SqlLiteral.createUnknown("DATE", p, s.end(this)); |
| } |
| | |
| <DATETIME> { s = span(); } p = SimpleStringLiteral() { |
| return SqlLiteral.createUnknown("DATETIME", p, s.end(this)); |
| } |
| | |
| <TIME> { s = span(); } p = SimpleStringLiteral() { |
| return SqlLiteral.createUnknown("TIME", p, s.end(this)); |
| } |
| | |
| LOOKAHEAD(2) |
| <TIMESTAMP> { s = span(); } p = SimpleStringLiteral() { |
| return SqlLiteral.createUnknown("TIMESTAMP", p, s.end(this)); |
| } |
| | |
| <TIMESTAMP> { s = span(); } <WITH> <LOCAL> <TIME> <ZONE> p = SimpleStringLiteral() { |
| return SqlLiteral.createUnknown("TIMESTAMP WITH LOCAL TIME ZONE", p, s.end(this)); |
| } |
| } |
| |
| /** Parses a Date/Time constructor function, for example "DATE(1969, 7, 21)" |
| * or "DATETIME(d, t)". Enabled in some libraries (e.g. BigQuery). */ |
| SqlNode DateTimeConstructorCall() : |
| { |
| final SqlFunctionCategory funcType = SqlFunctionCategory.TIMEDATE; |
| final SqlIdentifier qualifiedName; |
| final Span s; |
| final SqlLiteral quantifier; |
| final List<? extends SqlNode> args; |
| } |
| { |
| (<DATE> | <TIME> | <DATETIME> | <TIMESTAMP>) { |
| s = span(); |
| qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos()); |
| } |
| args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) { |
| quantifier = (SqlLiteral) args.get(0); |
| args.remove(0); |
| return createCall(qualifiedName, s.end(this), funcType, quantifier, args); |
| } |
| } |
| |
| /** Parses a MULTISET constructor */ |
| SqlNode MultisetConstructor() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| SqlNode e; |
| final Span s; |
| } |
| { |
| <MULTISET> { s = span(); } |
| ( |
| LOOKAHEAD(2) |
| <LPAREN> |
| // by sub query "MULTISET(SELECT * FROM T)" |
| e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.MULTISET_QUERY.createCall( |
| s.end(this), e); |
| } |
| | |
| // by enumeration "MULTISET[e0, e1, ..., eN]" |
| <LBRACKET> // TODO: do trigraph as well ??( ??) |
| AddExpression(args, ExprContext.ACCEPT_NON_QUERY) |
| ( <COMMA> AddExpression(args, ExprContext.ACCEPT_NON_QUERY) )* |
| <RBRACKET> |
| { |
| return SqlStdOperatorTable.MULTISET_VALUE.createCall( |
| s.end(this), args); |
| } |
| ) |
| } |
| |
| /** Parses an ARRAY constructor */ |
| SqlNode ArrayConstructor() : |
| { |
| SqlNodeList args; |
| SqlNode e; |
| final Span s; |
| final String p; |
| } |
| { |
| <ARRAY> { s = span(); } |
| ( |
| LOOKAHEAD(1) |
| <LPAREN> |
| // by sub query "MULTISET(SELECT * FROM T)" |
| e = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| <RPAREN> |
| { |
| return SqlStdOperatorTable.ARRAY_QUERY.createCall( |
| s.end(this), e); |
| } |
| | |
| // by enumeration "ARRAY[e0, e1, ..., eN]" |
| <LBRACKET> // TODO: do trigraph as well ??( ??) |
| ( |
| args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY) |
| | |
| { args = SqlNodeList.EMPTY; } |
| ) |
| <RBRACKET> |
| { |
| return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall( |
| s.end(this), args.getList()); |
| } |
| <#if (parser.includeParsingStringLiteralAsArrayLiteral!default.parser.includeParsingStringLiteralAsArrayLiteral) > |
| | |
| p = SimpleStringLiteral() { |
| try { |
| return SqlParserUtil.parseArrayLiteral(p); |
| } catch (SqlParseException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.illegalArrayExpression(p)); |
| } |
| } |
| </#if> |
| ) |
| } |
| |
| SqlCall ArrayLiteral() : |
| { |
| final List<SqlNode> list; |
| SqlNode e; |
| final Span s; |
| } |
| { |
| <LBRACE> { s = span(); } |
| ( |
| e = Literal() { list = startList(e); } |
| ( <COMMA> e = Literal() { list.add(e); } )* |
| | |
| e = ArrayLiteral() { list = startList(e); } |
| ( <COMMA> e = ArrayLiteral() { list.add(e); } )* |
| | |
| { list = Collections.emptyList(); } |
| ) |
| <RBRACE> { |
| return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(s.end(this), list); |
| } |
| } |
| |
| /** Parses a MAP constructor */ |
| SqlNode MapConstructor() : |
| { |
| SqlNodeList args; |
| SqlNode e; |
| final Span s; |
| } |
| { |
| <MAP> { s = span(); } |
| ( |
| LOOKAHEAD(1) |
| <LPAREN> |
| // by sub query "MAP (SELECT empno, deptno FROM emp)" |
| e = LeafQueryOrExpr(ExprContext.ACCEPT_QUERY) |
| <RPAREN> |
| { |
| return SqlStdOperatorTable.MAP_QUERY.createCall( |
| s.end(this), e); |
| } |
| | |
| // by enumeration "MAP[k0, v0, ..., kN, vN]" |
| <LBRACKET> // TODO: do trigraph as well ??( ??) |
| ( |
| args = ExpressionCommaList(s, ExprContext.ACCEPT_NON_QUERY) |
| | |
| { args = SqlNodeList.EMPTY; } |
| ) |
| <RBRACKET> |
| { |
| return SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR.createCall( |
| s.end(this), args.getList()); |
| } |
| ) |
| } |
| |
| /** Parses a PERIOD constructor */ |
| SqlNode PeriodConstructor() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| } |
| { |
| <PERIOD> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.ROW.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses an interval literal. |
| */ |
| SqlLiteral IntervalLiteral() : |
| { |
| final String p; |
| final SqlIntervalQualifier intervalQualifier; |
| int sign = 1; |
| final Span s; |
| } |
| { |
| <INTERVAL> { s = span(); } |
| [ |
| <MINUS> { sign = -1; } |
| | |
| <PLUS> { sign = 1; } |
| ] |
| p = SimpleStringLiteral() |
| intervalQualifier = IntervalQualifier() { |
| return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier), |
| sign, p, intervalQualifier); |
| } |
| } |
| |
| /** Parses an interval literal (e.g. {@code INTERVAL '2:3' HOUR TO MINUTE}) |
| * or an interval expression (e.g. {@code INTERVAL emp.empno MINUTE} |
| * or {@code INTERVAL 3 MONTHS}). */ |
| SqlNode IntervalLiteralOrExpression() : |
| { |
| final String p; |
| final SqlIntervalQualifier intervalQualifier; |
| int sign = 1; |
| final Span s; |
| SqlNode e; |
| } |
| { |
| <INTERVAL> { s = span(); } |
| [ |
| <MINUS> { sign = -1; } |
| | |
| <PLUS> { sign = 1; } |
| ] |
| ( |
| // literal (with quoted string) |
| p = SimpleStringLiteral() |
| intervalQualifier = IntervalQualifier() { |
| return SqlParserUtil.parseIntervalLiteral(s.end(intervalQualifier), |
| sign, p, intervalQualifier); |
| } |
| | |
| // To keep parsing simple, any expressions besides numeric literal and |
| // identifiers must be enclosed in parentheses. |
| ( |
| <LPAREN> |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> |
| | |
| e = UnsignedNumericLiteral() |
| | |
| e = CompoundIdentifier() |
| ) |
| intervalQualifier = IntervalQualifierStart() { |
| if (sign == -1) { |
| e = SqlStdOperatorTable.UNARY_MINUS.createCall(e.getParserPosition(), e); |
| } |
| return SqlStdOperatorTable.INTERVAL.createCall(s.end(this), e, |
| intervalQualifier); |
| } |
| ) |
| } |
| |
| TimeUnit Year() : |
| { |
| } |
| { |
| <YEAR> { return TimeUnit.YEAR; } |
| | |
| <YEARS> { return warn(TimeUnit.YEAR); } |
| } |
| |
| TimeUnit Quarter() : |
| { |
| } |
| { |
| <QUARTER> { return TimeUnit.QUARTER; } |
| | |
| <QUARTERS> { return warn(TimeUnit.QUARTER); } |
| } |
| |
| TimeUnit Month() : |
| { |
| } |
| { |
| <MONTH> { return TimeUnit.MONTH; } |
| | |
| <MONTHS> { return warn(TimeUnit.MONTH); } |
| } |
| |
| TimeUnit Week() : |
| { |
| } |
| { |
| <WEEK> { return TimeUnit.WEEK; } |
| | |
| <WEEKS> { return warn(TimeUnit.WEEK); } |
| } |
| |
| TimeUnit Day() : |
| { |
| } |
| { |
| <DAY> { return TimeUnit.DAY; } |
| | |
| <DAYS> { return warn(TimeUnit.DAY); } |
| } |
| |
| TimeUnit Hour() : |
| { |
| } |
| { |
| <HOUR> { return TimeUnit.HOUR; } |
| | |
| <HOURS> { return warn(TimeUnit.HOUR); } |
| } |
| |
| TimeUnit Minute() : |
| { |
| } |
| { |
| <MINUTE> { return TimeUnit.MINUTE; } |
| | |
| <MINUTES> { return warn(TimeUnit.MINUTE); } |
| } |
| |
| TimeUnit Second() : |
| { |
| } |
| { |
| <SECOND> { return TimeUnit.SECOND; } |
| | |
| <SECONDS> { return warn(TimeUnit.SECOND); } |
| } |
| |
| SqlIntervalQualifier IntervalQualifier() : |
| { |
| final Span s; |
| final TimeUnit start; |
| final TimeUnit end; |
| final int startPrec; |
| int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED; |
| } |
| { |
| ( |
| start = Year() { s = span(); } startPrec = PrecisionOpt() |
| ( |
| LOOKAHEAD(2) <TO> end = Month() |
| | { end = null; } |
| ) |
| | |
| start = Quarter() { s = span(); } startPrec = PrecisionOpt() |
| { end = null; } |
| | |
| start = Month() { s = span(); } startPrec = PrecisionOpt() |
| { end = null; } |
| | |
| start = Week() { s = span(); } startPrec = PrecisionOpt() |
| { end = null; } |
| | |
| start = Day() { s = span(); } startPrec = PrecisionOpt() |
| ( |
| LOOKAHEAD(2) <TO> |
| ( |
| end = Hour() |
| | |
| end = Minute() |
| | |
| end = Second() secondFracPrec = PrecisionOpt() |
| ) |
| | { end = null; } |
| ) |
| | |
| start = Hour() { s = span(); } startPrec = PrecisionOpt() |
| ( |
| LOOKAHEAD(2) <TO> |
| ( |
| end = Minute() |
| | |
| end = Second() |
| [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ] |
| ) |
| | { end = null; } |
| ) |
| | |
| start = Minute() { s = span(); } startPrec = PrecisionOpt() |
| ( |
| LOOKAHEAD(2) <TO> |
| end = Second() |
| [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ] |
| | { end = null; } |
| ) |
| | |
| start = Second() { s = span(); } |
| ( |
| <LPAREN> startPrec = UnsignedIntLiteral() |
| [ <COMMA> secondFracPrec = UnsignedIntLiteral() ] |
| <RPAREN> |
| | { startPrec = -1; } |
| ) |
| { end = null; } |
| ) |
| { |
| return new SqlIntervalQualifier(start, startPrec, end, secondFracPrec, |
| s.end(this)); |
| } |
| } |
| |
| /** Interval qualifier without 'TO unit'. */ |
| SqlIntervalQualifier IntervalQualifierStart() : |
| { |
| final Span s; |
| final TimeUnit start; |
| int startPrec = RelDataType.PRECISION_NOT_SPECIFIED; |
| int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED; |
| } |
| { |
| ( |
| ( |
| start = Year() |
| | start = Quarter() |
| | start = Month() |
| | start = Week() |
| | start = Day() |
| | start = Hour() |
| | start = Minute() |
| ) |
| { s = span(); } |
| startPrec = PrecisionOpt() |
| | |
| start = Second() { s = span(); } |
| [ <LPAREN> startPrec = UnsignedIntLiteral() |
| [ <COMMA> secondFracPrec = UnsignedIntLiteral() ] |
| <RPAREN> |
| ] |
| ) |
| { |
| return new SqlIntervalQualifier(start, startPrec, null, secondFracPrec, |
| s.end(this)); |
| } |
| } |
| |
| /** Parses a built-in time unit (e.g. "YEAR") |
| * or user-defined time frame (e.g. "MINUTE15") |
| * and in each case returns a {@link SqlIntervalQualifier}. |
| * |
| * <p>The units are used in several functions, incuding CEIL, FLOOR, EXTRACT. |
| * Includes NANOSECOND, MILLISECOND, which were previously allowed in EXTRACT |
| * but not CEIL, FLOOR. |
| * |
| * <p>Includes {@code WEEK} and {@code WEEK(SUNDAY)} through |
| {@code WEEK(SATURDAY)}. |
| * |
| * <p>Does not include SQL_TSI_DAY, SQL_TSI_FRAC_SECOND etc. These will be |
| * parsed as identifiers and can be resolved in the validator if they are |
| * registered as abbreviations in your time frame set. |
| */ |
| SqlIntervalQualifier TimeUnitOrName() : { |
| final SqlIdentifier unitName; |
| final SqlIntervalQualifier intervalQualifier; |
| } |
| { |
| // When we see a time unit that is also a non-reserved keyword, such as |
| // NANOSECOND, there is a choice between using the TimeUnit enum |
| // (TimeUnit.NANOSECOND) or the name. The following LOOKAHEAD directive |
| // tells the parser that we prefer the former. |
| // |
| // Reserved keywords, such as SECOND, cannot be identifiers, and are |
| // therefore not ambiguous. |
| LOOKAHEAD(2) |
| intervalQualifier = TimeUnit() { |
| return intervalQualifier; |
| } |
| | unitName = SimpleIdentifier() { |
| return new SqlIntervalQualifier(unitName.getSimple(), |
| unitName.getParserPosition()); |
| } |
| } |
| |
| /** Parses a built-in time unit (e.g. "YEAR") |
| * and returns a {@link SqlIntervalQualifier}. |
| * |
| * <p>Includes {@code WEEK} and {@code WEEK(SUNDAY)} through |
| {@code WEEK(SATURDAY)}. |
| * |
| * <p>Does not include SQL_TSI_DAY, SQL_TSI_FRAC_SECOND etc. These will be |
| * parsed as identifiers and can be resolved in the validator if they are |
| * registered as abbreviations in your time frame set. |
| */ |
| SqlIntervalQualifier TimeUnit() : { |
| final Span span; |
| final String w; |
| } |
| { |
| <NANOSECOND> { return new SqlIntervalQualifier(TimeUnit.NANOSECOND, null, getPos()); } |
| | <MICROSECOND> { return new SqlIntervalQualifier(TimeUnit.MICROSECOND, null, getPos()); } |
| | <MILLISECOND> { return new SqlIntervalQualifier(TimeUnit.MILLISECOND, null, getPos()); } |
| | <SECOND> { return new SqlIntervalQualifier(TimeUnit.SECOND, null, getPos()); } |
| | <MINUTE> { return new SqlIntervalQualifier(TimeUnit.MINUTE, null, getPos()); } |
| | <HOUR> { return new SqlIntervalQualifier(TimeUnit.HOUR, null, getPos()); } |
| | <DAY> { return new SqlIntervalQualifier(TimeUnit.DAY, null, getPos()); } |
| | <DAYOFWEEK> { return new SqlIntervalQualifier(TimeUnit.DOW, null, getPos()); } |
| | <DAYOFYEAR> { return new SqlIntervalQualifier(TimeUnit.DOY, null, getPos()); } |
| | <DOW> { return new SqlIntervalQualifier(TimeUnit.DOW, null, getPos()); } |
| | <DOY> { return new SqlIntervalQualifier(TimeUnit.DOY, null, getPos()); } |
| | <ISODOW> { return new SqlIntervalQualifier(TimeUnit.ISODOW, null, getPos()); } |
| | <ISOYEAR> { return new SqlIntervalQualifier(TimeUnit.ISOYEAR, null, getPos()); } |
| | <WEEK> { span = span(); } |
| ( |
| // There is a choice between "WEEK(weekday)" and "WEEK". We prefer |
| // the former, and the parser will look ahead for '('. |
| LOOKAHEAD(2) |
| <LPAREN> w = weekdayName() <RPAREN> { |
| return new SqlIntervalQualifier(w, span.end(this)); |
| } |
| | |
| { return new SqlIntervalQualifier(TimeUnit.WEEK, null, getPos()); } |
| ) |
| | <MONTH> { return new SqlIntervalQualifier(TimeUnit.MONTH, null, getPos()); } |
| | <QUARTER> { return new SqlIntervalQualifier(TimeUnit.QUARTER, null, getPos()); } |
| | <YEAR> { return new SqlIntervalQualifier(TimeUnit.YEAR, null, getPos()); } |
| | <EPOCH> { return new SqlIntervalQualifier(TimeUnit.EPOCH, null, getPos()); } |
| | <DECADE> { return new SqlIntervalQualifier(TimeUnit.DECADE, null, getPos()); } |
| | <CENTURY> { return new SqlIntervalQualifier(TimeUnit.CENTURY, null, getPos()); } |
| | <MILLENNIUM> { return new SqlIntervalQualifier(TimeUnit.MILLENNIUM, null, getPos()); } |
| } |
| |
| String weekdayName() : |
| { |
| } |
| { |
| <SUNDAY> { return "WEEK_SUNDAY"; } |
| | <MONDAY> { return "WEEK_MONDAY"; } |
| | <TUESDAY> { return "WEEK_TUESDAY"; } |
| | <WEDNESDAY> { return "WEEK_WEDNESDAY"; } |
| | <THURSDAY> { return "WEEK_THURSDAY"; } |
| | <FRIDAY> { return "WEEK_FRIDAY"; } |
| | <SATURDAY> { return "WEEK_SATURDAY"; } |
| } |
| |
| /** |
| * Parses a dynamic parameter marker. |
| */ |
| SqlDynamicParam DynamicParam() : |
| { |
| } |
| { |
| <HOOK> { |
| return new SqlDynamicParam(nDynamicParams++, getPos()); |
| } |
| } |
| |
| /** |
| * Parses one segment of an identifier that may be composite. |
| * |
| * <p>Each time it reads an identifier it writes one element to each list; |
| * the entry in {@code positions} records its position and whether the |
| * segment was quoted. |
| */ |
| void AddIdentifierSegment(List<String> names, List<SqlParserPos> positions) : |
| { |
| final String id; |
| char unicodeEscapeChar = BACKSLASH; |
| final SqlParserPos pos; |
| final Span span; |
| } |
| { |
| ( |
| <IDENTIFIER> { |
| id = unquotedIdentifier(); |
| pos = getPos(); |
| } |
| | |
| <HYPHENATED_IDENTIFIER> { |
| id = unquotedIdentifier(); |
| pos = getPos(); |
| } |
| | |
| <QUOTED_IDENTIFIER> { |
| id = SqlParserUtil.stripQuotes(getToken(0).image, DQ, DQ, DQDQ, |
| quotedCasing); |
| pos = getPos().withQuoting(true); |
| } |
| | |
| <BACK_QUOTED_IDENTIFIER> { |
| id = SqlParserUtil.stripQuotes(getToken(0).image, "`", "`", "``", |
| quotedCasing); |
| pos = getPos().withQuoting(true); |
| } |
| | |
| <BIG_QUERY_BACK_QUOTED_IDENTIFIER> { |
| id = SqlParserUtil.stripQuotes(getToken(0).image, "`", "`", "\\`", |
| quotedCasing); |
| pos = getPos().withQuoting(true); |
| } |
| | |
| <BRACKET_QUOTED_IDENTIFIER> { |
| id = SqlParserUtil.stripQuotes(getToken(0).image, "[", "]", "]]", |
| quotedCasing); |
| pos = getPos().withQuoting(true); |
| } |
| | |
| <UNICODE_QUOTED_IDENTIFIER> { |
| span = span(); |
| String image = getToken(0).image; |
| image = image.substring(image.indexOf('"')); |
| image = SqlParserUtil.stripQuotes(image, DQ, DQ, DQDQ, quotedCasing); |
| } |
| [ |
| <UESCAPE> <QUOTED_STRING> { |
| String s = SqlParserUtil.parseString(token.image); |
| unicodeEscapeChar = SqlParserUtil.checkUnicodeEscapeChar(s); |
| } |
| ] |
| { |
| pos = span.end(this).withQuoting(true); |
| SqlLiteral lit = SqlLiteral.createCharString(image, "UTF16", pos); |
| lit = lit.unescapeUnicode(unicodeEscapeChar); |
| id = lit.toValue(); |
| } |
| | |
| id = NonReservedKeyWord() { |
| pos = getPos(); |
| } |
| ) |
| { |
| if (id.length() > this.identifierMaxLength) { |
| throw SqlUtil.newContextException(pos, |
| RESOURCE.identifierTooLong(id, this.identifierMaxLength)); |
| } |
| names.add(id); |
| if (positions != null) { |
| positions.add(pos); |
| } |
| } |
| } |
| |
| /** As {@link #AddIdentifierSegment} but part of a table name (for example, |
| * following {@code FROM}, {@code INSERT} or {@code UPDATE}). |
| * |
| * <p>In some dialects the lexical rules for table names are different from |
| * for other identifiers. For example, in BigQuery, table names may contain |
| * hyphens. */ |
| void AddTableIdentifierSegment(List<String> names, List<SqlParserPos> positions) : |
| { |
| } |
| { |
| AddIdentifierSegment(names, positions) { |
| final int n = names.size(); |
| if (n > 0 |
| && positions.size() == n |
| && names.get(n - 1).contains(".") |
| && positions.get(n - 1).isQuoted() |
| && this.conformance.splitQuotedTableName()) { |
| final String name = names.remove(n - 1); |
| final SqlParserPos pos = positions.remove(n - 1); |
| final String[] splitNames = name.split("\\."); |
| for (String splitName : splitNames) { |
| names.add(splitName); |
| positions.add(pos); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Parses a simple identifier as a String. |
| */ |
| String Identifier() : |
| { |
| final List<String> names = new ArrayList<String>(); |
| } |
| { |
| AddIdentifierSegment(names, null) { |
| return names.get(0); |
| } |
| } |
| |
| /** |
| * Parses a simple identifier as an SqlIdentifier. |
| */ |
| SqlIdentifier SimpleIdentifier() : |
| { |
| final List<String> names = new ArrayList<String>(); |
| final List<SqlParserPos> positions = new ArrayList<SqlParserPos>(); |
| } |
| { |
| AddIdentifierSegment(names, positions) { |
| return new SqlIdentifier(names.get(0), positions.get(0)); |
| } |
| } |
| |
| /** |
| * Parses a character literal as an SqlIdentifier. |
| * Only valid for column aliases in certain dialects. |
| */ |
| SqlIdentifier SimpleIdentifierFromStringLiteral() : |
| { |
| } |
| { |
| <QUOTED_STRING> { |
| if (!this.conformance.allowCharLiteralAlias()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.charLiteralAliasNotValid()); |
| } |
| final String s = SqlParserUtil.parseString(token.image); |
| return new SqlIdentifier(s, getPos()); |
| } |
| } |
| |
| /** |
| * Parses a comma-separated list of simple identifiers. |
| */ |
| void AddSimpleIdentifiers(List<SqlNode> list) : |
| { |
| SqlIdentifier id; |
| } |
| { |
| id = SimpleIdentifier() {list.add(id);} |
| ( |
| <COMMA> id = SimpleIdentifier() { |
| list.add(id); |
| } |
| )* |
| } |
| |
| /** |
| * List of simple identifiers in parentheses. The position extends from the |
| * open parenthesis to the close parenthesis. |
| */ |
| SqlNodeList ParenthesizedSimpleIdentifierList() : |
| { |
| final Span s; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| <LPAREN> { s = span(); } |
| AddSimpleIdentifiers(list) |
| <RPAREN> { |
| return new SqlNodeList(list, s.end(this)); |
| } |
| } |
| |
| /** List of simple identifiers in parentheses or one simple identifier. |
| * |
| * <ul>Examples: |
| * <li>{@code DEPTNO} |
| * <li>{@code (EMPNO, DEPTNO)} |
| * </ul> |
| */ |
| SqlNodeList SimpleIdentifierOrList() : |
| { |
| SqlIdentifier id; |
| SqlNodeList list; |
| } |
| { |
| id = SimpleIdentifier() { |
| return new SqlNodeList(Collections.singletonList(id), id.getParserPosition()); |
| } |
| | |
| list = ParenthesizedSimpleIdentifierList() { |
| return list; |
| } |
| } |
| |
| <#if (parser.includeCompoundIdentifier!default.parser.includeCompoundIdentifier) > |
| /** |
| * Parses a compound identifier. |
| */ |
| SqlIdentifier CompoundIdentifier() : |
| { |
| final List<String> nameList = new ArrayList<String>(); |
| final List<SqlParserPos> posList = new ArrayList<SqlParserPos>(); |
| boolean star = false; |
| } |
| { |
| AddIdentifierSegment(nameList, posList) |
| ( |
| LOOKAHEAD(2) |
| <DOT> |
| AddIdentifierSegment(nameList, posList) |
| )* |
| ( |
| LOOKAHEAD(2) |
| <DOT> |
| <STAR> { |
| star = true; |
| nameList.add(""); |
| posList.add(getPos()); |
| } |
| )? |
| { |
| SqlParserPos pos = SqlParserPos.sum(posList); |
| if (star) { |
| return SqlIdentifier.star(nameList, pos, posList); |
| } |
| return new SqlIdentifier(nameList, null, pos, posList); |
| } |
| } |
| |
| /** |
| * Parses a compound identifier in the FROM clause. |
| */ |
| SqlIdentifier CompoundTableIdentifier() : |
| { |
| final List<String> nameList = new ArrayList<String>(); |
| final List<SqlParserPos> posList = new ArrayList<SqlParserPos>(); |
| } |
| { |
| AddTableIdentifierSegment(nameList, posList) |
| ( |
| LOOKAHEAD(2) |
| <DOT> |
| AddTableIdentifierSegment(nameList, posList) |
| )* |
| { |
| SqlParserPos pos = SqlParserPos.sum(posList); |
| return new SqlIdentifier(nameList, null, pos, posList); |
| } |
| } |
| |
| /** |
| * Parses a comma-separated list of compound identifiers. |
| */ |
| void AddCompoundIdentifierTypes(List<SqlNode> list, List<SqlNode> extendList) : |
| { |
| } |
| { |
| AddCompoundIdentifierType(list, extendList) |
| (<COMMA> AddCompoundIdentifierType(list, extendList))* |
| } |
| |
| /** |
| * List of compound identifiers in parentheses. The position extends from the |
| * open parenthesis to the close parenthesis. |
| */ |
| Pair<SqlNodeList, SqlNodeList> ParenthesizedCompoundIdentifierList() : |
| { |
| final Span s; |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final List<SqlNode> extendList = new ArrayList<SqlNode>(); |
| } |
| { |
| <LPAREN> { s = span(); } |
| AddCompoundIdentifierTypes(list, extendList) |
| <RPAREN> { |
| return Pair.of(new SqlNodeList(list, s.end(this)), new SqlNodeList(extendList, s.end(this))); |
| } |
| } |
| <#else> |
| <#include "/@includes/compoundIdentifier.ftl" /> |
| </#if> |
| |
| /** |
| * Parses a NEW UDT(...) expression. |
| */ |
| SqlNode NewSpecification() : |
| { |
| final Span s; |
| final SqlNode routineCall; |
| } |
| { |
| <NEW> { s = span(); } |
| routineCall = |
| NamedRoutineCall(SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR, |
| ExprContext.ACCEPT_SUB_QUERY) { |
| return SqlStdOperatorTable.NEW.createCall(s.end(routineCall), routineCall); |
| } |
| } |
| |
| //TODO: real parse errors. |
| int UnsignedIntLiteral() : |
| { |
| Token t; |
| } |
| { |
| t = <UNSIGNED_INTEGER_LITERAL> |
| { |
| try { |
| return Integer.parseInt(t.image); |
| } catch (NumberFormatException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName())); |
| } |
| } |
| } |
| |
| int IntLiteral() : |
| { |
| Token t; |
| } |
| { |
| ( |
| t = <UNSIGNED_INTEGER_LITERAL> |
| | |
| <PLUS> t = <UNSIGNED_INTEGER_LITERAL> |
| ) |
| { |
| try { |
| return Integer.parseInt(t.image); |
| } catch (NumberFormatException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName())); |
| } |
| } |
| | |
| <MINUS> t = <UNSIGNED_INTEGER_LITERAL> { |
| try { |
| return -Integer.parseInt(t.image); |
| } catch (NumberFormatException ex) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.invalidLiteral(t.image, Integer.class.getCanonicalName())); |
| } |
| } |
| } |
| |
| // Type name with optional scale and precision. |
| SqlDataTypeSpec DataType() : |
| { |
| SqlTypeNameSpec typeName; |
| final Span s; |
| } |
| { |
| typeName = TypeName() { |
| s = Span.of(typeName.getParserPos()); |
| } |
| ( |
| typeName = CollectionsTypeName(typeName) |
| )* |
| { |
| return new SqlDataTypeSpec(typeName, s.add(typeName.getParserPos()).pos()); |
| } |
| } |
| |
| // Some SQL type names need special handling due to the fact that they have |
| // spaces in them but are not quoted. |
| SqlTypeNameSpec TypeName() : |
| { |
| final SqlTypeNameSpec typeNameSpec; |
| final SqlIdentifier typeName; |
| final Span s = Span.of(); |
| } |
| { |
| ( |
| <#-- additional types are included here --> |
| <#-- put custom data types in front of Calcite core data types --> |
| <#list (parser.dataTypeParserMethods!default.parser.dataTypeParserMethods) as method> |
| LOOKAHEAD(2) |
| typeNameSpec = ${method} |
| | |
| </#list> |
| LOOKAHEAD(2) |
| typeNameSpec = SqlTypeName(s) |
| | |
| typeNameSpec = RowTypeName() |
| | |
| typeName = CompoundIdentifier() { |
| typeNameSpec = new SqlUserDefinedTypeNameSpec(typeName, s.end(this)); |
| } |
| ) |
| { |
| return typeNameSpec; |
| } |
| } |
| |
| // Types used for JDBC and ODBC scalar conversion function |
| SqlTypeNameSpec SqlTypeName(Span s) : |
| { |
| final SqlTypeNameSpec sqlTypeNameSpec; |
| } |
| { |
| ( |
| sqlTypeNameSpec = SqlTypeName1(s) |
| | |
| sqlTypeNameSpec = SqlTypeName2(s) |
| | |
| sqlTypeNameSpec = SqlTypeName3(s) |
| | |
| sqlTypeNameSpec = CharacterTypeName(s) |
| | |
| sqlTypeNameSpec = DateTimeTypeName() |
| ) |
| { |
| return sqlTypeNameSpec; |
| } |
| } |
| |
| // Parse sql type name that don't allow any extra specifications except the type name. |
| // For extra specification, we mean precision, scale, charSet, etc. |
| SqlTypeNameSpec SqlTypeName1(Span s) : |
| { |
| final SqlTypeName sqlTypeName; |
| } |
| { |
| ( |
| <GEOMETRY> { |
| if (!this.conformance.allowGeometry()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.geometryDisabled()); |
| } |
| s.add(this); |
| sqlTypeName = SqlTypeName.GEOMETRY; |
| } |
| | |
| <BOOLEAN> { s.add(this); sqlTypeName = SqlTypeName.BOOLEAN; } |
| | |
| ( <INTEGER> | <INT> ) { s.add(this); sqlTypeName = SqlTypeName.INTEGER; } |
| | |
| <TINYINT> { s.add(this); sqlTypeName = SqlTypeName.TINYINT; } |
| | |
| <SMALLINT> { s.add(this); sqlTypeName = SqlTypeName.SMALLINT; } |
| | |
| <BIGINT> { s.add(this); sqlTypeName = SqlTypeName.BIGINT; } |
| | |
| <REAL> { s.add(this); sqlTypeName = SqlTypeName.REAL; } |
| | |
| <DOUBLE> { s.add(this); } |
| [ <PRECISION> ] { sqlTypeName = SqlTypeName.DOUBLE; } |
| | |
| <FLOAT> { s.add(this); sqlTypeName = SqlTypeName.FLOAT; } |
| ) |
| { |
| return new SqlBasicTypeNameSpec(sqlTypeName, s.end(this)); |
| } |
| } |
| |
| // Parse sql type name that allows precision specification. |
| SqlTypeNameSpec SqlTypeName2(Span s) : |
| { |
| final SqlTypeName sqlTypeName; |
| int precision = -1; |
| } |
| { |
| ( |
| <BINARY> { s.add(this); } |
| ( |
| <VARYING> { sqlTypeName = SqlTypeName.VARBINARY; } |
| | |
| { sqlTypeName = SqlTypeName.BINARY; } |
| ) |
| | |
| <VARBINARY> { s.add(this); sqlTypeName = SqlTypeName.VARBINARY; } |
| ) |
| precision = PrecisionOpt() |
| { |
| return new SqlBasicTypeNameSpec(sqlTypeName, precision, s.end(this)); |
| } |
| } |
| |
| // Parse sql type name that allows precision and scale specifications. |
| SqlTypeNameSpec SqlTypeName3(Span s) : |
| { |
| final SqlTypeName sqlTypeName; |
| int precision = -1; |
| int scale = -1; |
| } |
| { |
| ( |
| (<DECIMAL> | <DEC> | <NUMERIC>) { s.add(this); sqlTypeName = SqlTypeName.DECIMAL; } |
| | |
| <ANY> { s.add(this); sqlTypeName = SqlTypeName.ANY; } |
| ) |
| [ |
| <LPAREN> |
| precision = UnsignedIntLiteral() |
| [ |
| <COMMA> |
| scale = UnsignedIntLiteral() |
| ] |
| <RPAREN> |
| ] |
| { |
| return new SqlBasicTypeNameSpec(sqlTypeName, precision, scale, s.end(this)); |
| } |
| } |
| |
| // Types used for for JDBC and ODBC scalar conversion function |
| SqlJdbcDataTypeName JdbcOdbcDataTypeName() : |
| { |
| } |
| { |
| (<SQL_CHAR> | <CHAR>) { return SqlJdbcDataTypeName.SQL_CHAR; } |
| | (<SQL_VARCHAR> | <VARCHAR>) { return SqlJdbcDataTypeName.SQL_VARCHAR; } |
| | (<SQL_DATE> | <DATE>) { return SqlJdbcDataTypeName.SQL_DATE; } |
| | (<SQL_TIME> | <TIME>) { return SqlJdbcDataTypeName.SQL_TIME; } |
| | (<SQL_TIMESTAMP> | <TIMESTAMP>) { return SqlJdbcDataTypeName.SQL_TIMESTAMP; } |
| | (<SQL_DECIMAL> | <DECIMAL>) { return SqlJdbcDataTypeName.SQL_DECIMAL; } |
| | (<SQL_NUMERIC> | <NUMERIC>) { return SqlJdbcDataTypeName.SQL_NUMERIC; } |
| | (<SQL_BOOLEAN> | <BOOLEAN>) { return SqlJdbcDataTypeName.SQL_BOOLEAN; } |
| | (<SQL_INTEGER> | <INTEGER>) { return SqlJdbcDataTypeName.SQL_INTEGER; } |
| | (<SQL_BINARY> | <BINARY>) { return SqlJdbcDataTypeName.SQL_BINARY; } |
| | (<SQL_VARBINARY> | <VARBINARY>) { return SqlJdbcDataTypeName.SQL_VARBINARY; } |
| | (<SQL_TINYINT> | <TINYINT>) { return SqlJdbcDataTypeName.SQL_TINYINT; } |
| | (<SQL_SMALLINT> | <SMALLINT>) { return SqlJdbcDataTypeName.SQL_SMALLINT; } |
| | (<SQL_BIGINT> | <BIGINT>) { return SqlJdbcDataTypeName.SQL_BIGINT; } |
| | (<SQL_REAL>| <REAL>) { return SqlJdbcDataTypeName.SQL_REAL; } |
| | (<SQL_DOUBLE> | <DOUBLE>) { return SqlJdbcDataTypeName.SQL_DOUBLE; } |
| | (<SQL_FLOAT> | <FLOAT>) { return SqlJdbcDataTypeName.SQL_FLOAT; } |
| | <SQL_INTERVAL_YEAR> { return SqlJdbcDataTypeName.SQL_INTERVAL_YEAR; } |
| | <SQL_INTERVAL_YEAR_TO_MONTH> { return SqlJdbcDataTypeName.SQL_INTERVAL_YEAR_TO_MONTH; } |
| | <SQL_INTERVAL_MONTH> { return SqlJdbcDataTypeName.SQL_INTERVAL_MONTH; } |
| | <SQL_INTERVAL_DAY> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY; } |
| | <SQL_INTERVAL_DAY_TO_HOUR> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_HOUR; } |
| | <SQL_INTERVAL_DAY_TO_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_MINUTE; } |
| | <SQL_INTERVAL_DAY_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_DAY_TO_SECOND; } |
| | <SQL_INTERVAL_HOUR> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR; } |
| | <SQL_INTERVAL_HOUR_TO_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR_TO_MINUTE; } |
| | <SQL_INTERVAL_HOUR_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_HOUR_TO_SECOND; } |
| | <SQL_INTERVAL_MINUTE> { return SqlJdbcDataTypeName.SQL_INTERVAL_MINUTE; } |
| | <SQL_INTERVAL_MINUTE_TO_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_MINUTE_TO_SECOND; } |
| | <SQL_INTERVAL_SECOND> { return SqlJdbcDataTypeName.SQL_INTERVAL_SECOND; } |
| } |
| |
| SqlLiteral JdbcOdbcDataType() : |
| { |
| SqlJdbcDataTypeName typeName; |
| } |
| { |
| typeName = JdbcOdbcDataTypeName() { |
| return typeName.symbol(getPos()); |
| } |
| } |
| |
| /** |
| * Parse a collection type name, the input element type name may |
| * also be a collection type. |
| */ |
| SqlTypeNameSpec CollectionsTypeName(SqlTypeNameSpec elementTypeName) : |
| { |
| final SqlTypeName collectionTypeName; |
| } |
| { |
| ( |
| <MULTISET> { collectionTypeName = SqlTypeName.MULTISET; } |
| | |
| <ARRAY> { collectionTypeName = SqlTypeName.ARRAY; } |
| ) |
| { |
| return new SqlCollectionTypeNameSpec(elementTypeName, |
| collectionTypeName, getPos()); |
| } |
| } |
| |
| /** |
| * Parse a nullable option, default is true. |
| */ |
| boolean NullableOptDefaultTrue() : |
| { |
| } |
| { |
| <NULL> { return true; } |
| | |
| <NOT> <NULL> { return false; } |
| | |
| { return true; } |
| } |
| |
| /** |
| * Parse a nullable option, default is false. |
| */ |
| boolean NullableOptDefaultFalse() : |
| { |
| } |
| { |
| <NULL> { return true; } |
| | |
| <NOT> <NULL> { return false; } |
| | |
| { return false; } |
| } |
| |
| /** Parses NOT NULL and returns false, or parses nothing and returns true. */ |
| boolean NotNullOpt() : |
| { |
| } |
| { |
| <NOT> <NULL> { return false; } |
| | |
| { return true; } |
| } |
| |
| /** |
| * Parse a "name1 type1 [NULL | NOT NULL], name2 type2 [NULL | NOT NULL] ..." list, |
| * the field type default is not nullable. |
| */ |
| void AddFieldNameTypes(List<SqlIdentifier> fieldNames, |
| List<SqlDataTypeSpec> fieldTypes) : |
| { |
| } |
| { |
| AddFieldNameType(fieldNames, fieldTypes) |
| ( <COMMA> AddFieldNameType(fieldNames, fieldTypes) )* |
| } |
| |
| void AddFieldNameType(List<SqlIdentifier> fieldNames, |
| List<SqlDataTypeSpec> fieldTypes) : |
| { |
| final SqlIdentifier fName; |
| final SqlDataTypeSpec fType; |
| final boolean nullable; |
| } |
| { |
| fName = SimpleIdentifier() |
| fType = DataType() |
| nullable = NullableOptDefaultFalse() |
| { |
| fieldNames.add(fName); |
| fieldTypes.add(fType.withNullable(nullable, getPos())); |
| } |
| } |
| |
| /** |
| * Parse Row type with format: Row(name1 type1, name2 type2). |
| * Every field type can have suffix of `NULL` or `NOT NULL` to indicate if this type is nullable. |
| * i.e. Row(f0 int not null, f1 varchar null). |
| */ |
| SqlTypeNameSpec RowTypeName() : |
| { |
| List<SqlIdentifier> fieldNames = new ArrayList<SqlIdentifier>(); |
| List<SqlDataTypeSpec> fieldTypes = new ArrayList<SqlDataTypeSpec>(); |
| } |
| { |
| <ROW> |
| <LPAREN> AddFieldNameTypes(fieldNames, fieldTypes) <RPAREN> |
| { |
| return new SqlRowTypeNameSpec(getPos(), fieldNames, fieldTypes); |
| } |
| } |
| |
| /** |
| * Parse character types: char, varchar. |
| */ |
| SqlTypeNameSpec CharacterTypeName(Span s) : |
| { |
| int precision = -1; |
| final SqlTypeName sqlTypeName; |
| String charSetName = null; |
| } |
| { |
| ( |
| (<CHARACTER> | <CHAR>) { s.add(this); } |
| ( |
| <VARYING> { sqlTypeName = SqlTypeName.VARCHAR; } |
| | |
| { sqlTypeName = SqlTypeName.CHAR; } |
| ) |
| | |
| <VARCHAR> { s.add(this); sqlTypeName = SqlTypeName.VARCHAR; } |
| ) |
| precision = PrecisionOpt() |
| [ |
| <CHARACTER> <SET> |
| charSetName = Identifier() |
| ] |
| { |
| return new SqlBasicTypeNameSpec(sqlTypeName, precision, charSetName, s.end(this)); |
| } |
| } |
| |
| /** |
| * Parse datetime types: date, time, timestamp. |
| */ |
| SqlTypeNameSpec DateTimeTypeName() : |
| { |
| int precision = -1; |
| SqlTypeName typeName; |
| boolean withLocalTimeZone = false; |
| final Span s; |
| } |
| { |
| <DATE> { |
| typeName = SqlTypeName.DATE; |
| return new SqlBasicTypeNameSpec(typeName, getPos()); |
| } |
| | |
| LOOKAHEAD(2) |
| <TIME> { s = span(); } |
| precision = PrecisionOpt() |
| withLocalTimeZone = TimeZoneOpt() |
| { |
| if (withLocalTimeZone) { |
| typeName = SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE; |
| } else { |
| typeName = SqlTypeName.TIME; |
| } |
| return new SqlBasicTypeNameSpec(typeName, precision, s.end(this)); |
| } |
| | |
| <TIMESTAMP> { s = span(); } |
| precision = PrecisionOpt() |
| withLocalTimeZone = TimeZoneOpt() |
| { |
| if (withLocalTimeZone) { |
| typeName = SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE; |
| } else { |
| typeName = SqlTypeName.TIMESTAMP; |
| } |
| return new SqlBasicTypeNameSpec(typeName, precision, s.end(this)); |
| } |
| } |
| |
| // Parse an optional data type precision, default is -1. |
| int PrecisionOpt() : |
| { |
| int precision = -1; |
| } |
| { |
| <LPAREN> |
| precision = UnsignedIntLiteral() |
| <RPAREN> |
| { return precision; } |
| | |
| { return -1; } |
| } |
| |
| /** |
| * Parse a time zone suffix for DateTime types. According to SQL-2011, |
| * "with time zone" and "without time zone" belong to standard SQL but we |
| * only implement the "without time zone". |
| * |
| * <p>We also support "with local time zone". |
| * |
| * @return true if this is "with local time zone". |
| */ |
| boolean TimeZoneOpt() : |
| { |
| } |
| { |
| LOOKAHEAD(3) |
| <WITHOUT> <TIME> <ZONE> { return false; } |
| | |
| <WITH> <LOCAL> <TIME> <ZONE> { return true; } |
| | |
| { return false; } |
| } |
| |
| /** |
| * Parses a CURSOR(query) expression. The parser allows these |
| * anywhere, but the validator restricts them to appear only as |
| * arguments to table functions. |
| */ |
| SqlNode CursorExpression(ExprContext exprContext) : |
| { |
| final SqlNode e; |
| final Span s; |
| } |
| { |
| <CURSOR> { |
| s = span(); |
| if (exprContext != ExprContext.ACCEPT_ALL |
| && exprContext != ExprContext.ACCEPT_CURSOR) { |
| throw SqlUtil.newContextException(s.end(this), |
| RESOURCE.illegalCursorExpression()); |
| } |
| } |
| e = Expression(ExprContext.ACCEPT_QUERY) { |
| return SqlStdOperatorTable.CURSOR.createCall(s.end(e), e); |
| } |
| } |
| |
| /** |
| * Parses a call to a builtin function with special syntax. |
| */ |
| SqlNode BuiltinFunctionCall() : |
| { |
| final SqlIdentifier name; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| SqlNode e; |
| final Span s; |
| SqlDataTypeSpec dt; |
| final SqlIntervalQualifier unit; |
| final SqlNode node; |
| final SqlLiteral style; // mssql convert 'style' operand |
| final SqlFunction f; |
| } |
| { |
| //~ FUNCTIONS WITH SPECIAL SYNTAX --------------------------------------- |
| ( |
| ( <CAST> { f = SqlStdOperatorTable.CAST; } |
| | <SAFE_CAST> { f = SqlLibraryOperators.SAFE_CAST; } |
| ) |
| { s = span(); } |
| <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <AS> |
| ( |
| dt = DataType() { args.add(dt); } |
| | |
| <INTERVAL> e = IntervalQualifier() { args.add(e); } |
| ) |
| <RPAREN> { |
| return f.createCall(s.end(this), args); |
| } |
| | |
| <EXTRACT> { s = span(); } |
| <LPAREN> unit = TimeUnitOrName() { |
| args.add(unit); |
| } |
| <FROM> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.EXTRACT.createCall(s.end(this), args); |
| } |
| | |
| <POSITION> { s = span(); } |
| <LPAREN> |
| // FIXME jvs 31-Aug-2006: FRG-192: This should be |
| // Expression(ExprContext.ACCEPT_SUB_QUERY), but that doesn't work |
| // because it matches the other kind of IN. |
| e = AtomicRowExpression() { args.add(e); } |
| <IN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| [ <FROM> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) ] |
| <RPAREN> { |
| return SqlStdOperatorTable.POSITION.createCall(s.end(this), args); |
| } |
| | |
| <CONVERT> { s = span(); } |
| <LPAREN> |
| ( |
| // CONVERT in the form of CONVERT(x USING y) |
| |
| // "AddExpression" matches INTERVAL, |
| // which can also be 1st token in args of MSSQL CONVERT |
| // So lookahead another token (to match <USING> vs. <COMMA>) |
| LOOKAHEAD(2) |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <USING> name = SimpleIdentifier() { args.add(name); } |
| <RPAREN> { |
| return SqlStdOperatorTable.CONVERT.createCall(s.end(this), args); |
| } |
| | // mssql CONVERT(type, val [,style]) |
| ( |
| dt = DataType() { args.add(dt); } |
| | |
| <INTERVAL> e = IntervalQualifier() { args.add(e); } |
| ) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| [ |
| <COMMA> |
| ( |
| style = UnsignedNumericLiteral() { args.add(style); } |
| | |
| <NULL> { args.add(SqlLiteral.createNull(getPos())); } |
| ) |
| ] |
| <RPAREN> { |
| return SqlLibraryOperators.MSSQL_CONVERT.createCall(s.end(this), args); |
| } |
| ) |
| | |
| <TRANSLATE> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <USING> name = SimpleIdentifier() { args.add(name); } |
| <RPAREN> { |
| return SqlStdOperatorTable.TRANSLATE.createCall(s.end(this), |
| args); |
| } |
| | |
| ( <COMMA> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) )* |
| <RPAREN> { |
| return SqlLibraryOperators.TRANSLATE3.createCall(s.end(this), |
| args); |
| } |
| ) |
| | |
| <OVERLAY> { s = span(); } |
| <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <PLACING> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <FROM> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| [ <FOR> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) ] |
| <RPAREN> { |
| return SqlStdOperatorTable.OVERLAY.createCall(s.end(this), args); |
| } |
| | |
| <FLOOR> { s = span(); } |
| e = FloorCeilOptions(s, true) { |
| return e; |
| } |
| | |
| ( <CEIL> | <CEILING>) { s = span(); } |
| e = FloorCeilOptions(s, false) { |
| return e; |
| } |
| | |
| <SUBSTRING> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( <FROM> | <COMMA>) |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| [ |
| (<FOR> | <COMMA>) |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ] |
| <RPAREN> { |
| return SqlStdOperatorTable.SUBSTRING.createCall( |
| s.end(this), args); |
| } |
| | |
| <TRIM> { |
| SqlLiteral flag = null; |
| SqlNode trimChars = null; |
| s = span(); |
| } |
| <LPAREN> |
| [ |
| LOOKAHEAD(2) |
| [ |
| <BOTH> { |
| s.add(this); |
| flag = SqlTrimFunction.Flag.BOTH.symbol(getPos()); |
| } |
| | |
| <TRAILING> { |
| s.add(this); |
| flag = SqlTrimFunction.Flag.TRAILING.symbol(getPos()); |
| } |
| | |
| <LEADING> { |
| s.add(this); |
| flag = SqlTrimFunction.Flag.LEADING.symbol(getPos()); |
| } |
| ] |
| [ trimChars = Expression(ExprContext.ACCEPT_SUB_QUERY) ] |
| ( |
| <FROM> { |
| if (null == flag && null == trimChars) { |
| throw SqlUtil.newContextException(getPos(), |
| RESOURCE.illegalFromEmpty()); |
| } |
| } |
| | |
| <RPAREN> { |
| // This is to handle the case of TRIM(x) |
| // (FRG-191). |
| if (flag == null) { |
| flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO); |
| } |
| args.add(flag); |
| args.add(null); // no trim chars |
| args.add(trimChars); // reinterpret trimChars as source |
| return SqlStdOperatorTable.TRIM.createCall(s.end(this), |
| args); |
| } |
| ) |
| ] |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| if (flag == null) { |
| flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO); |
| } |
| args.add(flag); |
| args.add(trimChars); |
| args.add(e); |
| } |
| <RPAREN> { |
| return SqlStdOperatorTable.TRIM.createCall(s.end(this), args); |
| } |
| | |
| node = DateTimeConstructorCall() { return node; } |
| | |
| node = DateTruncFunctionCall() { return node; } |
| | |
| node = TimestampAddFunctionCall() { return node; } |
| | |
| node = TimestampDiffFunctionCall() { return node; } |
| | |
| node = TimestampDiff3FunctionCall() { return node; } |
| | |
| node = TimestampTruncFunctionCall() { return node; } |
| | |
| node = TimeDiffFunctionCall() { return node; } |
| | |
| node = TimeTruncFunctionCall() { return node; } |
| | |
| <#list (parser.builtinFunctionCallMethods!default.parser.builtinFunctionCallMethods) as method> |
| node = ${method} { return node; } |
| | |
| </#list> |
| node = MatchRecognizeFunctionCall() { return node; } |
| | |
| node = JsonExistsFunctionCall() { return node; } |
| | |
| node = JsonValueFunctionCall() { return node; } |
| | |
| node = JsonQueryFunctionCall() { return node; } |
| | |
| node = JsonObjectFunctionCall() { return node; } |
| | |
| node = JsonObjectAggFunctionCall() { return node; } |
| | |
| node = JsonArrayFunctionCall() { return node; } |
| | |
| node = JsonArrayAggFunctionCall() { return node; } |
| | |
| node = GroupByWindowingCall() { return node; } |
| ) |
| } |
| |
| SqlJsonEncoding JsonRepresentation() : |
| { |
| } |
| { |
| <JSON> |
| [ |
| // Encoding is currently ignored. |
| LOOKAHEAD(2) <ENCODING> |
| ( |
| <UTF8> { return SqlJsonEncoding.UTF8; } |
| | |
| <UTF16> { return SqlJsonEncoding.UTF16; } |
| | |
| <UTF32> { return SqlJsonEncoding.UTF32; } |
| ) |
| ] |
| { |
| return SqlJsonEncoding.UTF8; |
| } |
| } |
| |
| void JsonInputClause() : |
| { |
| } |
| { |
| <FORMAT> JsonRepresentation() |
| } |
| |
| SqlDataTypeSpec JsonReturningClause() : |
| { |
| SqlDataTypeSpec dt; |
| } |
| { |
| <RETURNING> dt = DataType() { return dt; } |
| } |
| |
| SqlDataTypeSpec JsonOutputClause() : |
| { |
| SqlDataTypeSpec dataType; |
| } |
| { |
| dataType = JsonReturningClause() |
| [ |
| <FORMAT> JsonRepresentation() |
| ] |
| { |
| return dataType; |
| } |
| } |
| |
| SqlNode JsonPathSpec() : |
| { |
| SqlNode e; |
| } |
| { |
| e = StringLiteral() { |
| return e; |
| } |
| } |
| |
| List<SqlNode> JsonApiCommonSyntax() : |
| { |
| SqlNode e; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| } |
| { |
| AddExpression(args, ExprContext.ACCEPT_NON_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_NON_QUERY) |
| [ |
| // We currently don't support JSON passing clause, leave the java code blocks no-op |
| <PASSING> e = Expression(ExprContext.ACCEPT_NON_QUERY) { |
| // no-op |
| } |
| <AS> e = SimpleIdentifier() { |
| // no-op |
| } |
| ( |
| <COMMA> |
| e = Expression(ExprContext.ACCEPT_NON_QUERY) { |
| // no-op |
| } |
| <AS> e = SimpleIdentifier() { |
| // no-op |
| } |
| )* |
| ] |
| { |
| return args; |
| } |
| } |
| |
| SqlJsonExistsErrorBehavior JsonExistsErrorBehavior() : |
| { |
| |
| } |
| { |
| <TRUE> { return SqlJsonExistsErrorBehavior.TRUE; } |
| | |
| <FALSE> { return SqlJsonExistsErrorBehavior.FALSE; } |
| | |
| <UNKNOWN> { return SqlJsonExistsErrorBehavior.UNKNOWN; } |
| | |
| <ERROR> { return SqlJsonExistsErrorBehavior.ERROR; } |
| } |
| |
| SqlCall JsonExistsFunctionCall() : |
| { |
| List<SqlNode> args = new ArrayList<SqlNode>(); |
| List<SqlNode> commonSyntax; |
| final Span span; |
| SqlJsonExistsErrorBehavior errorBehavior; |
| } |
| { |
| <JSON_EXISTS> { span = span(); } |
| <LPAREN> commonSyntax = JsonApiCommonSyntax() { |
| args.addAll(commonSyntax); |
| } |
| [ |
| errorBehavior = JsonExistsErrorBehavior() { args.add(errorBehavior.symbol(getPos())); } |
| <ON> <ERROR> |
| ] |
| <RPAREN> { |
| return SqlStdOperatorTable.JSON_EXISTS.createCall(span.end(this), args); |
| } |
| } |
| |
| List<SqlNode> JsonValueEmptyOrErrorBehavior() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| ( |
| <ERROR> { |
| list.add(SqlJsonValueEmptyOrErrorBehavior.ERROR.symbol(getPos())); |
| } |
| | |
| <NULL> { |
| list.add(SqlJsonValueEmptyOrErrorBehavior.NULL.symbol(getPos())); |
| } |
| | |
| <DEFAULT_> { |
| list.add(SqlJsonValueEmptyOrErrorBehavior.DEFAULT.symbol(getPos())); |
| } |
| AddExpression(list, ExprContext.ACCEPT_NON_QUERY) |
| ) |
| <ON> |
| ( |
| <EMPTY> { |
| list.add(SqlJsonEmptyOrError.EMPTY.symbol(getPos())); |
| } |
| | |
| <ERROR> { |
| list.add(SqlJsonEmptyOrError.ERROR.symbol(getPos())); |
| } |
| ) |
| { return list; } |
| } |
| |
| SqlCall JsonValueFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(7); |
| SqlNode e; |
| List<SqlNode> commonSyntax; |
| final Span span; |
| List<SqlNode> behavior; |
| } |
| { |
| <JSON_VALUE> { span = span(); } |
| <LPAREN> commonSyntax = JsonApiCommonSyntax() { |
| args.addAll(commonSyntax); |
| } |
| [ |
| e = JsonReturningClause() { |
| args.add(SqlJsonValueReturning.RETURNING.symbol(getPos())); |
| args.add(e); |
| } |
| ] |
| ( |
| behavior = JsonValueEmptyOrErrorBehavior() { |
| args.addAll(behavior); |
| } |
| )* |
| <RPAREN> { |
| return SqlStdOperatorTable.JSON_VALUE.createCall(span.end(this), args); |
| } |
| } |
| |
| List<SqlNode> JsonQueryEmptyOrErrorBehavior() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| } |
| { |
| ( |
| <ERROR> { |
| list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.ERROR, getPos())); |
| } |
| | |
| <NULL> { |
| list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.NULL, getPos())); |
| } |
| | |
| LOOKAHEAD(2) |
| <EMPTY> <ARRAY> { |
| list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_ARRAY, getPos())); |
| } |
| | |
| <EMPTY> <OBJECT> { |
| list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_OBJECT, getPos())); |
| } |
| ) |
| <ON> |
| ( |
| <EMPTY> { |
| list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.EMPTY, getPos())); |
| } |
| | |
| <ERROR> { |
| list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.ERROR, getPos())); |
| } |
| ) |
| { return list; } |
| } |
| |
| SqlNode JsonQueryWrapperBehavior() : |
| { |
| } |
| { |
| <WITHOUT> [<ARRAY>] { |
| return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITHOUT_ARRAY, getPos()); |
| } |
| | |
| LOOKAHEAD(2) |
| <WITH> <CONDITIONAL> [<ARRAY>] { |
| return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_CONDITIONAL_ARRAY, getPos()); |
| } |
| | |
| <WITH> [<UNCONDITIONAL>] [<ARRAY>] { |
| return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_UNCONDITIONAL_ARRAY, getPos()); |
| } |
| } |
| |
| SqlCall JsonQueryFunctionCall() : |
| { |
| final SqlNode[] args = new SqlNode[5]; |
| SqlNode e; |
| List<SqlNode> commonSyntax; |
| final Span span; |
| List<SqlNode> behavior; |
| } |
| { |
| <JSON_QUERY> { span = span(); } |
| <LPAREN> commonSyntax = JsonApiCommonSyntax() { |
| args[0] = commonSyntax.get(0); |
| args[1] = commonSyntax.get(1); |
| } |
| [ |
| e = JsonQueryWrapperBehavior() <WRAPPER> { |
| args[2] = e; |
| } |
| ] |
| ( |
| behavior = JsonQueryEmptyOrErrorBehavior() { |
| final SqlJsonEmptyOrError symbol = |
| ((SqlLiteral) behavior.get(1)).getValueAs(SqlJsonEmptyOrError.class); |
| switch (symbol) { |
| case EMPTY: |
| args[3] = behavior.get(0); |
| break; |
| case ERROR: |
| args[4] = behavior.get(0); |
| break; |
| } |
| } |
| )* |
| <RPAREN> { |
| return SqlStdOperatorTable.JSON_QUERY.createCall(span.end(this), args); |
| } |
| } |
| |
| SqlNode JsonName() : |
| { |
| final SqlNode e; |
| } |
| { |
| e = Expression(ExprContext.ACCEPT_NON_QUERY) { |
| return e; |
| } |
| } |
| |
| List<SqlNode> JsonNameAndValue() : |
| { |
| final List<SqlNode> list = new ArrayList<SqlNode>(); |
| final SqlNode e; |
| boolean kvMode = false; |
| } |
| { |
| [ |
| LOOKAHEAD(2, <KEY> JsonName()) |
| <KEY> { kvMode = true; } |
| ] |
| e = JsonName() { |
| list.add(e); |
| } |
| ( |
| <VALUE> |
| | |
| <COLON> { |
| if (kvMode) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.illegalColon()); |
| } |
| } |
| ) |
| AddExpression(list, ExprContext.ACCEPT_NON_QUERY) |
| { |
| return list; |
| } |
| } |
| |
| SqlNode JsonConstructorNullClause() : |
| { |
| } |
| { |
| <NULL> <ON> <NULL> { |
| return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.NULL_ON_NULL, getPos()); |
| } |
| | |
| <ABSENT> <ON> <NULL> { |
| return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.ABSENT_ON_NULL, getPos()); |
| } |
| } |
| |
| SqlCall JsonObjectFunctionCall() : |
| { |
| final List<SqlNode> nvArgs = new ArrayList<SqlNode>(); |
| final SqlNode[] otherArgs = new SqlNode[1]; |
| SqlNode e; |
| List<SqlNode> list; |
| final Span span; |
| } |
| { |
| <JSON_OBJECT> { span = span(); } |
| <LPAREN> [ |
| LOOKAHEAD(2) |
| list = JsonNameAndValue() { |
| nvArgs.addAll(list); |
| } |
| ( |
| <COMMA> |
| list = JsonNameAndValue() { |
| nvArgs.addAll(list); |
| } |
| )* |
| ] |
| [ |
| e = JsonConstructorNullClause() { |
| otherArgs[0] = e; |
| } |
| ] |
| <RPAREN> { |
| final List<SqlNode> args = new ArrayList(); |
| args.addAll(Arrays.asList(otherArgs)); |
| args.addAll(nvArgs); |
| return SqlStdOperatorTable.JSON_OBJECT.createCall(span.end(this), args); |
| } |
| } |
| |
| SqlCall JsonObjectAggFunctionCall() : |
| { |
| final SqlNode[] args = new SqlNode[2]; |
| List<SqlNode> list; |
| final Span span; |
| SqlJsonConstructorNullClause nullClause = |
| SqlJsonConstructorNullClause.NULL_ON_NULL; |
| final SqlNode e; |
| } |
| { |
| <JSON_OBJECTAGG> { span = span(); } |
| <LPAREN> list = JsonNameAndValue() { |
| args[0] = list.get(0); |
| args[1] = list.get(1); |
| } |
| [ |
| e = JsonConstructorNullClause() { |
| nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue(); |
| } |
| ] |
| <RPAREN> { |
| return SqlStdOperatorTable.JSON_OBJECTAGG.with(nullClause) |
| .createCall(span.end(this), args); |
| } |
| } |
| |
| SqlCall JsonArrayFunctionCall() : |
| { |
| final List<SqlNode> elements = new ArrayList<SqlNode>(); |
| final SqlNode[] otherArgs = new SqlNode[1]; |
| SqlNode e; |
| final Span span; |
| } |
| { |
| <JSON_ARRAY> { span = span(); } |
| <LPAREN> [ |
| LOOKAHEAD(2) |
| AddExpression(elements, ExprContext.ACCEPT_NON_QUERY) |
| ( <COMMA> AddExpression(elements, ExprContext.ACCEPT_NON_QUERY) )* |
| ] |
| [ |
| e = JsonConstructorNullClause() { |
| otherArgs[0] = e; |
| } |
| ] |
| <RPAREN> { |
| final List<SqlNode> args = new ArrayList(); |
| args.addAll(Arrays.asList(otherArgs)); |
| args.addAll(elements); |
| return SqlStdOperatorTable.JSON_ARRAY.createCall(span.end(this), args); |
| } |
| } |
| |
| SqlNodeList JsonArrayAggOrderByClause() : |
| { |
| final SqlNodeList orderList; |
| } |
| { |
| orderList = OrderBy(true) |
| { return orderList; } |
| } |
| |
| SqlCall JsonArrayAggFunctionCall() : |
| { |
| final SqlNode valueExpr; |
| final SqlNodeList orderList; |
| final Span span; |
| final SqlJsonConstructorNullClause nullClause; |
| SqlNode e; |
| final SqlNode aggCall; |
| } |
| { |
| <JSON_ARRAYAGG> { span = span(); } |
| <LPAREN> e = Expression(ExprContext.ACCEPT_NON_QUERY) { |
| valueExpr = e; |
| } |
| ( orderList = JsonArrayAggOrderByClause() | { orderList = null; } ) |
| ( |
| e = JsonConstructorNullClause() { |
| nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue(); |
| } |
| | { nullClause = SqlJsonConstructorNullClause.ABSENT_ON_NULL; } |
| ) |
| <RPAREN> |
| { |
| aggCall = SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause) |
| .createCall(span.end(this), valueExpr, orderList); |
| } |
| [ |
| e = withinGroup(aggCall) { |
| if (orderList != null) { |
| throw SqlUtil.newContextException(span.pos().plus(e.getParserPosition()), |
| RESOURCE.ambiguousSortOrderInJsonArrayAggFunc()); |
| } |
| return (SqlCall) e; |
| } |
| ] |
| { |
| if (orderList == null) { |
| return SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause) |
| .createCall(span.end(this), valueExpr); |
| } |
| return SqlStdOperatorTable.JSON_ARRAYAGG.with(nullClause) |
| .createCall(span.end(this), valueExpr, orderList); |
| } |
| } |
| |
| /** |
| * Parses a call to TIMESTAMPADD. |
| */ |
| SqlCall TimestampAddFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIMESTAMPADD> { s = span(); } |
| <LPAREN> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.TIMESTAMP_ADD.createCall( |
| s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to TIMESTAMPDIFF. |
| */ |
| SqlCall TimestampDiffFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIMESTAMPDIFF> { s = span(); } |
| <LPAREN> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.TIMESTAMP_DIFF.createCall( |
| s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to BigQuery's TIMESTAMP_DIFF. |
| * |
| * <p>The difference between TIMESTAMPDIFF and TIMESTAMP_DIFF is the ordering of |
| * the parameters and the arrangement of the subtraction. |
| * TIMESTAMPDIFF uses (unit, timestamp1, timestamp2) with (t2 - t1), while |
| * TIMESTAMP_DIFF uses (timestamp1, timestamp2, unit) with (t1 - t2). |
| */ |
| SqlCall TimestampDiff3FunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIMESTAMP_DIFF> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <RPAREN> { |
| return SqlLibraryOperators.TIMESTAMP_DIFF3.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to DATE_TRUNC. |
| */ |
| SqlCall DateTruncFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <DATE_TRUNC> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| // A choice of arguments allows us to support both |
| // the BigQuery variant, e.g. "DATE_TRUNC(d, YEAR)", |
| // and the Redshift variant, e.g. "DATE_TRUNC('year', DATE '2008-09-08')". |
| ( |
| LOOKAHEAD(2) |
| unit = TimeUnit() { args.add(unit); } |
| | |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ) |
| <RPAREN> { |
| return SqlLibraryOperators.DATE_TRUNC.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to TIMESTAMP_TRUNC. |
| */ |
| SqlCall TimestampTruncFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIMESTAMP_TRUNC> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <RPAREN> { |
| return SqlLibraryOperators.TIMESTAMP_TRUNC.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to BigQuery's TIME_DIFF. |
| */ |
| SqlCall TimeDiffFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIME_DIFF> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <RPAREN> { |
| return SqlLibraryOperators.TIME_DIFF.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to TIME_TRUNC. |
| */ |
| SqlCall TimeTruncFunctionCall() : |
| { |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final Span s; |
| final SqlIntervalQualifier unit; |
| } |
| { |
| <TIME_TRUNC> { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| <COMMA> |
| unit = TimeUnitOrName() { args.add(unit); } |
| <RPAREN> { |
| return SqlLibraryOperators.TIME_TRUNC.createCall(s.end(this), args); |
| } |
| } |
| |
| /** |
| * Parses a call to a grouping function inside the GROUP BY clause, |
| * for example {@code TUMBLE(rowtime, INTERVAL '1' MINUTE)}. |
| */ |
| SqlCall GroupByWindowingCall() : |
| { |
| final Span s; |
| final List<SqlNode> args; |
| final SqlOperator op; |
| } |
| { |
| ( |
| <TUMBLE> { op = SqlStdOperatorTable.TUMBLE_OLD; } |
| | |
| <HOP> { op = SqlStdOperatorTable.HOP_OLD; } |
| | |
| <SESSION> { op = SqlStdOperatorTable.SESSION_OLD; } |
| ) |
| { s = span(); } |
| args = UnquantifiedFunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) { |
| return op.createCall(s.end(this), args); |
| } |
| } |
| |
| SqlCall MatchRecognizeFunctionCall() : |
| { |
| final SqlCall func; |
| final Span s; |
| } |
| { |
| ( |
| <CLASSIFIER> { s = span(); } <LPAREN> <RPAREN> { |
| func = SqlStdOperatorTable.CLASSIFIER.createCall(s.end(this)); |
| } |
| | |
| <MATCH_NUMBER> { s = span(); } <LPAREN> <RPAREN> { |
| func = SqlStdOperatorTable.MATCH_NUMBER.createCall(s.end(this)); |
| } |
| | |
| LOOKAHEAD(3) |
| func = MatchRecognizeNavigationLogical() |
| | |
| LOOKAHEAD(2) |
| func = MatchRecognizeNavigationPhysical() |
| | |
| func = MatchRecognizeCallWithModifier() |
| ) |
| { return func; } |
| } |
| |
| SqlCall MatchRecognizeCallWithModifier() : |
| { |
| final Span s; |
| final SqlOperator runningOp; |
| final SqlNode func; |
| } |
| { |
| ( |
| <RUNNING> { runningOp = SqlStdOperatorTable.RUNNING; } |
| | |
| <FINAL> { runningOp = SqlStdOperatorTable.FINAL; } |
| ) |
| { s = span(); } |
| func = NamedFunctionCall() { |
| return runningOp.createCall(s.end(func), func); |
| } |
| } |
| |
| SqlCall MatchRecognizeNavigationLogical() : |
| { |
| final Span s = Span.of(); |
| SqlCall func; |
| final SqlOperator funcOp; |
| final SqlOperator runningOp; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| SqlNode e; |
| } |
| { |
| ( |
| <RUNNING> { runningOp = SqlStdOperatorTable.RUNNING; s.add(this); } |
| | |
| <FINAL> { runningOp = SqlStdOperatorTable.FINAL; s.add(this); } |
| | { runningOp = null; } |
| ) |
| ( |
| <FIRST> { funcOp = SqlStdOperatorTable.FIRST; } |
| | |
| <LAST> { funcOp = SqlStdOperatorTable.LAST; } |
| ) |
| { s.add(this); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <COMMA> e = NumericLiteral() { args.add(e); } |
| | |
| { args.add(LITERAL_ZERO); } |
| ) |
| <RPAREN> { |
| func = funcOp.createCall(s.end(this), args); |
| if (runningOp != null) { |
| return runningOp.createCall(s.end(this), func); |
| } else { |
| return func; |
| } |
| } |
| } |
| |
| SqlCall MatchRecognizeNavigationPhysical() : |
| { |
| final Span s; |
| final SqlOperator funcOp; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| SqlNode e; |
| } |
| { |
| ( |
| <PREV> { funcOp = SqlStdOperatorTable.PREV; } |
| | |
| <NEXT> { funcOp = SqlStdOperatorTable.NEXT; } |
| ) |
| { s = span(); } |
| <LPAREN> |
| AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <COMMA> e = NumericLiteral() { args.add(e); } |
| | |
| { args.add(LITERAL_ONE); } |
| ) |
| <RPAREN> { |
| return funcOp.createCall(s.end(this), args); |
| } |
| } |
| |
| SqlCall withinDistinct(SqlNode arg) : |
| { |
| final Span s; |
| final SqlNodeList distinctList; |
| } |
| { |
| <WITHIN> { s = span(); } |
| <DISTINCT> |
| <LPAREN> |
| distinctList = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| return SqlStdOperatorTable.WITHIN_DISTINCT.createCall( |
| s.end(this), arg, distinctList); |
| } |
| } |
| |
| SqlCall withinGroup(SqlNode arg) : |
| { |
| final Span s; |
| final SqlNodeList orderList; |
| } |
| { |
| <WITHIN> { s = span(); } |
| <GROUP> |
| <LPAREN> |
| orderList = OrderBy(true) |
| <RPAREN> { |
| return SqlStdOperatorTable.WITHIN_GROUP.createCall( |
| s.end(this), arg, orderList); |
| } |
| } |
| |
| Pair<SqlParserPos, SqlOperator> NullTreatment() : |
| { |
| final Span span; |
| } |
| { |
| <IGNORE> { span = span(); } <NULLS> { |
| return Pair.of(span.end(this), SqlStdOperatorTable.IGNORE_NULLS); |
| } |
| | |
| <RESPECT> { span = span(); } <NULLS> { |
| return Pair.of(span.end(this), SqlStdOperatorTable.RESPECT_NULLS); |
| } |
| } |
| |
| SqlCall nullTreatment(SqlCall arg) : |
| { |
| final Pair<SqlParserPos, SqlOperator> pair; |
| } |
| { |
| pair = NullTreatment() { return pair.right.createCall(pair.left, arg); } |
| } |
| |
| /** |
| * Parses a call to a named function (could be a builtin with regular |
| * syntax, or else a UDF). |
| * |
| * <p>NOTE: every UDF has two names: an <em>invocation name</em> and a |
| * <em>specific name</em>. Normally, function calls are resolved via overload |
| * resolution and invocation names. The SPECIFIC prefix allows overload |
| * resolution to be bypassed. Note that usage of the SPECIFIC prefix in |
| * queries is non-standard; it is used internally by Farrago, e.g. in stored |
| * view definitions to permanently bind references to a particular function |
| * after the overload resolution performed by view creation. |
| * |
| * <p>TODO jvs 25-Mar-2005: Once we have SQL-Flagger support, flag SPECIFIC |
| * as non-standard. |
| */ |
| SqlNode NamedFunctionCall() : |
| { |
| SqlCall call; |
| final Span filterSpan; |
| final SqlNode filter; |
| final Span overSpan; |
| final SqlNode over; |
| } |
| { |
| ( |
| LOOKAHEAD(2) |
| call = StringAggFunctionCall() |
| | |
| call = NamedCall() |
| ) |
| [ |
| LOOKAHEAD(2) call = nullTreatment(call) |
| ] |
| [ |
| LOOKAHEAD(2) // decide between WITHIN DISTINCT and WITHIN GROUP |
| call = withinDistinct(call) |
| ] |
| [ |
| call = withinGroup(call) |
| ] |
| [ |
| <FILTER> { filterSpan = span(); } |
| <LPAREN> |
| <WHERE> |
| filter = Expression(ExprContext.ACCEPT_SUB_QUERY) |
| <RPAREN> { |
| call = SqlStdOperatorTable.FILTER.createCall( |
| filterSpan.end(this), call, filter); |
| } |
| ] |
| [ |
| <OVER> { overSpan = span(); } |
| ( |
| over = SimpleIdentifier() |
| | |
| over = WindowSpecification() |
| ) |
| { |
| call = SqlStdOperatorTable.OVER.createCall(overSpan.end(over), call, over); |
| } |
| ] |
| { |
| return call; |
| } |
| } |
| |
| SqlCall NamedCall() : |
| { |
| final SqlFunctionCategory funcType; |
| final SqlIdentifier qualifiedName; |
| final Span s; |
| final List<SqlNode> args; |
| SqlLiteral quantifier = null; |
| } |
| { |
| ( |
| <SPECIFIC> { |
| funcType = SqlFunctionCategory.USER_DEFINED_SPECIFIC_FUNCTION; |
| } |
| | |
| { funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION; } |
| ) |
| qualifiedName = FunctionName() { |
| s = span(); |
| } |
| ( |
| LOOKAHEAD(2) <LPAREN> <STAR> { |
| args = ImmutableList.of(SqlIdentifier.star(getPos())); |
| } |
| <RPAREN> |
| | |
| LOOKAHEAD(2) <LPAREN> <RPAREN> { |
| args = ImmutableList.of(); |
| } |
| | |
| args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) { |
| quantifier = (SqlLiteral) args.get(0); |
| args.remove(0); |
| } |
| ) |
| { |
| return createCall(qualifiedName, s.end(this), funcType, quantifier, args); |
| } |
| } |
| |
| /* |
| * Parse Floor/Ceil function parameters |
| */ |
| SqlNode StandardFloorCeilOptions(Span s, boolean floorFlag) : |
| { |
| SqlNode e; |
| final List<SqlNode> args = new ArrayList<SqlNode>(); |
| final SqlIntervalQualifier unit; |
| SqlCall function; |
| final Span s1; |
| } |
| { |
| <LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY) |
| ( |
| <TO> unit = TimeUnitOrName() { |
| args.add(unit); |
| } |
| )? |
| <RPAREN> { |
| SqlOperator op = floorFlag |
| ? SqlStdOperatorTable.FLOOR |
| : SqlStdOperatorTable.CEIL; |
| function = op.createCall(s.end(this), args); |
| } |
| ( |
| <OVER> { s1 = span(); } |
| ( |
| e = SimpleIdentifier() |
| | |
| e = WindowSpecification() |
| ) |
| { |
| return SqlStdOperatorTable.OVER.createCall(s1.end(this), function, e); |
| } |
| | |
| { return function; } |
| ) |
| } |
| |
| /** |
| * Parses the name of a JDBC function that is a token but is not reserved. |
| */ |
| String NonReservedJdbcFunctionName() : |
| { |
| } |
| { |
| ( |
| <SUBSTRING> |
| ) |
| { |
| return unquotedIdentifier(); |
| } |
| } |
| |
| /** |
| * Parses the name of a function (either a compound identifier or |
| * a reserved word which can be used as a function name). |
| */ |
| SqlIdentifier FunctionName() : |
| { |
| SqlIdentifier qualifiedName; |
| } |
| { |
| ( |
| qualifiedName = CompoundIdentifier() |
| | |
| qualifiedName = ReservedFunctionName() |
| ) |
| { |
| return qualifiedName; |
| } |
| } |
| |
| /** |
| * Parses a reserved word which is used as the name of a function. |
| */ |
| SqlIdentifier ReservedFunctionName() : |
| { |
| } |
| { |
| ( |
| <ABS> |
| | <AVG> |
| | <CARDINALITY> |
| | <CEILING> |
| | <CHAR> |
| | <CHAR_LENGTH> |
| | <CHARACTER_LENGTH> |
| | <COALESCE> |
| | <COLLECT> |
| | <COVAR_POP> |
| | <COVAR_SAMP> |
| | <CUME_DIST> |
| | <COUNT> |
| | <CURRENT_DATE> |
| | <CURRENT_TIME> |
| | <CURRENT_TIMESTAMP> |
| | <DENSE_RANK> |
| | <ELEMENT> |
| | <EVERY> |
| | <EXP> |
| | <FIRST_VALUE> |
| | <FLOOR> |
| | <FUSION> |
| | <INTERSECTION> |
| | <GROUPING> |
| | <HOUR> |
| | <LAG> |
| | <LEAD> |
| | <LEFT> |
| | <LAST_VALUE> |
| | <LN> |
| | <LOCALTIME> |
| | <LOCALTIMESTAMP> |
| | <LOWER> |
| | <MAX> |
| | <MIN> |
| | <MINUTE> |
| | <MOD> |
| | <MONTH> |
| | <NTH_VALUE> |
| | <NTILE> |
| | <NULLIF> |
| | <OCTET_LENGTH> |
| | <PERCENT_RANK> |
| | <PERCENTILE_CONT> |
| | <PERCENTILE_DISC> |
| | <POWER> |
| | <RANK> |
| | <REGR_COUNT> |
| | <REGR_SXX> |
| | <REGR_SYY> |
| | <RIGHT> |
| | <ROW_NUMBER> |
| | <SECOND> |
| | <SOME> |
| | <SQRT> |
| | <STDDEV_POP> |
| | <STDDEV_SAMP> |
| | <SUM> |
| | <UPPER> |
| | <TRUNCATE> |
| | <USER> |
| | <VAR_POP> |
| | <VAR_SAMP> |
| | <YEAR> |
| ) |
| { |
| return new SqlIdentifier(unquotedIdentifier(), getPos()); |
| } |
| } |
| |
| SqlIdentifier ContextVariable() : |
| { |
| } |
| { |
| ( |
| <CURRENT_CATALOG> |
| | <CURRENT_DATE> |
| | <CURRENT_DEFAULT_TRANSFORM_GROUP> |
| | <CURRENT_PATH> |
| | <CURRENT_ROLE> |
| | <CURRENT_SCHEMA> |
| | <CURRENT_TIME> |
| | <CURRENT_TIMESTAMP> |
| | <CURRENT_USER> |
| | <LOCALTIME> |
| | <LOCALTIMESTAMP> |
| | <SESSION_USER> |
| | <SYSTEM_USER> |
| | <USER> |
| ) |
| { |
| return new SqlIdentifier(unquotedIdentifier(), getPos()); |
| } |
| } |
| |
| /** |
| * Parses a function call expression with JDBC syntax. |
| */ |
| SqlNode JdbcFunctionCall() : |
| { |
| String name; |
| SqlIdentifier id; |
| SqlNode e; |
| SqlLiteral tl; |
| SqlNodeList args; |
| SqlCall call; |
| final Span s, s1; |
| } |
| { |
| <LBRACE_FN> { |
| s = span(); |
| } |
| ( |
| LOOKAHEAD(1) |
| call = TimestampAddFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(1) |
| call = DateTruncFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(1) |
| call = TimestampTruncFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(1) |
| call = TimeTruncFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(1) |
| call = TimestampDiff3FunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(1) |
| call = TimeDiffFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| LOOKAHEAD(3) |
| call = TimestampDiffFunctionCall() { |
| name = call.getOperator().getName(); |
| args = new SqlNodeList(call.getOperandList(), getPos()); |
| } |
| | |
| <CONVERT> { name = unquotedIdentifier(); } |
| <LPAREN> |
| e = Expression(ExprContext.ACCEPT_SUB_QUERY) { |
| args = new SqlNodeList(getPos()); |
| args.add(e); |
| } |
| <COMMA> |
| tl = JdbcOdbcDataType() { args.add(tl); } |
| <RPAREN> |
| | |
| ( |
| // INSERT is a reserved word, but we need to handle {fn insert} |
| // Similarly LEFT, RIGHT, TRUNCATE |
| LOOKAHEAD(1) |
| ( <INSERT> | <LEFT> | <RIGHT> | <TRUNCATE> ) { name = unquotedIdentifier(); } |
| | |
| // For cases like {fn power(1,2)} and {fn lower('a')} |
| id = ReservedFunctionName() { name = id.getSimple(); } |
| | |
| // For cases like {fn substring('foo', 1,2)} |
| name = NonReservedJdbcFunctionName() |
| | |
| name = Identifier() |
| ) |
| ( |
| LOOKAHEAD(2) <LPAREN> <STAR> { s1 = span(); } <RPAREN> |
| { |
| args = new SqlNodeList(s1.pos()); |
| args.add(SqlIdentifier.star(s1.pos())); |
| } |
| | |
| LOOKAHEAD(2) <LPAREN> <RPAREN> { args = SqlNodeList.EMPTY; } |
| | |
| args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY) |
| ) |
| ) |
| <RBRACE> { |
| return new SqlJdbcFunctionCall(name).createCall(s.end(this), |
| args.getList()); |
| } |
| } |
| |
| /** |
| * Parses a binary query operator like UNION. |
| */ |
| SqlBinaryOperator BinaryQueryOperator() : |
| { |
| } |
| { |
| // If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit. |
| ( |
| <UNION> |
| ( |
| <ALL> { return SqlStdOperatorTable.UNION_ALL; } |
| | <DISTINCT> { return SqlStdOperatorTable.UNION; } |
| | { return SqlStdOperatorTable.UNION; } |
| ) |
| | |
| <INTERSECT> |
| ( |
| <ALL> { return SqlStdOperatorTable.INTERSECT_ALL; } |
| | <DISTINCT> { return SqlStdOperatorTable.INTERSECT; } |
| | { return SqlStdOperatorTable.INTERSECT; } |
| ) |
| | |
| ( |
| <EXCEPT> |
| | |
| <SET_MINUS> { |
| if (!this.conformance.isMinusAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.minusNotAllowed()); |
| } |
| } |
| ) |
| ( |
| <ALL> { return SqlStdOperatorTable.EXCEPT_ALL; } |
| | <DISTINCT> { return SqlStdOperatorTable.EXCEPT; } |
| | { return SqlStdOperatorTable.EXCEPT; } |
| ) |
| ) |
| } |
| |
| /** |
| * Parses a binary multiset operator. |
| */ |
| SqlBinaryOperator BinaryMultisetOperator() : |
| { |
| } |
| { |
| // If both the ALL or DISTINCT keywords are missing, DISTINCT is implicit |
| <MULTISET> |
| ( |
| <UNION> |
| [ |
| <ALL> |
| | <DISTINCT> { return SqlStdOperatorTable.MULTISET_UNION_DISTINCT; } |
| ] |
| { return SqlStdOperatorTable.MULTISET_UNION; } |
| | |
| <INTERSECT> |
| [ |
| <ALL> |
| | <DISTINCT> { return SqlStdOperatorTable.MULTISET_INTERSECT_DISTINCT; } |
| ] |
| { return SqlStdOperatorTable.MULTISET_INTERSECT; } |
| | |
| <EXCEPT> |
| [ |
| <ALL> |
| | <DISTINCT> { return SqlStdOperatorTable.MULTISET_EXCEPT_DISTINCT; } |
| ] |
| { return SqlStdOperatorTable.MULTISET_EXCEPT; } |
| ) |
| } |
| |
| /** |
| * Parses a binary row operator like AND. |
| */ |
| SqlBinaryOperator BinaryRowOperator() : |
| { |
| SqlBinaryOperator op; |
| } |
| { |
| // <IN> is handled as a special case |
| <EQ> { return SqlStdOperatorTable.EQUALS; } |
| | <GT> { return SqlStdOperatorTable.GREATER_THAN; } |
| | <LT> { return SqlStdOperatorTable.LESS_THAN; } |
| | <LE> { return SqlStdOperatorTable.LESS_THAN_OR_EQUAL; } |
| | <GE> { return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL; } |
| | <NE> { return SqlStdOperatorTable.NOT_EQUALS; } |
| | <NE2> { |
| if (!this.conformance.isBangEqualAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.bangEqualNotAllowed()); |
| } |
| return SqlStdOperatorTable.NOT_EQUALS; |
| } |
| | <PLUS> { return SqlStdOperatorTable.PLUS; } |
| | <MINUS> { return SqlStdOperatorTable.MINUS; } |
| | <STAR> { return SqlStdOperatorTable.MULTIPLY; } |
| | <SLASH> { return SqlStdOperatorTable.DIVIDE; } |
| | <PERCENT_REMAINDER> { |
| if (!this.conformance.isPercentRemainderAllowed()) { |
| throw SqlUtil.newContextException(getPos(), RESOURCE.percentRemainderNotAllowed()); |
| } |
| return SqlStdOperatorTable.PERCENT_REMAINDER; |
| } |
| | <CONCAT> { return SqlStdOperatorTable.CONCAT; } |
| | <AND> { return SqlStdOperatorTable.AND; } |
| | <OR> { return SqlStdOperatorTable.OR; } |
| | LOOKAHEAD(2) <IS> <DISTINCT> <FROM> { return SqlStdOperatorTable.IS_DISTINCT_FROM; } |
| | <IS> <NOT> <DISTINCT> <FROM> { return SqlStdOperatorTable.IS_NOT_DISTINCT_FROM; } |
| | <MEMBER> <OF> { return SqlStdOperatorTable.MEMBER_OF; } |
| | LOOKAHEAD(2) <SUBMULTISET> <OF> { return SqlStdOperatorTable.SUBMULTISET_OF; } |
| | <NOT> <SUBMULTISET> <OF> { return SqlStdOperatorTable.NOT_SUBMULTISET_OF; } |
| | <CONTAINS> { return SqlStdOperatorTable.CONTAINS; } |
| | <OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; } |
| | <EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; } |
| | <PRECEDES> { return SqlStdOperatorTable.PRECEDES; } |
| | <SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; } |
| | LOOKAHEAD(2) <IMMEDIATELY> <PRECEDES> { return SqlStdOperatorTable.IMMEDIATELY_PRECEDES; } |
| | <IMMEDIATELY> <SUCCEEDS> { return SqlStdOperatorTable.IMMEDIATELY_SUCCEEDS; } |
| | op = BinaryMultisetOperator() { return op; } |
| } |
| |
| /** |
| * Parses a prefix row operator like NOT. |
| */ |
| SqlPrefixOperator PrefixRowOperator() : |
| {} |
| { |
| <PLUS> { return SqlStdOperatorTable.UNARY_PLUS; } |
| | <MINUS> { return SqlStdOperatorTable.UNARY_MINUS; } |
| | <NOT> { return SqlStdOperatorTable.NOT; } |
| | <EXISTS> { return SqlStdOperatorTable.EXISTS; } |
| | <UNIQUE> { return SqlStdOperatorTable.UNIQUE; } |
| } |
| |
| /** |
| * Parses a postfix row operator like IS NOT NULL. |
| */ |
| SqlPostfixOperator PostfixRowOperator() : |
| {} |
| { |
| <IS> |
| ( |
| <A> <SET> { return SqlStdOperatorTable.IS_A_SET; } |
| | |
| <NOT> |
| ( |
| <NULL> { return SqlStdOperatorTable.IS_NOT_NULL; } |
| | <TRUE> { return SqlStdOperatorTable.IS_NOT_TRUE; } |
| | <FALSE> { return SqlStdOperatorTable.IS_NOT_FALSE; } |
| | <UNKNOWN> { return SqlStdOperatorTable.IS_NOT_UNKNOWN; } |
| | <A> <SET> { return SqlStdOperatorTable.IS_NOT_A_SET; } |
| | <EMPTY> { return SqlStdOperatorTable.IS_NOT_EMPTY; } |
| | LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; } |
| | LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_NOT_JSON_OBJECT; } |
| | LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_NOT_JSON_ARRAY; } |
| | LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_NOT_JSON_SCALAR; } |
| | <JSON> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; } |
| ) |
| | |
| ( |
| <NULL> { return SqlStdOperatorTable.IS_NULL; } |
| | <TRUE> { return SqlStdOperatorTable.IS_TRUE; } |
| | <FALSE> { return SqlStdOperatorTable.IS_FALSE; } |
| | <UNKNOWN> { return SqlStdOperatorTable.IS_UNKNOWN; } |
| | <EMPTY> { return SqlStdOperatorTable.IS_EMPTY; } |
| | LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_JSON_VALUE; } |
| | LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_JSON_OBJECT; } |
| | LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_JSON_ARRAY; } |
| | LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_JSON_SCALAR; } |
| | <JSON> { return SqlStdOperatorTable.IS_JSON_VALUE; } |
| ) |
| ) |
| | |
| <FORMAT> |
| ( |
| JsonRepresentation() { |
| return SqlStdOperatorTable.JSON_VALUE_EXPRESSION; |
| } |
| ) |
| } |
| |
| |
| /* KEYWORDS: anything in this list is a reserved word unless it appears |
| in the NonReservedKeyWord() production. */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < A: "A" > |
| | < ABS: "ABS" > |
| | < ABSENT: "ABSENT" > |
| | < ABSOLUTE: "ABSOLUTE" > |
| | < ACTION: "ACTION" > |
| | < ADA: "ADA" > |
| | < ADD: "ADD" > |
| | < ADMIN: "ADMIN" > |
| | < AFTER: "AFTER" > |
| | < ALL: "ALL" > |
| | < ALLOCATE: "ALLOCATE" > |
| | < ALLOW: "ALLOW" > |
| | < ALTER: "ALTER" > |
| | < ALWAYS: "ALWAYS" > |
| | < AND: "AND" > |
| | < ANY: "ANY" > |
| | < APPLY: "APPLY" > |
| | < ARE: "ARE" > |
| | < ARRAY: "ARRAY" > |
| | < ARRAY_AGG: "ARRAY_AGG" > |
| | < ARRAY_CONCAT_AGG: "ARRAY_CONCAT_AGG" > |
| | < ARRAY_MAX_CARDINALITY: "ARRAY_MAX_CARDINALITY" > |
| | < AS: "AS" > |
| | < ASC: "ASC" > |
| | < ASENSITIVE: "ASENSITIVE" > |
| | < ASSERTION: "ASSERTION" > |
| | < ASSIGNMENT: "ASSIGNMENT" > |
| | < ASYMMETRIC: "ASYMMETRIC" > |
| | < AT: "AT" > |
| | < ATOMIC: "ATOMIC" > |
| | < ATTRIBUTE: "ATTRIBUTE" > |
| | < ATTRIBUTES: "ATTRIBUTES" > |
| | < AUTHORIZATION: "AUTHORIZATION" > |
| | < AVG: "AVG" > |
| | < BEFORE: "BEFORE" > |
| | < BEGIN: "BEGIN" > |
| | < BEGIN_FRAME: "BEGIN_FRAME" > |
| | < BEGIN_PARTITION: "BEGIN_PARTITION" > |
| | < BERNOULLI: "BERNOULLI" > |
| | < BETWEEN: "BETWEEN" > |
| | < BIGINT: "BIGINT" > |
| | < BINARY: "BINARY" > |
| | < BIT: "BIT" > |
| | < BLOB: "BLOB" > |
| | < BOOLEAN: "BOOLEAN" > |
| | < BOTH: "BOTH" > |
| | < BREADTH: "BREADTH" > |
| | < BY: "BY" > |
| | < C: "C" > |
| | < CALL: "CALL" > |
| | < CALLED: "CALLED" > |
| | < CARDINALITY: "CARDINALITY" > |
| | < CASCADE: "CASCADE" > |
| | < CASCADED: "CASCADED" > |
| | < CASE: "CASE" > |
| | < CAST: "CAST" > |
| | < CATALOG: "CATALOG" > |
| | < CATALOG_NAME: "CATALOG_NAME" > |
| | < CEIL: "CEIL" > |
| | < CEILING: "CEILING" > |
| | < CENTURY: "CENTURY" > |
| | < CHAIN: "CHAIN" > |
| | < CHAR: "CHAR" > |
| | < CHAR_LENGTH: "CHAR_LENGTH" > |
| | < CHARACTER: "CHARACTER" > |
| | < CHARACTER_LENGTH: "CHARACTER_LENGTH" > |
| | < CHARACTER_SET_CATALOG: "CHARACTER_SET_CATALOG" > |
| | < CHARACTER_SET_NAME: "CHARACTER_SET_NAME" > |
| | < CHARACTER_SET_SCHEMA: "CHARACTER_SET_SCHEMA" > |
| | < CHARACTERISTICS: "CHARACTERISTICS" > |
| | < CHARACTERS: "CHARACTERS" > |
| | < CHECK: "CHECK" > |
| | < CLASSIFIER: "CLASSIFIER" > |
| | < CLASS_ORIGIN: "CLASS_ORIGIN" > |
| | < CLOB: "CLOB" > |
| | < CLOSE: "CLOSE" > |
| | < COALESCE: "COALESCE" > |
| | < COBOL: "COBOL" > |
| | < COLLATE: "COLLATE" > |
| | < COLLATION: "COLLATION" > |
| | < COLLATION_CATALOG: "COLLATION_CATALOG" > |
| | < COLLATION_NAME: "COLLATION_NAME" > |
| | < COLLATION_SCHEMA: "COLLATION_SCHEMA" > |
| | < COLLECT: "COLLECT" > |
| | < COLUMN: "COLUMN" > |
| | < COLUMN_NAME: "COLUMN_NAME" > |
| | < COMMAND_FUNCTION: "COMMAND_FUNCTION" > |
| | < COMMAND_FUNCTION_CODE: "COMMAND_FUNCTION_CODE" > |
| | < COMMIT: "COMMIT" > |
| | < COMMITTED: "COMMITTED" > |
| | < CONDITION: "CONDITION" > |
| | < CONDITIONAL: "CONDITIONAL" > |
| | < CONDITION_NUMBER: "CONDITION_NUMBER" > |
| | < CONNECT: "CONNECT" > |
| | < CONNECTION: "CONNECTION" > |
| | < CONNECTION_NAME: "CONNECTION_NAME" > |
| | < CONSTRAINT: "CONSTRAINT" > |
| | < CONSTRAINT_CATALOG: "CONSTRAINT_CATALOG" > |
| | < CONSTRAINT_NAME: "CONSTRAINT_NAME" > |
| | < CONSTRAINT_SCHEMA: "CONSTRAINT_SCHEMA" > |
| | < CONSTRAINTS: "CONSTRAINTS" > |
| | < CONSTRUCTOR: "CONSTRUCTOR" > |
| | < CONTAINS: "CONTAINS" > |
| | < CONTINUE: "CONTINUE" > |
| | < CONVERT: "CONVERT" > |
| | < CORR: "CORR" > |
| | < CORRESPONDING: "CORRESPONDING" > |
| | < COUNT: "COUNT" > |
| | < COVAR_POP: "COVAR_POP" > |
| | < COVAR_SAMP: "COVAR_SAMP" > |
| | < CREATE: "CREATE" > |
| | < CROSS: "CROSS" > |
| | < CUBE: "CUBE" > |
| | < CUME_DIST: "CUME_DIST" > |
| | < CURRENT: "CURRENT" > |
| | < CURRENT_CATALOG: "CURRENT_CATALOG" > |
| | < CURRENT_DATE: "CURRENT_DATE" > |
| | < CURRENT_DEFAULT_TRANSFORM_GROUP: "CURRENT_DEFAULT_TRANSFORM_GROUP" > |
| | < CURRENT_PATH: "CURRENT_PATH" > |
| | < CURRENT_ROLE: "CURRENT_ROLE" > |
| | < CURRENT_ROW: "CURRENT_ROW" > |
| | < CURRENT_SCHEMA: "CURRENT_SCHEMA" > |
| | < CURRENT_TIME: "CURRENT_TIME" > |
| | < CURRENT_TIMESTAMP: "CURRENT_TIMESTAMP" > |
| | < CURRENT_TRANSFORM_GROUP_FOR_TYPE: "CURRENT_TRANSFORM_GROUP_FOR_TYPE" > |
| | < CURRENT_USER: "CURRENT_USER" > |
| | < CURSOR: "CURSOR" > |
| | < CURSOR_NAME: "CURSOR_NAME" > |
| | < CYCLE: "CYCLE" > |
| | < DATA: "DATA" > |
| | < DATABASE: "DATABASE" > |
| | < DATE: "DATE" > |
| | < DATE_TRUNC: "DATE_TRUNC" > |
| | < DATETIME: "DATETIME" > |
| | < DATETIME_INTERVAL_CODE: "DATETIME_INTERVAL_CODE" > |
| | < DATETIME_INTERVAL_PRECISION: "DATETIME_INTERVAL_PRECISION" > |
| | < DAY: "DAY" > |
| | < DAYOFWEEK: "DAYOFWEEK" > |
| | < DAYOFYEAR: "DAYOFYEAR" > |
| | < DAYS: "DAYS" > |
| | < DEALLOCATE: "DEALLOCATE" > |
| | < DEC: "DEC" > |
| | < DECADE: "DECADE" > |
| | < DECIMAL: "DECIMAL" > |
| | < DECLARE: "DECLARE" > |
| | < DEFAULT_: "DEFAULT" > |
| | < DEFAULTS: "DEFAULTS" > |
| | < DEFERRABLE: "DEFERRABLE" > |
| | < DEFERRED: "DEFERRED" > |
| | < DEFINE: "DEFINE" > |
| | < DEFINED: "DEFINED" > |
| | < DEFINER: "DEFINER" > |
| | < DEGREE: "DEGREE" > |
| | < DELETE: "DELETE" > { beforeTableName(); } |
| | < DENSE_RANK: "DENSE_RANK" > |
| | < DEPTH: "DEPTH" > |
| | < DEREF: "DEREF" > |
| | < DERIVED: "DERIVED" > |
| | < DESC: "DESC" > |
| | < DESCRIBE: "DESCRIBE" > { beforeTableName(); } |
| | < DESCRIPTION: "DESCRIPTION" > |
| | < DESCRIPTOR: "DESCRIPTOR" > |
| | < DETERMINISTIC: "DETERMINISTIC" > |
| | < DIAGNOSTICS: "DIAGNOSTICS" > |
| | < DISALLOW: "DISALLOW" > |
| | < DISCONNECT: "DISCONNECT" > |
| | < DISPATCH: "DISPATCH" > |
| | < DISTINCT: "DISTINCT" > |
| | < DOMAIN: "DOMAIN" > |
| | < DOT_FORMAT: "DOT" > |
| | < DOUBLE: "DOUBLE" > |
| | < DOW: "DOW" > |
| | < DOY: "DOY" > |
| | < DROP: "DROP" > |
| | < DYNAMIC: "DYNAMIC" > |
| | < DYNAMIC_FUNCTION: "DYNAMIC_FUNCTION" > |
| | < DYNAMIC_FUNCTION_CODE: "DYNAMIC_FUNCTION_CODE" > |
| | < EACH: "EACH" > |
| | < ELEMENT: "ELEMENT" > |
| | < ELSE: "ELSE" > |
| | < EMPTY: "EMPTY" > |
| | < ENCODING: "ENCODING"> |
| | < END: "END" > |
| | < END_EXEC: "END-EXEC" > |
| | < END_FRAME: "END_FRAME" > |
| | < END_PARTITION: "END_PARTITION" > |
| | < EPOCH: "EPOCH" > |
| | < EQUALS: "EQUALS" > |
| | < ERROR: "ERROR" > |
| | < ESCAPE: "ESCAPE" > |
| | < EVERY: "EVERY" > |
| | < EXCEPT: "EXCEPT" > |
| | < EXCEPTION: "EXCEPTION" > |
| | < EXCLUDE: "EXCLUDE" > |
| | < EXCLUDING: "EXCLUDING" > |
| | < EXEC: "EXEC" > |
| | < EXECUTE: "EXECUTE" > |
| | < EXISTS: "EXISTS" > |
| | < EXP: "EXP" > |
| | < EXPLAIN: "EXPLAIN" > |
| | < EXTEND: "EXTEND" > |
| | < EXTERNAL: "EXTERNAL" > |
| | < EXTRACT: "EXTRACT" > |
| | < FALSE: "FALSE" > |
| | < FETCH: "FETCH" > |
| | < FILTER: "FILTER" > |
| | < FINAL: "FINAL" > |
| | < FIRST: "FIRST" > |
| | < FIRST_VALUE: "FIRST_VALUE"> |
| | < FLOAT: "FLOAT" > |
| | < FLOOR: "FLOOR" > |
| | < FOLLOWING: "FOLLOWING" > |
| | < FOR: "FOR" > |
| | < FORMAT: "FORMAT" > |
| | < FOREIGN: "FOREIGN" > |
| | < FORTRAN: "FORTRAN" > |
| | < FOUND: "FOUND" > |
| | < FRAC_SECOND: "FRAC_SECOND" > |
| | < FRAME_ROW: "FRAME_ROW" > |
| | < FREE: "FREE" > |
| | < FRIDAY: "FRIDAY" > |
| | < FROM: "FROM" > { beforeTableName(); } |
| | < FULL: "FULL" > |
| | < FUNCTION: "FUNCTION" > |
| | < FUSION: "FUSION" > |
| | < G: "G" > |
| | < GENERAL: "GENERAL" > |
| | < GENERATED: "GENERATED" > |
| | < GEOMETRY: "GEOMETRY" > |
| | < GET: "GET" > |
| | < GLOBAL: "GLOBAL" > |
| | < GO: "GO" > |
| | < GOTO: "GOTO" > |
| | < GRANT: "GRANT" > |
| | < GRANTED: "GRANTED" > |
| | < GROUP: "GROUP" > |
| | < GROUP_CONCAT: "GROUP_CONCAT" > |
| | < GROUPING: "GROUPING" > |
| | < GROUPS: "GROUPS" > |
| | < HAVING: "HAVING" > |
| | < HIERARCHY: "HIERARCHY" > |
| | < HOLD: "HOLD" > |
| | < HOP: "HOP" > |
| | < HOUR: "HOUR" > |
| | < HOURS: "HOURS" > |
| | < IDENTITY: "IDENTITY" > |
| | < IGNORE: "IGNORE" > |
| | < ILIKE: "ILIKE" > |
| | < IMMEDIATE: "IMMEDIATE" > |
| | < IMMEDIATELY: "IMMEDIATELY" > |
| | < IMPLEMENTATION: "IMPLEMENTATION" > |
| | < IMPORT: "IMPORT" > |
| | < IN: "IN" > |
| | < INCLUDE: "INCLUDE" > |
| | < INCLUDING: "INCLUDING" > |
| | < INCREMENT: "INCREMENT" > |
| | < INDICATOR: "INDICATOR" > |
| | < INITIAL: "INITIAL" > |
| | < INITIALLY: "INITIALLY" > |
| | < INNER: "INNER" > |
| | < INOUT: "INOUT" > |
| | < INPUT: "INPUT" > |
| | < INSENSITIVE: "INSENSITIVE" > |
| | < INSERT: "INSERT" > { beforeTableName(); } |
| | < INSTANCE: "INSTANCE" > |
| | < INSTANTIABLE: "INSTANTIABLE" > |
| | < INT: "INT" > |
| | < INTEGER: "INTEGER" > |
| | < INTERSECT: "INTERSECT" > |
| | < INTERSECTION: "INTERSECTION" > |
| | < INTERVAL: "INTERVAL" > |
| | < INTO: "INTO" > |
| | < INVOKER: "INVOKER" > |
| | < IS: "IS" > |
| | < ISODOW: "ISODOW" > |
| | < ISOYEAR: "ISOYEAR" > |
| | < ISOLATION: "ISOLATION" > |
| | < JAVA: "JAVA" > |
| | < JOIN: "JOIN" > { beforeTableName(); } |
| | < JSON: "JSON" > |
| | < JSON_ARRAY: "JSON_ARRAY"> |
| | < JSON_ARRAYAGG: "JSON_ARRAYAGG"> |
| | < JSON_EXISTS: "JSON_EXISTS" > |
| | < JSON_OBJECT: "JSON_OBJECT"> |
| | < JSON_OBJECTAGG: "JSON_OBJECTAGG"> |
| | < JSON_QUERY: "JSON_QUERY" > |
| | < JSON_VALUE: "JSON_VALUE" > |
| | < K: "K" > |
| | < KEY: "KEY" > |
| | < KEY_MEMBER: "KEY_MEMBER" > |
| | < KEY_TYPE: "KEY_TYPE" > |
| | < LABEL: "LABEL" > |
| | < LAG: "LAG" > |
| | < LANGUAGE: "LANGUAGE" > |
| | < LARGE: "LARGE" > |
| | < LAST: "LAST" > |
| | < LAST_VALUE: "LAST_VALUE" > |
| | < LATERAL: "LATERAL" > |
| | < LEAD: "LEAD" > |
| | < LEADING: "LEADING" > |
| | < LEFT: "LEFT" > |
| | < LENGTH: "LENGTH" > |
| | < LEVEL: "LEVEL" > |
| | < LIBRARY: "LIBRARY" > |
| | < LIKE: "LIKE" > |
| | < LIKE_REGEX: "LIKE_REGEX" > |
| | < LIMIT: "LIMIT" > |
| | < LN: "LN" > |
| | < LOCAL: "LOCAL" > |
| | < LOCALTIME: "LOCALTIME" > |
| | < LOCALTIMESTAMP: "LOCALTIMESTAMP" > |
| | < LOCATOR: "LOCATOR" > |
| | < LOWER: "LOWER" > |
| | < M: "M" > |
| | < MAP: "MAP" > |
| | < MATCH: "MATCH" > |
| | < MATCHED: "MATCHED" > |
| | < MATCHES: "MATCHES" > |
| | < MATCH_NUMBER: "MATCH_NUMBER"> |
| | < MATCH_RECOGNIZE: "MATCH_RECOGNIZE"> |
| | < MAX: "MAX" > |
| | < MAXVALUE: "MAXVALUE" > |
| | < MEASURES: "MEASURES" > |
| | < MEMBER: "MEMBER" > |
| | < MERGE: "MERGE" > { beforeTableName(); } |
| | < MESSAGE_LENGTH: "MESSAGE_LENGTH" > |
| | < MESSAGE_OCTET_LENGTH: "MESSAGE_OCTET_LENGTH" > |
| | < MESSAGE_TEXT: "MESSAGE_TEXT" > |
| | < METHOD: "METHOD" > |
| | < MICROSECOND: "MICROSECOND" > |
| | < MILLISECOND: "MILLISECOND" > |
| | < MILLENNIUM: "MILLENNIUM" > |
| | < MIN: "MIN" > |
| | < MINUTE: "MINUTE" > |
| | < MINUTES: "MINUTES" > |
| | < MINVALUE: "MINVALUE" > |
| | < MOD: "MOD" > |
| | < MODIFIES: "MODIFIES" > |
| | < MODULE: "MODULE" > |
| | < MONDAY: "MONDAY" > |
| | < MONTH: "MONTH" > |
| | < MONTHS: "MONTHS" > |
| | < MORE_: "MORE" > |
| | < MULTISET: "MULTISET" > |
| | < MUMPS: "MUMPS" > |
| | < NAME: "NAME" > |
| | < NAMES: "NAMES" > |
| | < NANOSECOND: "NANOSECOND" > |
| | < NATIONAL: "NATIONAL" > |
| | < NATURAL: "NATURAL" > |
| | < NCHAR: "NCHAR" > |
| | < NCLOB: "NCLOB" > |
| | < NESTING: "NESTING" > |
| | < NEW: "NEW" > |
| | < NEXT: "NEXT" > |
| | < NO: "NO" > |
| | < NONE: "NONE" > |
| | < NORMALIZE: "NORMALIZE" > |
| | < NORMALIZED: "NORMALIZED" > |
| | < NOT: "NOT" > |
| | < NTH_VALUE: "NTH_VALUE" > |
| | < NTILE: "NTILE" > |
| | < NULL: "NULL" > |
| | < NULLABLE: "NULLABLE" > |
| | < NULLIF: "NULLIF" > |
| | < NULLS: "NULLS" > |
| | < NUMBER: "NUMBER" > |
| | < NUMERIC: "NUMERIC" > |
| | < OBJECT: "OBJECT" > |
| | < OCCURRENCES_REGEX: "OCCURRENCES_REGEX" > |
| | < OCTET_LENGTH: "OCTET_LENGTH" > |
| | < OCTETS: "OCTETS" > |
| | < OF: "OF" > |
| | < OFFSET: "OFFSET" > |
| | < OLD: "OLD" > |
| | < OMIT: "OMIT" > |
| | < ON: "ON" > |
| | < ONE: "ONE" > |
| | < ONLY: "ONLY" > |
| | < OPEN: "OPEN" > |
| | < OPTION: "OPTION" > |
| | < OPTIONS: "OPTIONS" > |
| | < OR: "OR" > |
| | < ORDER: "ORDER" > |
| | < ORDERING: "ORDERING" > |
| | < ORDINALITY: "ORDINALITY" > |
| | < OTHERS: "OTHERS" > |
| | < OUT: "OUT" > |
| | < OUTER: "OUTER" > |
| | < OUTPUT: "OUTPUT" > |
| | < OVER: "OVER" > |
| | < OVERLAPS: "OVERLAPS" > |
| | < OVERLAY: "OVERLAY" > |
| | < OVERRIDING: "OVERRIDING" > |
| | < PAD: "PAD" > |
| | < PARAMETER: "PARAMETER" > |
| | < PARAMETER_MODE: "PARAMETER_MODE" > |
| | < PARAMETER_NAME: "PARAMETER_NAME" > |
| | < PARAMETER_ORDINAL_POSITION: "PARAMETER_ORDINAL_POSITION" > |
| | < PARAMETER_SPECIFIC_CATALOG: "PARAMETER_SPECIFIC_CATALOG" > |
| | < PARAMETER_SPECIFIC_NAME: "PARAMETER_SPECIFIC_NAME" > |
| | < PARAMETER_SPECIFIC_SCHEMA: "PARAMETER_SPECIFIC_SCHEMA" > |
| | < PARTIAL: "PARTIAL" > |
| | < PARTITION: "PARTITION" > |
| | < PASCAL: "PASCAL" > |
| | < PASSING: "PASSING" > |
| | < PASSTHROUGH: "PASSTHROUGH" > |
| | < PAST: "PAST" > |
| | < PATH: "PATH" > |
| | < PATTERN: "PATTERN" > |
| | < PER: "PER" > |
| | < PERCENT: "PERCENT" > |
| | < PERCENTILE_CONT: "PERCENTILE_CONT" > |
| | < PERCENTILE_DISC: "PERCENTILE_DISC" > |
| | < PERCENT_RANK: "PERCENT_RANK" > |
| | < PERIOD: "PERIOD" > |
| | < PERMUTE: "PERMUTE" > |
| | < PIVOT: "PIVOT" > |
| | < PLACING: "PLACING" > |
| | < PLAN: "PLAN" > |
| | < PLI: "PLI" > |
| | < PORTION: "PORTION" > |
| | < POSITION: "POSITION" > |
| | < POSITION_REGEX: "POSITION_REGEX" > |
| | < POWER: "POWER" > |
| | < PRECEDES: "PRECEDES" > |
| | < PRECEDING: "PRECEDING" > |
| | < PRECISION: "PRECISION" > |
| | < PREPARE: "PREPARE" > |
| | < PRESERVE: "PRESERVE" > |
| | < PREV: "PREV" > |
| | < PRIMARY: "PRIMARY" > |
| | < PRIOR: "PRIOR" > |
| | < PRIVILEGES: "PRIVILEGES" > |
| | < PROCEDURE: "PROCEDURE" > |
| | < PUBLIC: "PUBLIC" > |
| | < QUALIFY: "QUALIFY" > |
| | < QUARTER: "QUARTER" > |
| | < QUARTERS: "QUARTERS" > |
| | < RANGE: "RANGE" > |
| | < RANK: "RANK" > |
| | < READ: "READ" > |
| | < READS: "READS" > |
| | < REAL: "REAL" > |
| | < RECURSIVE: "RECURSIVE" > |
| | < REF: "REF" > |
| | < REFERENCES: "REFERENCES" > |
| | < REFERENCING: "REFERENCING" > |
| | < REGR_AVGX: "REGR_AVGX" > |
| | < REGR_AVGY: "REGR_AVGY" > |
| | < REGR_COUNT: "REGR_COUNT" > |
| | < REGR_INTERCEPT: "REGR_INTERCEPT" > |
| | < REGR_R2: "REGR_R2" > |
| | < REGR_SLOPE: "REGR_SLOPE" > |
| | < REGR_SXX: "REGR_SXX" > |
| | < REGR_SXY: "REGR_SXY" > |
| | < REGR_SYY: "REGR_SYY" > |
| | < RELATIVE: "RELATIVE" > |
| | < RELEASE: "RELEASE" > |
| | < REPEATABLE: "REPEATABLE" > |
| | < REPLACE: "REPLACE" > |
| | < RESET: "RESET" > |
| | < RESPECT: "RESPECT" > |
| | < RESTART: "RESTART" > |
| | < RESTRICT: "RESTRICT" > |
| | < RESULT: "RESULT" > |
| | < RETURN: "RETURN" > |
| | < RETURNED_CARDINALITY: "RETURNED_CARDINALITY" > |
| | < RETURNED_LENGTH: "RETURNED_LENGTH" > |
| | < RETURNED_OCTET_LENGTH: "RETURNED_OCTET_LENGTH" > |
| | < RETURNED_SQLSTATE: "RETURNED_SQLSTATE" > |
| | < RETURNING: "RETURNING" > |
| | < RETURNS: "RETURNS" > |
| | < REVOKE: "REVOKE" > |
| | < RIGHT: "RIGHT" > |
| | < RLIKE: "RLIKE" > |
| | < ROLE: "ROLE" > |
| | < ROLLBACK: "ROLLBACK" > |
| | < ROLLUP: "ROLLUP" > |
| | < ROUTINE: "ROUTINE" > |
| | < ROUTINE_CATALOG: "ROUTINE_CATALOG" > |
| | < ROUTINE_NAME: "ROUTINE_NAME" > |
| | < ROUTINE_SCHEMA: "ROUTINE_SCHEMA" > |
| | < ROW: "ROW" > |
| | < ROW_COUNT: "ROW_COUNT" > |
| | < ROW_NUMBER: "ROW_NUMBER" > |
| | < ROWS: "ROWS" > |
| | < RUNNING: "RUNNING" > |
| | < SAFE_CAST: "SAFE_CAST" > |
| | < SATURDAY: "SATURDAY" > |
| | < SAVEPOINT: "SAVEPOINT" > |
| | < SCALAR: "SCALAR" > |
| | < SCALE: "SCALE" > |
| | < SCHEMA: "SCHEMA" > |
| | < SCHEMA_NAME: "SCHEMA_NAME" > |
| | < SCOPE: "SCOPE" > |
| | < SCOPE_CATALOGS: "SCOPE_CATALOGS" > |
| | < SCOPE_NAME: "SCOPE_NAME" > |
| | < SCOPE_SCHEMA: "SCOPE_SCHEMA" > |
| | < SCROLL: "SCROLL" > |
| | < SEARCH: "SEARCH" > |
| | < SECOND: "SECOND" > |
| | < SECONDS: "SECONDS" > |
| | < SECTION: "SECTION" > |
| | < SECURITY: "SECURITY" > |
| | < SEEK: "SEEK" > |
| | < SELECT: "SELECT" > { afterTableName(); } |
| | < SELF: "SELF" > |
| | < SENSITIVE: "SENSITIVE" > |
| | < SEPARATOR: "SEPARATOR" > |
| | < SEQUENCE: "SEQUENCE" > |
| | < SERIALIZABLE: "SERIALIZABLE" > |
| | < SERVER: "SERVER" > |
| | < SERVER_NAME: "SERVER_NAME" > |
| | < SESSION: "SESSION" > |
| | < SESSION_USER: "SESSION_USER" > |
| | < SET: "SET" > { afterTableName(); } |
| | < SETS: "SETS" > |
| | < SET_MINUS: "MINUS"> |
| | < SHOW: "SHOW" > |
| | < SIMILAR: "SIMILAR" > |
| | < SIMPLE: "SIMPLE" > |
| | < SIZE: "SIZE" > |
| | < SKIP_: "SKIP" > |
| | < SMALLINT: "SMALLINT" > |
| | < SOME: "SOME" > |
| | < SOURCE: "SOURCE" > |
| | < SPACE: "SPACE" > |
| | < SPECIFIC: "SPECIFIC" > |
| | < SPECIFIC_NAME: "SPECIFIC_NAME" > |
| | < SPECIFICTYPE: "SPECIFICTYPE" > |
| | < SQL: "SQL" > |
| | < SQLEXCEPTION: "SQLEXCEPTION" > |
| | < SQLSTATE: "SQLSTATE" > |
| | < SQLWARNING: "SQLWARNING" > |
| | < SQL_BIGINT: "SQL_BIGINT" > |
| | < SQL_BINARY: "SQL_BINARY" > |
| | < SQL_BIT: "SQL_BIT" > |
| | < SQL_BLOB: "SQL_BLOB" > |
| | < SQL_BOOLEAN: "SQL_BOOLEAN" > |
| | < SQL_CHAR: "SQL_CHAR" > |
| | < SQL_CLOB: "SQL_CLOB" > |
| | < SQL_DATE: "SQL_DATE" > |
| | < SQL_DECIMAL: "SQL_DECIMAL" > |
| | < SQL_DOUBLE: "SQL_DOUBLE" > |
| | < SQL_FLOAT: "SQL_FLOAT" > |
| | < SQL_INTEGER: "SQL_INTEGER" > |
| | < SQL_INTERVAL_DAY: "SQL_INTERVAL_DAY" > |
| | < SQL_INTERVAL_DAY_TO_HOUR: "SQL_INTERVAL_DAY_TO_HOUR" > |
| | < SQL_INTERVAL_DAY_TO_MINUTE: "SQL_INTERVAL_DAY_TO_MINUTE" > |
| | < SQL_INTERVAL_DAY_TO_SECOND: "SQL_INTERVAL_DAY_TO_SECOND" > |
| | < SQL_INTERVAL_HOUR: "SQL_INTERVAL_HOUR" > |
| | < SQL_INTERVAL_HOUR_TO_MINUTE: "SQL_INTERVAL_HOUR_TO_MINUTE" > |
| | < SQL_INTERVAL_HOUR_TO_SECOND: "SQL_INTERVAL_HOUR_TO_SECOND" > |
| | < SQL_INTERVAL_MINUTE: "SQL_INTERVAL_MINUTE" > |
| | < SQL_INTERVAL_MINUTE_TO_SECOND: "SQL_INTERVAL_MINUTE_TO_SECOND" > |
| | < SQL_INTERVAL_MONTH: "SQL_INTERVAL_MONTH" > |
| | < SQL_INTERVAL_SECOND: "SQL_INTERVAL_SECOND" > |
| | < SQL_INTERVAL_YEAR: "SQL_INTERVAL_YEAR" > |
| | < SQL_INTERVAL_YEAR_TO_MONTH: "SQL_INTERVAL_YEAR_TO_MONTH" > |
| | < SQL_LONGVARBINARY: "SQL_LONGVARBINARY" > |
| | < SQL_LONGVARCHAR: "SQL_LONGVARCHAR" > |
| | < SQL_LONGVARNCHAR: "SQL_LONGVARNCHAR" > |
| | < SQL_NCHAR: "SQL_NCHAR" > |
| | < SQL_NCLOB: "SQL_NCLOB" > |
| | < SQL_NUMERIC: "SQL_NUMERIC" > |
| | < SQL_NVARCHAR: "SQL_NVARCHAR" > |
| | < SQL_REAL: "SQL_REAL" > |
| | < SQL_SMALLINT: "SQL_SMALLINT" > |
| | < SQL_TIME: "SQL_TIME" > |
| | < SQL_TIMESTAMP: "SQL_TIMESTAMP" > |
| | < SQL_TINYINT: "SQL_TINYINT" > |
| | < SQL_TSI_DAY: "SQL_TSI_DAY" > |
| | < SQL_TSI_FRAC_SECOND: "SQL_TSI_FRAC_SECOND" > |
| | < SQL_TSI_HOUR: "SQL_TSI_HOUR" > |
| | < SQL_TSI_MICROSECOND: "SQL_TSI_MICROSECOND" > |
| | < SQL_TSI_MINUTE: "SQL_TSI_MINUTE" > |
| | < SQL_TSI_MONTH: "SQL_TSI_MONTH" > |
| | < SQL_TSI_QUARTER: "SQL_TSI_QUARTER" > |
| | < SQL_TSI_SECOND: "SQL_TSI_SECOND" > |
| | < SQL_TSI_WEEK: "SQL_TSI_WEEK" > |
| | < SQL_TSI_YEAR: "SQL_TSI_YEAR" > |
| | < SQL_VARBINARY: "SQL_VARBINARY" > |
| | < SQL_VARCHAR: "SQL_VARCHAR" > |
| | < SQRT: "SQRT" > |
| | < START: "START" > |
| | < STATE: "STATE" > |
| | < STATEMENT: "STATEMENT" > |
| | < STATIC: "STATIC" > |
| | < STDDEV_POP: "STDDEV_POP" > |
| | < STDDEV_SAMP: "STDDEV_SAMP" > |
| | < STREAM: "STREAM" > |
| | < STRING_AGG: "STRING_AGG" > |
| | < STRUCTURE: "STRUCTURE" > |
| | < STYLE: "STYLE" > |
| | < SUBCLASS_ORIGIN: "SUBCLASS_ORIGIN" > |
| | < SUBMULTISET: "SUBMULTISET" > |
| | < SUBSET: "SUBSET" > |
| | < SUBSTITUTE: "SUBSTITUTE" > |
| | < SUBSTRING: "SUBSTRING" > |
| | < SUBSTRING_REGEX: "SUBSTRING_REGEX" > |
| | < SUCCEEDS: "SUCCEEDS" > |
| | < SUM: "SUM" > |
| | < SUNDAY: "SUNDAY" > |
| | < SYMMETRIC: "SYMMETRIC" > |
| | < SYSTEM: "SYSTEM" > |
| | < SYSTEM_TIME: "SYSTEM_TIME" > |
| | < SYSTEM_USER: "SYSTEM_USER" > |
| | < TABLE: "TABLE" > { beforeTableName(); } |
| | < TABLE_NAME: "TABLE_NAME" > |
| | < TABLESAMPLE: "TABLESAMPLE" > |
| | < TEMPORARY: "TEMPORARY" > |
| | < THEN: "THEN" > |
| | < THURSDAY: "THURSDAY" > |
| | < TIES: "TIES" > |
| | < TIME: "TIME" > |
| | < TIME_DIFF: "TIME_DIFF" > |
| | < TIME_TRUNC: "TIME_TRUNC" > |
| | < TIMESTAMP: "TIMESTAMP" > |
| | < TIMESTAMPADD: "TIMESTAMPADD" > |
| | < TIMESTAMPDIFF: "TIMESTAMPDIFF" > |
| | < TIMESTAMP_DIFF: "TIMESTAMP_DIFF" > |
| | < TIMESTAMP_TRUNC: "TIMESTAMP_TRUNC" > |
| | < TIMEZONE_HOUR: "TIMEZONE_HOUR" > |
| | < TIMEZONE_MINUTE: "TIMEZONE_MINUTE" > |
| | < TINYINT: "TINYINT" > |
| | < TO: "TO" > |
| | < TOP_LEVEL_COUNT: "TOP_LEVEL_COUNT" > |
| | < TRAILING: "TRAILING" > |
| | < TRANSACTION: "TRANSACTION" > |
| | < TRANSACTIONS_ACTIVE: "TRANSACTIONS_ACTIVE" > |
| | < TRANSACTIONS_COMMITTED: "TRANSACTIONS_COMMITTED" > |
| | < TRANSACTIONS_ROLLED_BACK: "TRANSACTIONS_ROLLED_BACK" > |
| | < TRANSFORM: "TRANSFORM" > |
| | < TRANSFORMS: "TRANSFORMS" > |
| | < TRANSLATE: "TRANSLATE" > |
| | < TRANSLATE_REGEX: "TRANSLATE_REGEX" > |
| | < TRANSLATION: "TRANSLATION" > |
| | < TREAT: "TREAT" > |
| | < TRIGGER: "TRIGGER" > |
| | < TRIGGER_CATALOG: "TRIGGER_CATALOG" > |
| | < TRIGGER_NAME: "TRIGGER_NAME" > |
| | < TRIGGER_SCHEMA: "TRIGGER_SCHEMA" > |
| | < TRIM: "TRIM" > |
| | < TRIM_ARRAY: "TRIM_ARRAY" > |
| | < TRUE: "TRUE" > |
| | < TRUNCATE: "TRUNCATE" > |
| | < TUESDAY: "TUESDAY" > |
| | < TUMBLE: "TUMBLE" > |
| | < TYPE: "TYPE" > |
| | < UESCAPE: "UESCAPE" > |
| | < UNBOUNDED: "UNBOUNDED" > |
| | < UNCOMMITTED: "UNCOMMITTED" > |
| | < UNCONDITIONAL: "UNCONDITIONAL" > |
| | < UNDER: "UNDER" > |
| | < UNION: "UNION" > |
| | < UNIQUE: "UNIQUE" > |
| | < UNKNOWN: "UNKNOWN" > |
| | < UNPIVOT: "UNPIVOT" > |
| | < UNNAMED: "UNNAMED" > |
| | < UNNEST: "UNNEST" > |
| | < UPDATE: "UPDATE" > { beforeTableName(); } |
| | < UPPER: "UPPER" > |
| | < UPSERT: "UPSERT" > |
| | < USAGE: "USAGE" > |
| | < USER: "USER" > |
| | < USER_DEFINED_TYPE_CATALOG: "USER_DEFINED_TYPE_CATALOG" > |
| | < USER_DEFINED_TYPE_CODE: "USER_DEFINED_TYPE_CODE" > |
| | < USER_DEFINED_TYPE_NAME: "USER_DEFINED_TYPE_NAME" > |
| | < USER_DEFINED_TYPE_SCHEMA: "USER_DEFINED_TYPE_SCHEMA" > |
| | < USING: "USING" > |
| | < UTF8: "UTF8" > |
| | < UTF16: "UTF16" > |
| | < UTF32: "UTF32" > |
| | < VALUE: "VALUE" > |
| | < VALUES: "VALUES" > { afterTableName(); } |
| | < VALUE_OF: "VALUE_OF" > |
| | < VAR_POP: "VAR_POP" > |
| | < VAR_SAMP: "VAR_SAMP" > |
| | < VARBINARY: "VARBINARY" > |
| | < VARCHAR: "VARCHAR" > |
| | < VARYING: "VARYING" > |
| | < VERSION: "VERSION" > |
| | < VERSIONING: "VERSIONING" > |
| | < VIEW: "VIEW" > |
| | < WEDNESDAY: "WEDNESDAY" > |
| | < WEEK: "WEEK" > |
| | < WEEKS: "WEEKS" > |
| | < WHEN: "WHEN" > |
| | < WHENEVER: "WHENEVER" > |
| | < WHERE: "WHERE" > |
| | < WIDTH_BUCKET: "WIDTH_BUCKET" > |
| | < WINDOW: "WINDOW" > |
| | < WITH: "WITH" > |
| | < WITHIN: "WITHIN" > |
| | < WITHOUT: "WITHOUT" > |
| | < WORK: "WORK" > |
| | < WRAPPER: "WRAPPER" > |
| | < WRITE: "WRITE" > |
| | < XML: "XML" > |
| | < YEAR: "YEAR" > |
| | < YEARS: "YEARS" > |
| | < ZONE: "ZONE" > |
| <#-- additional parser keywords are included here --> |
| <#list (parser.keywords!default.parser.keywords) as keyword> |
| | < ${keyword}: "${keyword}" > |
| </#list> |
| } |
| |
| /** |
| * Parses a non-reserved keyword for use as an identifier. |
| * |
| * <p>The method is broken up into several sub-methods; without this |
| * decomposition, parsers such as Babel with more than ~1,000 non-reserved |
| * keywords would generate such deeply nested 'if' statements that javac would |
| * fail with a {@link StackOverflowError}. |
| * |
| * <p>The list is generated from the FMPP config data. To add or remove |
| * keywords, modify config.fmpp. For parsers except Babel, make sure that |
| * keywords are not reserved by the SQL standard. |
| * |
| * @see Glossary#SQL2003 SQL:2003 Part 2 Section 5.2 |
| */ |
| String NonReservedKeyWord() : |
| { |
| } |
| { |
| ( |
| NonReservedKeyWord0of3() |
| | NonReservedKeyWord1of3() |
| | NonReservedKeyWord2of3() |
| ) |
| { |
| return unquotedIdentifier(); |
| } |
| } |
| |
| /** @see #NonReservedKeyWord */ |
| void NonReservedKeyWord0of3() : |
| { |
| } |
| { |
| ( |
| <#list (parser.nonReservedKeywords!default.parser.nonReservedKeywords) + (parser.nonReservedKeywordsToAdd!default.parser.nonReservedKeywordsToAdd) as keyword> |
| <#if keyword?index == 0> |
| <${keyword}> |
| <#elseif keyword?index % 3 == 0> |
| | <${keyword}> |
| </#if> |
| </#list> |
| ) |
| } |
| |
| /** @see #NonReservedKeyWord */ |
| void NonReservedKeyWord1of3() : |
| { |
| } |
| { |
| ( |
| <#list (parser.nonReservedKeywords!default.parser.nonReservedKeywords) + (parser.nonReservedKeywordsToAdd!default.parser.nonReservedKeywordsToAdd) as keyword> |
| <#if keyword?index == 1> |
| <${keyword}> |
| <#elseif keyword?index % 3 == 1> |
| | <${keyword}> |
| </#if> |
| </#list> |
| ) |
| } |
| |
| /** @see #NonReservedKeyWord */ |
| void NonReservedKeyWord2of3() : |
| { |
| } |
| { |
| ( |
| <#list (parser.nonReservedKeywords!default.parser.nonReservedKeywords) + (parser.nonReservedKeywordsToAdd!default.parser.nonReservedKeywordsToAdd) as keyword> |
| <#if keyword?index == 2> |
| <${keyword}> |
| <#elseif keyword?index % 3 == 2> |
| | <${keyword}> |
| </#if> |
| </#list> |
| ) |
| } |
| |
| /* LITERALS */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < UNSIGNED_INTEGER_LITERAL: (["0"-"9"])+ > |
| | |
| < APPROX_NUMERIC_LITERAL: |
| (<UNSIGNED_INTEGER_LITERAL> | <DECIMAL_NUMERIC_LITERAL>) <EXPONENT> > |
| | |
| < DECIMAL_NUMERIC_LITERAL: |
| (["0"-"9"])+(".")?(["0"-"9"])* |
| | "."(["0"-"9"])+ |
| > |
| | |
| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > |
| | |
| < #HEXDIGIT: ["0"-"9","a"-"f","A"-"F"] > |
| | |
| < #WHITESPACE: |
| [ " ","\t","\n","\r","\f" ] |
| > |
| | |
| /* To improve error reporting, we allow all kinds of characters, |
| * not just hexits, in a binary string literal. */ |
| < BINARY_STRING_LITERAL: ["x","X"] <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> > |
| } |
| |
| // All databases except BigQuery support standard single-quoted literals, |
| // which use single-quote as the escape character. |
| <DEFAULT, DQID, BTID> TOKEN : |
| { |
| < QUOTED_STRING: <QUOTE> ( (~["'"]) | ("''"))* <QUOTE> > |
| | |
| < PREFIXED_STRING_LITERAL: ("_" <CHARSETNAME> | "N") <QUOTED_STRING> > |
| | |
| < UNICODE_STRING_LITERAL: "U" "&" <QUOTED_STRING> > |
| | |
| < C_STYLE_ESCAPED_STRING_LITERAL: "E" <QUOTE> ( (~["'", "\\"]) | ("\\" ~[]) | "''")* <QUOTE> > |
| | |
| < #CHARSETNAME: (["a"-"z","A"-"Z","0"-"9"]) |
| (["a"-"z","A"-"Z","0"-"9",":",".","-","_"])* |
| > |
| } |
| |
| // BigQuery supports single- and double-quoted literals with back-slash |
| // as the escape character. |
| <BQID, BQHID> TOKEN : |
| { |
| // BigQuery-style double-quoted string, escaped using backslash |
| < BIG_QUERY_DOUBLE_QUOTED_STRING: |
| <DOUBLE_QUOTE> ( (~["\\", "\""]) | ("\\" ~[]) )* <DOUBLE_QUOTE> |
| > |
| | |
| // BigQuery-style single-quoted string, escaped using backslash |
| < BIG_QUERY_QUOTED_STRING: |
| <QUOTE> ( (~["\\", "'"]) | ("\\" ~[]) )* <QUOTE> |
| > |
| } |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < UNICODE_QUOTED_ESCAPE_CHAR: |
| <QUOTE> |
| (~["0"-"9","a"-"f","A"-"F","+","\""," ","\t","\n","\r","\f"]) |
| <QUOTE> |
| > |
| } |
| |
| /* SEPARATORS */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < LPAREN: "("> |
| | < RPAREN: ")"> |
| <#if (parser.includeBraces!default.parser.includeBraces) > |
| | < LBRACE_D: "{" (" ")* ["d","D"] > |
| | < LBRACE_T: "{" (" ")* ["t","T"] > |
| | < LBRACE_TS: "{" (" ")* ["t","T"] ["s","S"] > |
| | < LBRACE_FN: "{" (" ")* ["f","F"] ["n","N"] > |
| | < LBRACE: "{" > |
| | < RBRACE: "}" > |
| <#else> |
| <#include "/@includes/braces.ftl" /> |
| </#if> |
| | < LBRACKET: "[" > |
| | < RBRACKET: "]" > |
| | < SEMICOLON: ";" > |
| | < DOT: "." > |
| | < COMMA: "," > |
| } |
| |
| /* OPERATORS */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < EQ: "=" > |
| | < GT: ">" > |
| | < LT: "<" > |
| | < HOOK: "?" > |
| | < COLON: ":" > |
| | < LE: "<=" > |
| | < GE: ">=" > |
| | < NE: "<>" > |
| | < NE2: "!=" > |
| | < PLUS: "+" > |
| | < MINUS: "-" > |
| | < STAR: "*" > |
| | < SLASH: "/" > |
| | < PERCENT_REMAINDER: "%" > |
| | < CONCAT: "||" > |
| | < NAMED_ARGUMENT_ASSIGNMENT: "=>" > |
| | < DOUBLE_PERIOD: ".." > |
| | < QUOTE: "'" > |
| | < DOUBLE_QUOTE: "\"" > |
| | < VERTICAL_BAR: "|" > |
| | < CARET: "^" > |
| | < DOLLAR: "$" > |
| <#list (parser.binaryOperatorsTokens!default.parser.binaryOperatorsTokens) as operator> |
| | ${operator} |
| </#list> |
| } |
| |
| |
| /***************************************** |
| * Lexical Descriptions * |
| *****************************************/ |
| |
| TOKEN_MGR_DECLS : { |
| final List<Integer> lexicalStateStack = new ArrayList<Integer>(); |
| |
| void pushState() { |
| lexicalStateStack.add(curLexState); |
| } |
| |
| void popState() { |
| SwitchTo(lexicalStateStack.remove(lexicalStateStack.size() - 1)); |
| } |
| |
| void beforeTableName() { |
| if (curLexState == BQID) { |
| pushState(); |
| SwitchTo(BQHID); |
| } |
| } |
| |
| void afterTableName() { |
| if (curLexState == BQHID) { |
| popState(); |
| } |
| } |
| |
| <#if (parser.includeAdditionalDeclarations!default.parser.includeAdditionalDeclarations)> |
| <#include "/@includes/tokenManagerDeclarations.ftl" /> |
| </#if> |
| } |
| |
| /* |
| Lexical states: |
| |
| DEFAULT: Identifiers are quoted in brackets, e.g. [My Identifier] |
| DQID: Identifiers are double-quoted, e.g. "My Identifier" |
| BTID: Identifiers are enclosed in back-ticks, escaped using back-ticks, |
| e.g. `My ``Quoted`` Identifier` |
| BQID: Identifiers are enclosed in back-ticks, escaped using backslash, |
| e.g. `My \`Quoted\` Identifier`, |
| and with the potential to shift into BQHID in contexts where table |
| names are expected, and thus allow hyphen-separated identifiers as |
| part of table names |
| BQHID: Identifiers are enclosed in back-ticks, escaped using backslash, |
| e.g. `My \`Quoted\` Identifier` |
| and unquoted identifiers may contain hyphens, e.g. foo-bar |
| IN_SINGLE_LINE_COMMENT: |
| IN_FORMAL_COMMENT: |
| IN_MULTI_LINE_COMMENT: |
| |
| DEFAULT, DQID, BTID, BQID are the 4 'normal states'. Behavior is identical |
| except for how quoted identifiers are recognized. |
| |
| The BQHID state exists only at the start of a table name (e.g. immediately after |
| FROM or INSERT INTO). As soon as an identifier is seen, the state shifts back |
| to BTID. |
| |
| After a comment has completed, the lexer returns to the previous state, one |
| of the 'normal states'. |
| */ |
| |
| /* WHITE SPACE */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> SKIP : |
| { |
| " " |
| | "\t" |
| | "\n" |
| | "\r" |
| | "\f" |
| } |
| |
| /* COMMENTS */ |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < HINT_BEG: "/*+"> |
| | < COMMENT_END: "*/" > |
| } |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> MORE : |
| { |
| <"/**" ~["/"]> { pushState(); } : IN_FORMAL_COMMENT |
| } |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> MORE : |
| { |
| "/*" { pushState(); } : IN_MULTI_LINE_COMMENT |
| } |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> SKIP : |
| { |
| <SINGLE_LINE_COMMENT: ("//"|"--")(~["\n","\r"])* ("\n"|"\r"|"\r\n")? > |
| } |
| |
| <IN_FORMAL_COMMENT> |
| SPECIAL_TOKEN : |
| { |
| <FORMAL_COMMENT: <COMMENT_END> > { popState(); } |
| } |
| |
| <IN_MULTI_LINE_COMMENT> |
| SPECIAL_TOKEN : |
| { |
| <MULTI_LINE_COMMENT: <COMMENT_END> > { popState(); } |
| } |
| |
| <IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT> |
| MORE : |
| { |
| < ~[] > |
| } |
| |
| |
| /* IDENTIFIERS */ |
| |
| <DEFAULT> TOKEN : |
| { |
| < BRACKET_QUOTED_IDENTIFIER: |
| "[" |
| ( |
| (~["]","\n","\r"]) |
| | |
| ("]]") |
| )+ |
| "]" |
| > |
| } |
| |
| <DQID> TOKEN : |
| { |
| < QUOTED_IDENTIFIER: |
| "\"" |
| ( |
| (~["\"","\n","\r"]) |
| | |
| ("\"\"") |
| )+ |
| "\"" |
| > |
| } |
| |
| <BTID> TOKEN : |
| { |
| < BACK_QUOTED_IDENTIFIER: |
| "`" |
| ( |
| (~["`","\n","\r"]) |
| | |
| ("``") |
| )+ |
| "`" |
| > |
| } |
| |
| <BQID, BQHID> TOKEN : |
| { |
| // BigQuery-style backtick-quoted identifier, escaped using backslash |
| < BIG_QUERY_BACK_QUOTED_IDENTIFIER: |
| "`" |
| ( |
| (~["\\", "`"]) |
| | |
| ("\\" ~[]) |
| )* |
| "`" |
| > |
| } |
| |
| <BQHID> TOKEN : |
| { |
| // Per BigQuery: "Project IDs must contain 6-63 lowercase letters, digits, |
| // or dashes. IDs must start with a letter and may not end with a dash." |
| // We do not restrict length, or prevent identifiers from ending in a dash. |
| < HYPHENATED_IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>|"-")* > { popState(); } |
| } |
| |
| <DEFAULT, DQID, BTID, BQID> TOKEN : |
| { |
| < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* > |
| } |
| |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < COLLATION_ID: |
| (<LETTER>|<DIGIT>)+ (<LETTER>|<DIGIT>|":"|"."|"-"|"_")* |
| "$" |
| (<LETTER>|"_")+ |
| ("$" (<LETTER>|<DIGIT>|"_")+)? |
| > |
| | |
| < UNICODE_QUOTED_IDENTIFIER: "U" "&" <QUOTED_IDENTIFIER> > |
| | |
| < #LETTER: |
| [ |
| "\u0024", |
| "\u0041"-"\u005a", |
| "\u005f", |
| "\u0061"-"\u007a", |
| "\u00c0"-"\u00d6", |
| "\u00d8"-"\u00f6", |
| "\u00f8"-"\u00ff", |
| "\u0100"-"\u1fff", |
| "\u3040"-"\u318f", |
| "\u3300"-"\u337f", |
| "\u3400"-"\u3d2d", |
| "\u4e00"-"\u9fff", |
| "\uf900"-"\ufaff" |
| ] |
| > |
| | |
| < #DIGIT: |
| [ |
| "\u0030"-"\u0039", |
| "\u0660"-"\u0669", |
| "\u06f0"-"\u06f9", |
| "\u0966"-"\u096f", |
| "\u09e6"-"\u09ef", |
| "\u0a66"-"\u0a6f", |
| "\u0ae6"-"\u0aef", |
| "\u0b66"-"\u0b6f", |
| "\u0be7"-"\u0bef", |
| "\u0c66"-"\u0c6f", |
| "\u0ce6"-"\u0cef", |
| "\u0d66"-"\u0d6f", |
| "\u0e50"-"\u0e59", |
| "\u0ed0"-"\u0ed9", |
| "\u1040"-"\u1049" |
| ] |
| > |
| } |
| |
| /* Special token to throw a wrench in the works. It is never valid in SQL, |
| and so when it occurs, it causes the parser to print which tokens would |
| have been valid at that point. Used by SqlAdvisor. */ |
| <DEFAULT, DQID, BTID, BQID, BQHID> TOKEN : |
| { |
| < BEL: |
| [ |
| "\u0007" |
| ] |
| > |
| } |
| |
| /** |
| * Defines a production which can never be accepted by the parser. |
| * In effect, it tells the parser, "If you got here, you've gone too far." |
| * It is used as the default production for parser extension points; |
| * derived parsers replace it with a real production when they want to |
| * implement a particular extension point. |
| */ |
| void UnusedExtension() : |
| { |
| } |
| { |
| ( |
| LOOKAHEAD({false}) <ZONE> |
| ) |
| } |