org.jooq.impl.ParserImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payment-retries-plugin Show documentation
Show all versions of payment-retries-plugin Show documentation
Kill Bill Payment Retries plugin
The newest version!
/*
* Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com)
* All rights reserved.
*
* Licensed 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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.jooq.exception.SQLStateSubclass.C42000_NO_SUBCLASS;
import static org.jooq.impl.DSL.acos;
import static org.jooq.impl.DSL.ascii;
import static org.jooq.impl.DSL.asin;
import static org.jooq.impl.DSL.atan;
import static org.jooq.impl.DSL.atan2;
import static org.jooq.impl.DSL.avg;
import static org.jooq.impl.DSL.avgDistinct;
import static org.jooq.impl.DSL.bitLength;
import static org.jooq.impl.DSL.boolOr;
import static org.jooq.impl.DSL.cast;
import static org.jooq.impl.DSL.ceil;
import static org.jooq.impl.DSL.charLength;
import static org.jooq.impl.DSL.check;
import static org.jooq.impl.DSL.choose;
import static org.jooq.impl.DSL.coalesce;
import static org.jooq.impl.DSL.concat;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.constraint;
import static org.jooq.impl.DSL.cos;
import static org.jooq.impl.DSL.cosh;
import static org.jooq.impl.DSL.cot;
import static org.jooq.impl.DSL.coth;
import static org.jooq.impl.DSL.count;
import static org.jooq.impl.DSL.countDistinct;
import static org.jooq.impl.DSL.cube;
import static org.jooq.impl.DSL.cumeDist;
import static org.jooq.impl.DSL.currentDate;
import static org.jooq.impl.DSL.currentSchema;
import static org.jooq.impl.DSL.currentTime;
import static org.jooq.impl.DSL.currentTimestamp;
import static org.jooq.impl.DSL.currentUser;
import static org.jooq.impl.DSL.date;
import static org.jooq.impl.DSL.day;
import static org.jooq.impl.DSL.deg;
import static org.jooq.impl.DSL.denseRank;
import static org.jooq.impl.DSL.every;
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.exp;
import static org.jooq.impl.DSL.extract;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.firstValue;
import static org.jooq.impl.DSL.floor;
import static org.jooq.impl.DSL.foreignKey;
import static org.jooq.impl.DSL.greatest;
import static org.jooq.impl.DSL.grouping;
import static org.jooq.impl.DSL.groupingId;
import static org.jooq.impl.DSL.groupingSets;
import static org.jooq.impl.DSL.hour;
import static org.jooq.impl.DSL.ifnull;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.isnull;
import static org.jooq.impl.DSL.lag;
import static org.jooq.impl.DSL.lastValue;
import static org.jooq.impl.DSL.lateral;
import static org.jooq.impl.DSL.lead;
import static org.jooq.impl.DSL.least;
import static org.jooq.impl.DSL.left;
import static org.jooq.impl.DSL.length;
import static org.jooq.impl.DSL.level;
import static org.jooq.impl.DSL.ln;
import static org.jooq.impl.DSL.log;
import static org.jooq.impl.DSL.lower;
import static org.jooq.impl.DSL.lpad;
import static org.jooq.impl.DSL.ltrim;
import static org.jooq.impl.DSL.max;
import static org.jooq.impl.DSL.maxDistinct;
import static org.jooq.impl.DSL.md5;
import static org.jooq.impl.DSL.mid;
import static org.jooq.impl.DSL.min;
import static org.jooq.impl.DSL.minDistinct;
import static org.jooq.impl.DSL.minute;
import static org.jooq.impl.DSL.month;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.nthValue;
import static org.jooq.impl.DSL.ntile;
import static org.jooq.impl.DSL.nullif;
import static org.jooq.impl.DSL.nvl;
import static org.jooq.impl.DSL.nvl2;
import static org.jooq.impl.DSL.octetLength;
import static org.jooq.impl.DSL.orderBy;
import static org.jooq.impl.DSL.partitionBy;
import static org.jooq.impl.DSL.percentRank;
import static org.jooq.impl.DSL.position;
import static org.jooq.impl.DSL.primaryKey;
import static org.jooq.impl.DSL.prior;
import static org.jooq.impl.DSL.rad;
import static org.jooq.impl.DSL.rangeBetweenCurrentRow;
import static org.jooq.impl.DSL.rangeBetweenFollowing;
import static org.jooq.impl.DSL.rangeBetweenPreceding;
import static org.jooq.impl.DSL.rangeBetweenUnboundedFollowing;
import static org.jooq.impl.DSL.rangeBetweenUnboundedPreceding;
import static org.jooq.impl.DSL.rangeCurrentRow;
import static org.jooq.impl.DSL.rangeFollowing;
import static org.jooq.impl.DSL.rangePreceding;
import static org.jooq.impl.DSL.rangeUnboundedFollowing;
import static org.jooq.impl.DSL.rangeUnboundedPreceding;
import static org.jooq.impl.DSL.rank;
import static org.jooq.impl.DSL.regrAvgX;
import static org.jooq.impl.DSL.regrAvgY;
import static org.jooq.impl.DSL.regrCount;
import static org.jooq.impl.DSL.regrIntercept;
import static org.jooq.impl.DSL.regrR2;
import static org.jooq.impl.DSL.regrSXX;
import static org.jooq.impl.DSL.regrSXY;
import static org.jooq.impl.DSL.regrSYY;
import static org.jooq.impl.DSL.regrSlope;
import static org.jooq.impl.DSL.repeat;
import static org.jooq.impl.DSL.replace;
import static org.jooq.impl.DSL.reverse;
import static org.jooq.impl.DSL.right;
import static org.jooq.impl.DSL.rollup;
import static org.jooq.impl.DSL.round;
import static org.jooq.impl.DSL.rowNumber;
import static org.jooq.impl.DSL.rownum;
import static org.jooq.impl.DSL.rowsBetweenCurrentRow;
import static org.jooq.impl.DSL.rowsBetweenFollowing;
import static org.jooq.impl.DSL.rowsBetweenPreceding;
import static org.jooq.impl.DSL.rowsBetweenUnboundedFollowing;
import static org.jooq.impl.DSL.rowsBetweenUnboundedPreceding;
import static org.jooq.impl.DSL.rowsCurrentRow;
import static org.jooq.impl.DSL.rowsFollowing;
import static org.jooq.impl.DSL.rowsPreceding;
import static org.jooq.impl.DSL.rowsUnboundedFollowing;
import static org.jooq.impl.DSL.rowsUnboundedPreceding;
import static org.jooq.impl.DSL.rpad;
import static org.jooq.impl.DSL.rtrim;
import static org.jooq.impl.DSL.schema;
import static org.jooq.impl.DSL.second;
import static org.jooq.impl.DSL.sequence;
import static org.jooq.impl.DSL.sign;
import static org.jooq.impl.DSL.sin;
import static org.jooq.impl.DSL.sinh;
import static org.jooq.impl.DSL.space;
import static org.jooq.impl.DSL.sqrt;
import static org.jooq.impl.DSL.stddevPop;
import static org.jooq.impl.DSL.stddevSamp;
import static org.jooq.impl.DSL.substring;
import static org.jooq.impl.DSL.sum;
import static org.jooq.impl.DSL.sumDistinct;
import static org.jooq.impl.DSL.table;
import static org.jooq.impl.DSL.tan;
import static org.jooq.impl.DSL.tanh;
import static org.jooq.impl.DSL.time;
import static org.jooq.impl.DSL.timestamp;
import static org.jooq.impl.DSL.trim;
import static org.jooq.impl.DSL.unique;
import static org.jooq.impl.DSL.varPop;
import static org.jooq.impl.DSL.varSamp;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.DSL.year;
import static org.jooq.impl.ParserImpl.Type.B;
import static org.jooq.impl.ParserImpl.Type.D;
import static org.jooq.impl.ParserImpl.Type.N;
import static org.jooq.impl.ParserImpl.Type.S;
import static org.jooq.impl.Tools.EMPTY_COLLECTION;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.EMPTY_STRING;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.jooq.AggregateFunction;
import org.jooq.AlterIndexFinalStep;
import org.jooq.AlterIndexStep;
import org.jooq.AlterSchemaFinalStep;
import org.jooq.AlterSchemaStep;
import org.jooq.AlterTableDropStep;
import org.jooq.AlterTableFinalStep;
import org.jooq.AlterTableStep;
import org.jooq.CaseConditionStep;
import org.jooq.CaseValueStep;
import org.jooq.CaseWhenStep;
import org.jooq.Comparator;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Constraint;
import org.jooq.ConstraintTypeStep;
import org.jooq.CreateIndexFinalStep;
import org.jooq.CreateIndexStep;
import org.jooq.CreateIndexWhereStep;
import org.jooq.CreateTableAsStep;
import org.jooq.CreateTableColumnStep;
import org.jooq.CreateTableConstraintStep;
import org.jooq.CreateTableFinalStep;
import org.jooq.DDLQuery;
import org.jooq.DSLContext;
import org.jooq.DataType;
import org.jooq.DatePart;
import org.jooq.Delete;
import org.jooq.DeleteFinalStep;
import org.jooq.DeleteWhereStep;
import org.jooq.DropIndexFinalStep;
import org.jooq.DropIndexOnStep;
import org.jooq.DropSchemaFinalStep;
import org.jooq.DropSchemaStep;
import org.jooq.DropSequenceFinalStep;
import org.jooq.DropTableFinalStep;
import org.jooq.DropTableStep;
import org.jooq.DropViewFinalStep;
import org.jooq.Field;
import org.jooq.GroupField;
import org.jooq.Insert;
import org.jooq.InsertSetStep;
import org.jooq.InsertValuesStepN;
import org.jooq.JoinType;
import org.jooq.Merge;
import org.jooq.MergeFinalStep;
import org.jooq.MergeMatchedStep;
import org.jooq.MergeNotMatchedStep;
import org.jooq.Name;
import org.jooq.Parser;
import org.jooq.Queries;
import org.jooq.Query;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Select;
import org.jooq.Sequence;
import org.jooq.SortField;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableLike;
import org.jooq.TableOptionalOnStep;
import org.jooq.Truncate;
import org.jooq.TruncateCascadeStep;
import org.jooq.TruncateFinalStep;
import org.jooq.TruncateIdentityStep;
import org.jooq.Update;
import org.jooq.WindowBeforeOverStep;
import org.jooq.WindowIgnoreNullsStep;
import org.jooq.WindowOverStep;
import org.jooq.WindowSpecification;
import org.jooq.WindowSpecificationOrderByStep;
import org.jooq.WindowSpecificationRowsAndStep;
import org.jooq.WindowSpecificationRowsStep;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.SQLStateSubclass;
/**
* @author Lukas Eder
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Deprecated
class ParserImpl implements Parser {
private final Configuration configuration;
private final DSLContext dsl;
ParserImpl(Configuration configuration) {
this.configuration = configuration;
this.dsl = DSL.using(configuration);
}
// -----------------------------------------------------------------------------------------------------------------
// Top level parsing
// -----------------------------------------------------------------------------------------------------------------
@Override
public final Queries parse(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
List result = new ArrayList();
do {
result.add(parseQuery(ctx));
}
while (parseIf(ctx, ";"));
if (!ctx.done())
throw new ParserException(ctx);
return new QueriesImpl(result);
}
@Override
public final Query parseQuery(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
Query result = parseQuery(ctx);
if (!ctx.done())
throw new ParserException(ctx);
return result;
}
@Override
public final Table> parseTable(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
Table> result = parseTable(ctx);
if (!ctx.done())
throw new ParserException(ctx);
return result;
}
@Override
public final Field> parseField(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
Field> result = parseField(ctx);
if (!ctx.done())
throw new ParserException(ctx);
return result;
}
@Override
public final Condition parseCondition(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
Condition result = parseCondition(ctx);
if (!ctx.done())
throw new ParserException(ctx);
return result;
}
@Override
public final Name parseName(String sql) {
ParserContext ctx = new ParserContext(dsl, sql);
Name result = parseName(ctx);
if (!ctx.done())
throw new ParserException(ctx);
return result;
}
static final Query parseQuery(ParserContext ctx) {
if (ctx.done())
return null;
parseWhitespaceIf(ctx);
try {
switch (ctx.character()) {
case 'a':
case 'A':
if (peekKeyword(ctx, "ALTER"))
return parseAlter(ctx);
break;
case 'c':
case 'C':
if (peekKeyword(ctx, "CREATE"))
return parseCreate(ctx);
break;
case 'd':
case 'D':
if (peekKeyword(ctx, "DELETE"))
return parseDelete(ctx);
else if (peekKeyword(ctx, "DROP"))
return parseDrop(ctx);
break;
case 'i':
case 'I':
if (peekKeyword(ctx, "INSERT"))
return parseInsert(ctx);
break;
case 'm':
case 'M':
if (peekKeyword(ctx, "MERGE"))
return parseMerge(ctx);
break;
case 'r':
case 'R':
if (peekKeyword(ctx, "RENAME"))
return parseRename(ctx);
break;
case 's':
case 'S':
if (peekKeyword(ctx, "SELECT"))
return parseSelect(ctx);
break;
case 't':
case 'T':
if (peekKeyword(ctx, "TRUNCATE"))
return parseTruncate(ctx);
break;
case 'u':
case 'U':
if (peekKeyword(ctx, "UPDATE"))
return parseUpdate(ctx);
break;
case '(':
// TODO are there other possible statement types?
return parseSelect(ctx);
default:
break;
}
}
finally {
parseWhitespaceIf(ctx);
if (!ctx.done() && ctx.character() != ';')
throw ctx.unexpectedToken();
}
throw ctx.exception();
}
// -----------------------------------------------------------------------------------------------------------------
// Statement parsing
// -----------------------------------------------------------------------------------------------------------------
private static final SelectQueryImpl parseSelect(ParserContext ctx) {
SelectQueryImpl result = parseQueryPrimary(ctx);
CombineOperator combine;
while ((combine = parseCombineOperatorIf(ctx)) != null) {
switch (combine) {
case UNION:
result = (SelectQueryImpl) result.union(parseQueryPrimary(ctx));
break;
case UNION_ALL:
result = (SelectQueryImpl) result.unionAll(parseQueryPrimary(ctx));
break;
case EXCEPT:
result = (SelectQueryImpl) result.except(parseQueryPrimary(ctx));
break;
case EXCEPT_ALL:
result = (SelectQueryImpl) result.exceptAll(parseQueryPrimary(ctx));
break;
case INTERSECT:
result = (SelectQueryImpl) result.intersect(parseQueryPrimary(ctx));
break;
case INTERSECT_ALL:
result = (SelectQueryImpl) result.intersectAll(parseQueryPrimary(ctx));
break;
default:
ctx.unexpectedToken();
break;
}
}
if (parseKeywordIf(ctx, "ORDER BY"))
result.addOrderBy(parseSortSpecification(ctx));
if (!result.getLimit().isApplicable()) {
boolean offsetStandard = false;
boolean offsetPostgres = false;
if (parseKeywordIf(ctx, "OFFSET")) {
result.addOffset((int) (long) parseUnsignedInteger(ctx));
if (parseKeywordIf(ctx, "ROWS") || parseKeywordIf(ctx, "ROW"))
offsetStandard = true;
else
offsetPostgres = true;
}
if (!offsetStandard && parseKeywordIf(ctx, "LIMIT")) {
int limit = (int) (long) parseUnsignedInteger(ctx);
if (!offsetPostgres && parseIf(ctx, ','))
result.addLimit(limit, (int) (long) parseUnsignedInteger(ctx));
else if (!offsetPostgres && parseKeywordIf(ctx, "OFFSET"))
result.addLimit((int) (long) parseUnsignedInteger(ctx), limit);
else
result.addLimit(limit);
}
else if (!offsetPostgres && parseKeywordIf(ctx, "FETCH")) {
if (!parseKeywordIf(ctx, "FIRST") && !parseKeywordIf(ctx, "NEXT"))
throw ctx.unexpectedToken();
result.addLimit((int) (long) parseUnsignedInteger(ctx));
if (!parseKeywordIf(ctx, "ROWS ONLY") && !parseKeywordIf(ctx, "ROW ONLY"))
throw ctx.unexpectedToken();
}
}
// TODO FOR UPDATE, etc.
return result;
}
private static final SelectQueryImpl parseQueryPrimary(ParserContext ctx) {
if (parseIf(ctx, '(')) {
SelectQueryImpl result = parseSelect(ctx);
parse(ctx, ')');
return result;
}
parseKeyword(ctx, "SELECT");
boolean distinct = parseKeywordIf(ctx, "DISTINCT") || parseKeywordIf(ctx, "UNIQUE");
Long limit = null;
Long offset = null;
if (!distinct)
parseKeywordIf(ctx, "ALL");
if (parseKeywordIf(ctx, "TOP")) {
limit = parseUnsignedInteger(ctx);
if (parseKeywordIf(ctx, "START AT"))
offset = parseUnsignedInteger(ctx);
}
List> select = parseSelectList(ctx);
List> from = null;
Condition startWith = null;
Condition connectBy = null;
boolean connectByNoCycle = false;
Condition where = null;
List groupBy = null;
Condition having = null;
if (parseKeywordIf(ctx, "FROM"))
from = parseTables(ctx);
// TODO is there a better way?
if (from != null && from.size() == 1 && from.get(0).getName().equalsIgnoreCase("dual"))
from = null;
if (parseKeywordIf(ctx, "START WITH")) {
startWith = parseCondition(ctx);
parseKeyword(ctx, "CONNECT BY");
connectByNoCycle = parseKeywordIf(ctx, "NOCYCLE");
connectBy = parseCondition(ctx);
}
else if (parseKeywordIf(ctx, "CONNECT BY")) {
connectByNoCycle = parseKeywordIf(ctx, "NOCYCLE");
connectBy = parseCondition(ctx);
if (parseKeywordIf(ctx, "START WITH"))
startWith = parseCondition(ctx);
}
if (parseKeywordIf(ctx, "WHERE"))
where = parseCondition(ctx);
if (parseKeywordIf(ctx, "GROUP BY")) {
if (parseIf(ctx, '(')) {
parse(ctx, ')');
groupBy = emptyList();
}
else if (parseKeywordIf(ctx, "ROLLUP")) {
parse(ctx, '(');
groupBy = singletonList(rollup(parseFields(ctx).toArray(EMPTY_FIELD)));
parse(ctx, ')');
}
else if (parseKeywordIf(ctx, "CUBE")) {
parse(ctx, '(');
groupBy = singletonList(cube(parseFields(ctx).toArray(EMPTY_FIELD)));
parse(ctx, ')');
}
else if (parseKeywordIf(ctx, "GROUPING SETS")) {
List>> fieldSets = new ArrayList>>();
parse(ctx, '(');
do {
parse(ctx, '(');
if (parseIf(ctx, ')')) {
fieldSets.add(Collections.>emptyList());
}
else {
fieldSets.add(parseFields(ctx));
parse(ctx, ')');
}
}
while (parseIf(ctx, ','));
parse(ctx, ')');
groupBy = singletonList(groupingSets(fieldSets.toArray((Collection[]) EMPTY_COLLECTION)));
}
else {
groupBy = (List) parseFields(ctx);
if (parseKeywordIf(ctx, "WITH ROLLUP"))
groupBy = singletonList(rollup(groupBy.toArray(EMPTY_FIELD)));
}
}
if (parseKeywordIf(ctx, "HAVING"))
having = parseCondition(ctx);
// TODO support WINDOW
SelectQueryImpl result = (SelectQueryImpl) ctx.dsl.selectQuery();
if (distinct)
result.setDistinct(distinct);
if (select.size() > 0)
result.addSelect(select);
if (from != null)
result.addFrom(from);
if (connectBy != null)
if (connectByNoCycle)
result.addConnectByNoCycle(connectBy);
else
result.addConnectBy(connectBy);
if (startWith != null)
result.setConnectByStartWith(startWith);
if (where != null)
result.addConditions(where);
if (groupBy != null)
result.addGroupBy(groupBy);
if (having != null)
result.addHaving(having);
if (limit != null)
if (offset != null)
result.addLimit((int) (long) offset, (int) (long) limit);
else
result.addLimit((int) (long) limit);
return result;
}
private static final Delete> parseDelete(ParserContext ctx) {
parseKeyword(ctx, "DELETE");
parseKeywordIf(ctx, "FROM");
Table> tableName = parseTableName(ctx);
boolean where = parseKeywordIf(ctx, "WHERE");
Condition condition = where ? parseCondition(ctx) : null;
// TODO Implement returning
// boolean returning = parseKeywordIf(ctx, "RETURNING");
DeleteWhereStep> s1;
DeleteFinalStep> s2;
s1 = ctx.dsl.delete(tableName);
s2 = where
? s1.where(condition)
: s1;
return s2;
}
private static final Insert> parseInsert(ParserContext ctx) {
parseKeyword(ctx, "INSERT INTO");
Table> tableName = parseTableName(ctx);
Field>[] fields = null;
if (parseIf(ctx, '(')) {
fields = Tools.fieldsByName(parseIdentifiers(ctx).toArray(EMPTY_STRING));
parse(ctx, ')');
}
if (parseKeywordIf(ctx, "VALUES")) {
List>> allValues = new ArrayList>>();
do {
parse(ctx, '(');
List> values = parseFields(ctx);
if (fields != null && fields.length != values.size())
throw ctx.exception();
allValues.add(values);
parse(ctx, ')');
}
while (parseIf(ctx, ','));
InsertSetStep> step1 = ctx.dsl.insertInto(tableName);
InsertValuesStepN> step2 = (fields != null)
? step1.columns(fields)
: (InsertValuesStepN>) step1;
for (List> values : allValues)
step2 = step2.values(values);
return step2;
}
else if (parseKeywordIf(ctx, "SET")) {
Map, Object> map = parseSetClauseList(ctx);
return ctx.dsl.insertInto(tableName).set(map);
}
else if (peekKeyword(ctx, "SELECT")){
SelectQueryImpl select = parseSelect(ctx);
return fields == null
? ctx.dsl.insertInto(tableName).select(select)
: ctx.dsl.insertInto(tableName).columns(fields).select(select);
}
else if (parseKeywordIf(ctx, "DEFAULT VALUES")) {
if (fields != null)
throw ctx.exception();
else
return ctx.dsl.insertInto(tableName).defaultValues();
}
throw ctx.unexpectedToken();
// TODO Support RETURNING
// TODO Support ON DUPLICATE KEY UPDATE
}
private static final Update> parseUpdate(ParserContext ctx) {
parseKeyword(ctx, "UPDATE");
Table> tableName = parseTableName(ctx);
parseKeyword(ctx, "SET");
// TODO Row value expression updates
Map, Object> map = parseSetClauseList(ctx);
// TODO support FROM
Condition condition = parseKeywordIf(ctx, "WHERE") ? parseCondition(ctx) : null;
// TODO support RETURNING
return condition == null
? ctx.dsl.update(tableName).set(map)
: ctx.dsl.update(tableName).set(map).where(condition);
}
private static final Map, Object> parseSetClauseList(ParserContext ctx) {
Map, Object> map = new LinkedHashMap, Object>();
do {
Field> field = parseFieldName(ctx);
if (map.containsKey(field))
throw ctx.exception();
parse(ctx, '=');
Field> value = parseField(ctx);
map.put(field, value);
}
while (parseIf(ctx, ','));
return map;
}
private static final Merge> parseMerge(ParserContext ctx) {
parseKeyword(ctx, "MERGE INTO");
Table> target = parseTableName(ctx);
parseKeyword(ctx, "USING");
parse(ctx, '(');
TableLike> using = parseSelect(ctx);
parse(ctx, ')');
if (parseKeywordIf(ctx, "AS"))
using = using.asTable(parseIdentifier(ctx));
parseKeyword(ctx, "ON");
Condition on = parseCondition(ctx);
boolean update = false;
boolean insert = false;
List> insertColumns = null;
List> insertValues = null;
Map, Object> updateSet = null;
for (;;) {
if (!update && (update = parseKeywordIf(ctx, "WHEN MATCHED THEN UPDATE SET"))) {
updateSet = parseSetClauseList(ctx);
}
else if (!insert && (insert = parseKeywordIf(ctx, "WHEN NOT MATCHED THEN INSERT"))) {
parse(ctx, '(');
insertColumns = Arrays.asList(Tools.fieldsByName(parseIdentifiers(ctx)));
parse(ctx, ')');
parseKeyword(ctx, "VALUES");
parse(ctx, '(');
insertValues = parseFields(ctx);
parse(ctx, ')');
if (insertColumns.size() != insertValues.size())
throw ctx.exception();
}
else
break;
}
if (!update && !insert)
throw ctx.exception();
// TODO support WHERE
// TODO support multi clause MERGE
// TODO support DELETE
MergeMatchedStep> s1 = ctx.dsl.mergeInto(target).using(using).on(on);
MergeNotMatchedStep> s2 = update ? s1.whenMatchedThenUpdate().set(updateSet) : s1;
MergeFinalStep> s3 = insert ? s2.whenNotMatchedThenInsert(insertColumns).values(insertValues) : s2;
return s3;
}
private static final DDLQuery parseCreate(ParserContext ctx) {
parseKeyword(ctx, "CREATE");
if (parseKeywordIf(ctx, "TABLE"))
return parseCreateTable(ctx);
if (parseKeywordIf(ctx, "INDEX"))
return parseCreateIndex(ctx);
else if (parseKeywordIf(ctx, "SCHEMA"))
return parseCreateSchema(ctx);
else if (parseKeywordIf(ctx, "VIEW"))
return parseCreateView(ctx);
else
throw ctx.unexpectedToken();
}
private static final DDLQuery parseAlter(ParserContext ctx) {
parseKeyword(ctx, "ALTER");
if (parseKeywordIf(ctx, "TABLE"))
return parseAlterTable(ctx);
else if (parseKeywordIf(ctx, "INDEX"))
return parseAlterIndex(ctx);
else if (parseKeywordIf(ctx, "SCHEMA"))
return parseAlterSchema(ctx);
else if (parseKeywordIf(ctx, "VIEW"))
return parseAlterView(ctx);
else
throw ctx.unexpectedToken();
}
private static final DDLQuery parseDrop(ParserContext ctx) {
parseKeyword(ctx, "DROP");
if (parseKeywordIf(ctx, "TABLE"))
return parseDropTable(ctx);
else if (parseKeywordIf(ctx, "INDEX"))
return parseDropIndex(ctx);
else if (parseKeywordIf(ctx, "VIEW"))
return parseDropView(ctx);
else if (parseKeywordIf(ctx, "SEQUENCE"))
return parseDropSequence(ctx);
else if (parseKeywordIf(ctx, "SCHEMA"))
return parseDropSchema(ctx);
else
throw ctx.unexpectedToken();
}
private static final Truncate> parseTruncate(ParserContext ctx) {
parseKeyword(ctx, "TRUNCATE");
parseKeyword(ctx, "TABLE");
Table> table = parseTableName(ctx);
boolean continueIdentity = parseKeywordIf(ctx, "CONTINUE IDENTITY");
boolean restartIdentity = !continueIdentity && parseKeywordIf(ctx, "RESTART IDENTITY");
boolean cascade = parseKeywordIf(ctx, "CASCADE");
boolean restrict = !cascade && parseKeywordIf(ctx, "RESTRICT");
TruncateIdentityStep> step1 = ctx.dsl.truncate(table);
TruncateCascadeStep> step2 =
continueIdentity
? step1.continueIdentity()
: restartIdentity
? step1.restartIdentity()
: step1;
TruncateFinalStep> step3 =
cascade
? step2.cascade()
: restrict
? step2.restrict()
: step2;
return step3;
}
// -----------------------------------------------------------------------------------------------------------------
// Statement clause parsing
// -----------------------------------------------------------------------------------------------------------------
private static final DDLQuery parseCreateView(ParserContext ctx) {
boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS");
Table> view = parseTableName(ctx);
Field>[] fields = EMPTY_FIELD;
if (parseIf(ctx, '(')) {
fields = parseFieldNames(ctx).toArray(fields);
parse(ctx, ')');
}
parseKeyword(ctx, "AS");
Select> select = parseSelect(ctx);
if (fields.length > 0 && fields.length != select.getSelect().size())
throw ctx.exception();
return ifNotExists
? ctx.dsl.createViewIfNotExists(view, fields).as(select)
: ctx.dsl.createView(view, fields).as(select);
}
private static final DDLQuery parseAlterView(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Table> oldName = parseTableName(ctx);
parseKeyword(ctx, "RENAME TO");
Table> newName = parseTableName(ctx);
return ifExists
? ctx.dsl.alterViewIfExists(oldName).renameTo(newName)
: ctx.dsl.alterView(oldName).renameTo(newName);
}
private static final DDLQuery parseDropView(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Table> tableName = parseTableName(ctx);
DropViewFinalStep s1;
s1 = ifExists
? ctx.dsl.dropViewIfExists(tableName)
: ctx.dsl.dropView(tableName);
return s1;
}
private static final DDLQuery parseDropSequence(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Sequence> sequenceName = parseSequenceName(ctx);
DropSequenceFinalStep s1;
s1 = ifExists
? ctx.dsl.dropSequenceIfExists(sequenceName)
: ctx.dsl.dropSequence(sequenceName);
return s1;
}
private static final DDLQuery parseCreateTable(ParserContext ctx) {
boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS");
Table> tableName = parseTableName(ctx);
if (parseKeywordIf(ctx, "AS")) {
Select> select = parseSelect(ctx);
CreateTableAsStep s1 = ifNotExists
? ctx.dsl.createTableIfNotExists(tableName)
: ctx.dsl.createTable(tableName);
CreateTableFinalStep s2 = s1.as(select);
return s2;
}
else {
List> fields = new ArrayList>();
List constraints = new ArrayList();
boolean primary = false;
boolean noConstraint = true;
parse(ctx, '(');
do {
String fieldName = parseIdentifier(ctx);
DataType> type = parseDataType(ctx);
boolean nullable = false;
boolean defaultValue = false;
boolean unique = false;
for (;;) {
if (!nullable) {
if (parseKeywordIf(ctx, "NULL")) {
type = type.nullable(true);
nullable = true;
continue;
}
else if (parseKeywordIf(ctx, "NOT NULL")) {
type = type.nullable(false);
nullable = true;
continue;
}
}
if (!defaultValue) {
if (parseKeywordIf(ctx, "DEFAULT")) {
type = type.defaultValue((Field) parseField(ctx));
defaultValue = true;
continue;
}
}
if (!unique) {
if (parseKeywordIf(ctx, "PRIMARY KEY")) {
constraints.add(primaryKey(fieldName));
primary = true;
unique = true;
continue;
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
constraints.add(unique(fieldName));
unique = true;
continue;
}
}
if (parseKeywordIf(ctx, "CHECK")) {
parse(ctx, '(');
constraints.add(check(parseCondition(ctx)));
parse(ctx, ')');
continue;
}
break;
}
fields.add(field(name(fieldName), type));
}
while (parseIf(ctx, ',')
&& (noConstraint =
!peekKeyword(ctx, "PRIMARY KEY")
&& !peekKeyword(ctx, "UNIQUE")
&& !peekKeyword(ctx, "FOREIGN KEY")
&& !peekKeyword(ctx, "CHECK")
&& !peekKeyword(ctx, "CONSTRAINT"))
);
if (!noConstraint) {
do {
ConstraintTypeStep constraint = null;
if (parseKeywordIf(ctx, "CONSTRAINT"))
constraint = constraint(parseIdentifier(ctx));
if (parseKeywordIf(ctx, "PRIMARY KEY")) {
if (primary) {
throw ctx.exception();
}
else {
primary = true;
parse(ctx, '(');
Field>[] fieldNames = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
constraints.add(constraint == null
? primaryKey(fieldNames)
: constraint.primaryKey(fieldNames));
}
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
parse(ctx, '(');
Field>[] fieldNames = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
constraints.add(constraint == null
? unique(fieldNames)
: constraint.unique(fieldNames));
}
else if (parseKeywordIf(ctx, "FOREIGN KEY")) {
parse(ctx, '(');
Field>[] referencing = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
parseKeyword(ctx, "REFERENCES");
Table> referencedTable = parseTableName(ctx);
parse(ctx, '(');
Field>[] referencedFields = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
if (referencing.length != referencedFields.length)
throw ctx.exception();
constraints.add(constraint == null
? foreignKey(referencing).references(referencedTable, referencedFields)
: constraint.foreignKey(referencing).references(referencedTable, referencedFields));
}
else if (parseKeywordIf(ctx, "CHECK")) {
parse(ctx, '(');
Condition condition = parseCondition(ctx);
parse(ctx, ')');
constraints.add(constraint == null
? check(condition)
: constraint.check(condition));
}
else {
throw ctx.unexpectedToken();
}
}
while (parseIf(ctx, ','));
}
parse(ctx, ')');
CreateTableAsStep s1 = ifNotExists
? ctx.dsl.createTableIfNotExists(tableName)
: ctx.dsl.createTable(tableName);
CreateTableColumnStep s2 = s1.columns(fields);
CreateTableConstraintStep s3 = constraints.isEmpty()
? s2
: s2.constraints(constraints);
return s3;
}
}
private static final DDLQuery parseAlterTable(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Table> tableName = parseTableName(ctx);
parseWhitespaceIf(ctx);
AlterTableStep s1 = ifExists
? ctx.dsl.alterTableIfExists(tableName)
: ctx.dsl.alterTable(tableName);
switch (ctx.character()) {
case 'a':
case 'A':
if (parseKeywordIf(ctx, "ADD")) {
ConstraintTypeStep constraint = null;
if (parseKeywordIf(ctx, "CONSTRAINT"))
constraint = constraint(parseIdentifier(ctx));
if (parseKeywordIf(ctx, "PRIMARY KEY")) {
parse(ctx, '(');
Field>[] fieldNames = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
return constraint == null
? s1.add(primaryKey(fieldNames))
: s1.add(constraint.primaryKey(fieldNames));
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
parse(ctx, '(');
Field>[] fieldNames = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
return constraint == null
? s1.add(unique(fieldNames))
: s1.add(constraint.unique(fieldNames));
}
else if (parseKeywordIf(ctx, "FOREIGN KEY")) {
parse(ctx, '(');
Field>[] referencing = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
parseKeyword(ctx, "REFERENCES");
Table> referencedTable = parseTableName(ctx);
parse(ctx, '(');
Field>[] referencedFields = parseFieldNames(ctx).toArray(EMPTY_FIELD);
parse(ctx, ')');
if (referencing.length != referencedFields.length)
throw ctx.exception();
return constraint == null
? s1.add(foreignKey(referencing).references(referencedTable, referencedFields))
: s1.add(constraint.foreignKey(referencing).references(referencedTable, referencedFields));
}
else if (parseKeywordIf(ctx, "CHECK")) {
parse(ctx, '(');
Condition condition = parseCondition(ctx);
parse(ctx, ')');
return constraint == null
? s1.add(check(condition))
: s1.add(constraint.check(condition));
}
else if (constraint != null) {
throw ctx.unexpectedToken();
}
else {
parseKeywordIf(ctx, "COLUMN");
// The below code is taken from CREATE TABLE, with minor modifications as
// https://github.com/jOOQ/jOOQ/issues/5317 has not yet been implemented
// Once implemented, we might be able to factor out the common logic into
// a new parseXXX() method.
String fieldName = parseIdentifier(ctx);
DataType type = parseDataType(ctx);
boolean nullable = false;
boolean defaultValue = false;
boolean unique = false;
for (;;) {
if (!nullable) {
if (parseKeywordIf(ctx, "NULL")) {
type = type.nullable(true);
nullable = true;
continue;
}
else if (parseKeywordIf(ctx, "NOT NULL")) {
type = type.nullable(false);
nullable = true;
continue;
}
}
if (!defaultValue) {
if (parseKeywordIf(ctx, "DEFAULT")) {
type = type.defaultValue(parseField(ctx));
defaultValue = true;
continue;
}
}
if (!unique) {
if (parseKeywordIf(ctx, "PRIMARY KEY")) {
throw ctx.unexpectedToken();
}
else if (parseKeywordIf(ctx, "UNIQUE")) {
throw ctx.unexpectedToken();
}
}
if (parseKeywordIf(ctx, "CHECK")) {
throw ctx.unexpectedToken();
}
break;
}
return s1.add(field(name(fieldName), type), type);
}
}
break;
case 'd':
case 'D':
if (parseKeywordIf(ctx, "DROP")) {
if (parseKeywordIf(ctx, "COLUMN")) {
Field> field = parseFieldName(ctx);
boolean cascade = parseKeywordIf(ctx, "CASCADE");
boolean restrict = !cascade && parseKeywordIf(ctx, "RESTRICT");
AlterTableDropStep s2 = s1.dropColumn(field);
AlterTableFinalStep s3 =
cascade
? s2.cascade()
: restrict
? s2.restrict()
: s2;
return s3;
}
else if (parseKeywordIf(ctx, "CONSTRAINT")) {
String constraint = parseIdentifier(ctx);
return s1.dropConstraint(constraint);
}
}
break;
case 'r':
case 'R':
if (parseKeywordIf(ctx, "RENAME")) {
if (parseKeywordIf(ctx, "TO")) {
String newName = parseIdentifier(ctx);
return s1.renameTo(newName);
}
else if (parseKeywordIf(ctx, "COLUMN")) {
String oldName = parseIdentifier(ctx);
parseKeyword(ctx, "TO");
String newName = parseIdentifier(ctx);
return s1.renameColumn(oldName).to(newName);
}
else if (parseKeywordIf(ctx, "CONSTRAINT")) {
String oldName = parseIdentifier(ctx);
parseKeyword(ctx, "TO");
String newName = parseIdentifier(ctx);
return s1.renameConstraint(oldName).to(newName);
}
}
break;
}
throw ctx.unexpectedToken();
}
private static final DDLQuery parseRename(ParserContext ctx) {
parseKeyword(ctx, "RENAME");
parseWhitespaceIf(ctx);
switch (ctx.character()) {
case 'c':
case 'C':
if (parseKeywordIf(ctx, "COLUMN")) {
TableField, ?> oldName = parseFieldName(ctx);
parseKeyword(ctx, "TO");
Field> newName = parseFieldName(ctx);
return ctx.dsl.alterTable(oldName.getTable().getName()).renameColumn(oldName).to(newName);
}
break;
case 'i':
case 'I':
if (parseKeywordIf(ctx, "INDEX")) {
Name oldName = parseIndexName(ctx);
parseKeyword(ctx, "TO");
Name newName = parseIndexName(ctx);
return ctx.dsl.alterIndex(oldName).renameTo(newName);
}
break;
case 's':
case 'S':
if (parseKeywordIf(ctx, "SCHEMA")) {
Schema oldName = parseSchemaName(ctx);
parseKeyword(ctx, "TO");
Schema newName = parseSchemaName(ctx);
return ctx.dsl.alterSchema(oldName).renameTo(newName);
}
else if (parseKeywordIf(ctx, "SEQUENCE")) {
Sequence> oldName = parseSequenceName(ctx);
parseKeyword(ctx, "TO");
Sequence> newName = parseSequenceName(ctx);
return ctx.dsl.alterSequence(oldName).renameTo(newName);
}
break;
case 'v':
case 'V':
if (parseKeywordIf(ctx, "VIEW")) {
Table> oldName = parseTableName(ctx);
parseKeyword(ctx, "TO");
Table> newName = parseTableName(ctx);
return ctx.dsl.alterView(oldName).renameTo(newName);
}
break;
}
// If all of the above fails, we can assume we're renaming a table.
parseKeywordIf(ctx, "TABLE");
Table> oldName = parseTableName(ctx);
parseKeyword(ctx, "TO");
Table> newName = parseTableName(ctx);
return ctx.dsl.alterTable(oldName).renameTo(newName);
}
private static final DDLQuery parseDropTable(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Table> tableName = parseTableName(ctx);
boolean cascade = parseKeywordIf(ctx, "CASCADE");
boolean restrict = !cascade && parseKeywordIf(ctx, "RESTRICT");
DropTableStep s1;
DropTableFinalStep s2;
s1 = ifExists
? ctx.dsl.dropTableIfExists(tableName)
: ctx.dsl.dropTable(tableName);
s2 = cascade
? s1.cascade()
: restrict
? s1.restrict()
: s1;
return s2;
}
private static final DDLQuery parseCreateSchema(ParserContext ctx) {
boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS");
Schema schemaName = parseSchemaName(ctx);
return ifNotExists
? ctx.dsl.createSchemaIfNotExists(schemaName)
: ctx.dsl.createSchema(schemaName);
}
private static final DDLQuery parseAlterSchema(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Schema schemaName = parseSchemaName(ctx);
parseKeyword(ctx, "RENAME TO");
Schema newName = parseSchemaName(ctx);
AlterSchemaStep s1 = ifExists
? ctx.dsl.alterSchemaIfExists(schemaName)
: ctx.dsl.alterSchema(schemaName);
AlterSchemaFinalStep s2 = s1.renameTo(newName);
return s2;
}
private static final DDLQuery parseDropSchema(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Schema schemaName = parseSchemaName(ctx);
boolean cascade = parseKeywordIf(ctx, "CASCADE");
boolean restrict = !cascade && parseKeywordIf(ctx, "RESTRICT");
DropSchemaStep s1;
DropSchemaFinalStep s2;
s1 = ifExists
? ctx.dsl.dropSchemaIfExists(schemaName)
: ctx.dsl.dropSchema(schemaName);
s2 = cascade
? s1.cascade()
: restrict
? s1.restrict()
: s1;
return s2;
}
private static final DDLQuery parseCreateIndex(ParserContext ctx) {
boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS");
Name indexName = parseIndexName(ctx);
parseKeyword(ctx, "ON");
Table> tableName = parseTableName(ctx);
parse(ctx, '(');
Field>[] fieldNames = Tools.fieldsByName(parseIdentifiers(ctx));
parse(ctx, ')');
Condition condition = parseKeywordIf(ctx, "WHERE")
? parseCondition(ctx)
: null;
CreateIndexStep s1 = ifNotExists
? ctx.dsl.createIndexIfNotExists(indexName)
: ctx.dsl.createIndex(indexName);
CreateIndexWhereStep s2 = s1.on(tableName, fieldNames);
CreateIndexFinalStep s3 = condition != null
? s2.where(condition)
: s2;
return s3;
}
private static final DDLQuery parseAlterIndex(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Name indexName = parseIndexName(ctx);
parseKeyword(ctx, "RENAME TO");
Name newName = parseIndexName(ctx);
AlterIndexStep s1 = ifExists
? ctx.dsl.alterIndexIfExists(indexName)
: ctx.dsl.alterIndex(indexName);
AlterIndexFinalStep s2 = s1.renameTo(newName);
return s2;
}
private static final DDLQuery parseDropIndex(ParserContext ctx) {
boolean ifExists = parseKeywordIf(ctx, "IF EXISTS");
Name indexName = parseIndexName(ctx);
boolean on = parseKeywordIf(ctx, "ON");
Table> onTable = on ? parseTableName(ctx) : null;
DropIndexOnStep s1;
DropIndexFinalStep s2;
s1 = ifExists
? ctx.dsl.dropIndexIfExists(indexName)
: ctx.dsl.dropIndex(indexName);
s2 = on
? s1.on(onTable)
: s1;
return s2;
}
// -----------------------------------------------------------------------------------------------------------------
// QueryPart parsing
// -----------------------------------------------------------------------------------------------------------------
static final Condition parseCondition(ParserContext ctx) {
Condition condition = parseBooleanTerm(ctx);
while (parseKeywordIf(ctx, "OR"))
condition = condition.or(parseBooleanTerm(ctx));
return condition;
}
private static final Condition parseBooleanTerm(ParserContext ctx) {
Condition condition = parseBooleanFactor(ctx);
while (parseKeywordIf(ctx, "AND"))
condition = condition.and(parseBooleanFactor(ctx));
return condition;
}
private static final Condition parseBooleanFactor(ParserContext ctx) {
boolean not = parseKeywordIf(ctx, "NOT");
Condition condition = parseBooleanTest(ctx);
return not ? condition.not() : condition;
}
private static final Condition parseBooleanTest(ParserContext ctx) {
Condition condition = parseBooleanPrimary(ctx);
if (parseKeywordIf(ctx, "IS")) {
Field field = field(condition);
boolean not = parseKeywordIf(ctx, "NOT");
TruthValue truth = parseTruthValue(ctx);
switch (truth) {
case FALSE:
return not ? field.ne(inline(false)) : field.eq(inline(false));
case TRUE:
return not ? field.ne(inline(true)) : field.eq(inline(true));
case NULL:
return not ? field.isNotNull() : field.isNull();
default:
throw ctx.internalError();
}
}
return condition;
}
private static final Condition parseBooleanPrimary(ParserContext ctx) {
if (parseIf(ctx, '(')) {
Condition result = parseCondition(ctx);
parse(ctx, ')');
return result;
}
TruthValue truth = parseTruthValueIf(ctx);
if (truth != null) {
Comparator comp = parseComparatorIf(ctx);
switch (truth) {
case TRUE:
return comp == null ? condition(true) : inline(true).compare(comp, (Field) parseField(ctx));
case FALSE:
return comp == null ? condition(false) : inline(false).compare(comp, (Field) parseField(ctx));
case NULL:
return comp == null ? condition((Boolean) null) : inline((Boolean) null).compare(comp, (Field) parseField(ctx));
default:
throw ctx.exception();
}
}
return parsePredicate(ctx);
}
private static final Condition parsePredicate(ParserContext ctx) {
if (parseKeywordIf(ctx, "EXISTS")) {
parse(ctx, '(');
Select> select = parseSelect(ctx);
parse(ctx, ')');
return exists(select);
}
else {
// TODO row value expressions
Field left;
Comparator comp;
boolean not;
left = parseFieldConcat(ctx, null);
not = parseKeywordIf(ctx, "NOT");
if (!not && (comp = parseComparatorIf(ctx)) != null) {
boolean all = parseKeywordIf(ctx, "ALL");
boolean any = !all && (parseKeywordIf(ctx, "ANY") || parseKeywordIf(ctx, "SOME"));
if (all || any)
parse(ctx, '(');
Condition result =
all
? left.compare(comp, DSL.all(parseSelect(ctx)))
: any
? left.compare(comp, DSL.any(parseSelect(ctx)))
: left.compare(comp, parseFieldConcat(ctx, null));
if (all || any)
parse(ctx, ')');
return result;
}
else if (!not && parseKeywordIf(ctx, "IS")) {
not = parseKeywordIf(ctx, "NOT");
if (parseKeywordIf(ctx, "NULL"))
return not ? left.isNotNull() : left.isNull();
parseKeyword(ctx, "DISTINCT FROM");
Field right = parseFieldConcat(ctx, null);
return not ? left.isNotDistinctFrom(right) : left.isDistinctFrom(right);
}
else if (parseKeywordIf(ctx, "IN")) {
Condition result;
parse(ctx, '(');
if (peekKeyword(ctx, "SELECT"))
result = not ? left.notIn(parseSelect(ctx)) : left.in(parseSelect(ctx));
else
result = not ? left.notIn(parseFields(ctx)) : left.in(parseFields(ctx));
parse(ctx, ')');
return result;
}
else if (parseKeywordIf(ctx, "BETWEEN")) {
boolean symmetric = parseKeywordIf(ctx, "SYMMETRIC");
Field r1 = parseFieldConcat(ctx, null);
parseKeyword(ctx, "AND");
Field r2 = parseFieldConcat(ctx, null);
return symmetric
? not
? left.notBetweenSymmetric(r1, r2)
: left.betweenSymmetric(r1, r2)
: not
? left.notBetween(r1, r2)
: left.between(r1, r2);
}
else if (parseKeywordIf(ctx, "LIKE")) {
Field right = parseFieldConcat(ctx, null);
boolean escape = parseKeywordIf(ctx, "ESCAPE");
char character = escape ? parseCharacterLiteral(ctx) : ' ';
return escape
? not
? left.notLike(right, character)
: left.like(right, character)
: not
? left.notLike(right)
: left.like(right);
}
}
throw ctx.exception();
}
private static final List> parseTables(ParserContext ctx) {
parseWhitespaceIf(ctx);
List> result = new ArrayList>();
do {
result.add(parseTable(ctx));
}
while (parseIf(ctx, ','));
return result;
}
private static final Table> parseTable(ParserContext ctx) {
Table> result = parseTableFactor(ctx);
for (;;) {
Table> joined = parseJoinedTableIf(ctx, result);
if (joined == null)
return result;
else
result = joined;
}
}
private static final Table> parseTableFactor(ParserContext ctx) {
return parseTablePrimary(ctx);
// TODO Support SAMPLE clause
}
private static final Table> parseTablePrimary(ParserContext ctx) {
Table> result = null;
// TODO [#5306] Support FINAL TABLE ()
if (parseKeywordIf(ctx, "LATERAL")) {
parse(ctx, '(');
result = lateral(parseSelect(ctx));
parse(ctx, ')');
}
else if (parseKeywordIf(ctx, "UNNEST")) {
// TODO
throw ctx.exception();
}
else if (parseIf(ctx, '(')) {
if (peekKeyword(ctx, "SELECT")) {
result = table(parseSelect(ctx));
parse(ctx, ')');
}
else {
int parens = 0;
while (parseIf(ctx, '('))
parens++;
result = parseJoinedTable(ctx);
while (parens --> 0)
parse(ctx, ')');
parse(ctx, ')');
return result;
}
}
else {
result = parseTableName(ctx);
}
String alias = null;
List columnAliases = null;
if (parseKeywordIf(ctx, "AS"))
alias = parseIdentifier(ctx);
else if (!peekKeyword(ctx, SELECT_KEYWORDS))
alias = parseIdentifierIf(ctx);
if (alias != null) {
if (parseIf(ctx, '(')) {
columnAliases = parseIdentifiers(ctx);
parse(ctx, ')');
}
if (columnAliases != null)
result = result.as(alias, columnAliases.toArray(EMPTY_STRING));
else
result = result.as(alias);
}
return result;
}
private static final Table> parseJoinedTable(ParserContext ctx) {
Table> result = parseTableFactor(ctx);
for (int i = 0;; i++) {
Table> joined = parseJoinedTableIf(ctx, result);
if (joined == null)
if (i == 0)
ctx.unexpectedToken();
else
return result;
else
result = joined;
}
}
private static final Table> parseJoinedTableIf(ParserContext ctx, Table> left) {
JoinType joinType = parseJoinTypeIf(ctx);
if (joinType == null)
return null;
Table> right = parseTableFactor(ctx);
TableOptionalOnStep> result1 = left.join(right, joinType);
Table> result2 = result1;
switch (joinType) {
case JOIN:
case LEFT_OUTER_JOIN:
case FULL_OUTER_JOIN:
case RIGHT_OUTER_JOIN:
case OUTER_APPLY: {
boolean on = parseKeywordIf(ctx, "ON");
if (on) {
result2 = result1.on(parseCondition(ctx));
}
else {
parseKeyword(ctx, "USING");
parse(ctx, '(');
result2 = result1.using(Tools.fieldsByName(parseIdentifiers(ctx).toArray(EMPTY_STRING)));
parse(ctx, ')');
}
break;
}
}
return result2;
}
private static final List> parseSelectList(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (parseIf(ctx, '*'))
return Collections.emptyList();
// TODO Support qualified asterisk
List> result = new ArrayList>();
do {
Field> field = parseField(ctx);
String alias = null;
if (parseKeywordIf(ctx, "AS"))
alias = parseIdentifier(ctx);
else if (!peekKeyword(ctx, SELECT_KEYWORDS))
alias = parseIdentifierIf(ctx);
result.add(alias == null ? field : field.as(alias));
}
while (parseIf(ctx, ','));
return result;
}
private static final List> parseSortSpecification(ParserContext ctx) {
List> result = new ArrayList>();
do {
Field> field = parseField(ctx);
SortField> sort;
if (parseKeywordIf(ctx, "DESC"))
sort = field.desc();
else if (parseKeywordIf(ctx, "ASC") || true)
sort = field.asc();
if (parseKeywordIf(ctx, "NULLS FIRST"))
sort = sort.nullsFirst();
else if (parseKeywordIf(ctx, "NULLS LAST"))
sort = sort.nullsLast();
result.add(sort);
}
while (parseIf(ctx, ','));
return result;
}
private static final List> parseFields(ParserContext ctx) {
parseWhitespaceIf(ctx);
List> result = new ArrayList>();
do {
result.add(parseField(ctx));
}
while (parseIf(ctx, ','));
return result;
}
static final Field> parseField(ParserContext ctx) {
return parseField(ctx, null);
}
static enum Type {
D,
S,
N,
B;
boolean is(Type type) {
return type == null || type == this;
}
}
private static final Field> parseField(ParserContext ctx, Type type) {
if (B.is(type)) {
Field> r = parseFieldAnd(ctx);
Condition c = null;
while (parseKeywordIf(ctx, "OR"))
c = ((c == null) ? condition((Field) r) : c).or((Field) parseFieldAnd(ctx));
return c == null ? r : field(c);
}
else {
return parseFieldConcat(ctx, type);
}
}
private static final Field> parseFieldConcat(ParserContext ctx, Type type) {
Field> r = parseFieldSum(ctx, type);
if (S.is(type))
while (parseIf(ctx, "||"))
r = concat(r, parseFieldSum(ctx, type));
return r;
}
private static final Field> parseFieldSumParenthesised(ParserContext ctx) {
parse(ctx, '(');
Field> r = parseFieldSum(ctx, N);
parse(ctx, ')');
return r;
}
private static final Field> parseFieldSum(ParserContext ctx, Type type) {
Field> r = parseFieldFactor(ctx, type);
if (N.is(type))
for (;;)
if (parseIf(ctx, '+'))
r = r.add(parseFieldFactor(ctx, type));
else if (parseIf(ctx, '-'))
r = r.sub(parseFieldFactor(ctx, type));
else
break;
return r;
}
private static final Field> parseFieldFactor(ParserContext ctx, Type type) {
Field> r = parseFieldTerm(ctx, type);
if (N.is(type))
for (;;)
if (parseIf(ctx, '*'))
r = r.mul((Field) parseFieldTerm(ctx, type));
else if (parseIf(ctx, '/'))
r = r.div((Field) parseFieldTerm(ctx, type));
else if (parseIf(ctx, '%'))
r = r.mod((Field) parseFieldTerm(ctx, type));
else
break;
return r;
}
private static final Field> parseFieldAnd(ParserContext ctx) {
Field> r = parseFieldCondition(ctx);
Condition c = null;
while (parseKeywordIf(ctx, "AND"))
c = ((c == null) ? condition((Field) r) : c).and((Field) parseFieldCondition(ctx));
return c == null ? r : field(c);
}
private static final Field> parseFieldCondition(ParserContext ctx) {
// TODO all predicates also as fields
return parseFieldConcat(ctx, null);
}
private static final Field> parseFieldTerm(ParserContext ctx, Type type) {
parseWhitespaceIf(ctx);
Field> field;
switch (ctx.character()) {
case ':':
case '?':
return parseBindVariable(ctx);
case '\'':
return inline(parseStringLiteral(ctx));
case 'a':
case 'A':
if (N.is(type))
if ((field = parseFieldAsciiIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "ACOS"))
return acos((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "ASIN"))
return asin((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "ATAN"))
return atan((Field) parseFieldSumParenthesised(ctx));
else if ((field = parseFieldAtan2If(ctx)) != null)
return field;
break;
case 'b':
case 'B':
if (N.is(type))
if ((field = parseFieldBitLengthIf(ctx)) != null)
return field;
break;
case 'c':
case 'C':
if (S.is(type))
if ((field = parseFieldConcatIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "CURRENT_SCHEMA"))
return currentSchema();
else if (parseKeywordIf(ctx, "CURRENT_USER"))
return currentUser();
if (N.is(type))
if ((field = parseFieldCharIndexIf(ctx)) != null)
return field;
else if ((field = parseFieldCharLengthIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "CEILING") || parseKeywordIf(ctx, "CEIL"))
return ceil((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "COSH"))
return cosh((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "COS"))
return cos((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "COTH"))
return coth((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "COT"))
return cot((Field) parseFieldSumParenthesised(ctx));
if (D.is(type))
if (parseKeywordIf(ctx, "CURRENT_TIMESTAMP"))
return currentTimestamp();
else if (parseKeywordIf(ctx, "CURRENT_TIME"))
return currentTime();
else if (parseKeywordIf(ctx, "CURRENT_DATE"))
return currentDate();
if ((field = parseFieldCaseIf(ctx)) != null)
return field;
else if ((field = parseCastIf(ctx)) != null)
return field;
else if ((field = parseFieldCoalesceIf(ctx)) != null)
return field;
else if ((field = parseFieldCumeDistIf(ctx)) != null)
return field;
break;
case 'd':
case 'D':
if (D.is(type))
if ((field = parseFieldDateLiteralIf(ctx)) != null)
return field;
if (N.is(type))
if ((field = parseFieldDenseRankIf(ctx)) != null)
return field;
else if ((field = parseFieldDayIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "DEGREE") || parseKeywordIf(ctx, "DEG"))
return deg((Field) parseFieldSumParenthesised(ctx));
break;
case 'e':
case 'E':
if (N.is(type))
if ((field = parseFieldExtractIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "EXP"))
return exp((Field) parseFieldSumParenthesised(ctx));
break;
case 'f':
case 'F':
if (N.is(type))
if (parseKeywordIf(ctx, "FLOOR"))
return floor((Field) parseFieldSumParenthesised(ctx));
if ((field = parseFieldFirstValueIf(ctx)) != null)
return field;
break;
case 'g':
case 'G':
if ((field = parseFieldGreatestIf(ctx)) != null)
return field;
else if (N.is(type) && (field = parseFieldGroupingIdIf(ctx)) != null)
return field;
else if (N.is(type) && (field = parseFieldGroupingIf(ctx)) != null)
return field;
else
break;
case 'h':
case 'H':
if (N.is(type))
if ((field = parseFieldHourIf(ctx)) != null)
return field;
break;
case 'i':
case 'I':
if (N.is(type) && (field = parseFieldInstrIf(ctx)) != null)
return field;
else if ((field = parseFieldIfnullIf(ctx)) != null)
return field;
else if ((field = parseFieldIsnullIf(ctx)) != null)
return field;
else
break;
case 'l':
case 'L':
if (S.is(type))
if ((field = parseFieldLowerIf(ctx)) != null)
return field;
else if ((field = parseFieldLpadIf(ctx)) != null)
return field;
else if ((field = parseFieldLtrimIf(ctx)) != null)
return field;
else if ((field = parseFieldLeftIf(ctx)) != null)
return field;
if (N.is(type))
if ((field = parseFieldLengthIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "LN"))
return ln((Field) parseFieldSumParenthesised(ctx));
else if ((field = parseFieldLogIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "LEVEL"))
return level();
if ((field = parseFieldLeastIf(ctx)) != null)
return field;
else if ((field = parseFieldLeadLagIf(ctx)) != null)
return field;
else if ((field = parseFieldLastValueIf(ctx)) != null)
return field;
break;
case 'm':
case 'M':
if (N.is(type))
if ((field = parseFieldModIf(ctx)) != null)
return field;
else if ((field = parseFieldMonthIf(ctx)) != null)
return field;
else if ((field = parseFieldMinuteIf(ctx)) != null)
return field;
if (S.is(type))
if ((field = parseFieldMidIf(ctx)) != null)
return field;
else if ((field = parseFieldMd5If(ctx)) != null)
return field;
break;
case 'n':
case 'N':
if ((field = parseFieldNvl2If(ctx)) != null)
return field;
else if ((field = parseFieldNvlIf(ctx)) != null)
return field;
else if ((field = parseFieldNullifIf(ctx)) != null)
return field;
else if ((field = parseFieldNtileIf(ctx)) != null)
return field;
else if ((field = parseFieldNthValueIf(ctx)) != null)
return field;
break;
case 'o':
case 'O':
if (N.is(type))
if ((field = parseFieldOctetLengthIf(ctx)) != null)
return field;
break;
case 'p':
case 'P':
if (N.is(type))
if ((field = parseFieldPositionIf(ctx)) != null)
return field;
else if ((field = parseFieldPercentRankIf(ctx)) != null)
return field;
else if ((field = parseFieldPowerIf(ctx)) != null)
return field;
if (parseKeywordIf(ctx, "PRIOR"))
return prior(parseField(ctx));
break;
case 'r':
case 'R':
if (S.is(type))
if ((field = parseFieldReplaceIf(ctx)) != null)
return field;
else if ((field = parseFieldRepeatIf(ctx)) != null)
return field;
else if ((field = parseFieldReverseIf(ctx)) != null)
return field;
else if ((field = parseFieldRpadIf(ctx)) != null)
return field;
else if ((field = parseFieldRtrimIf(ctx)) != null)
return field;
else if ((field = parseFieldRightIf(ctx)) != null)
return field;
if (N.is(type))
if ((field = parseFieldRowNumberIf(ctx)) != null)
return field;
else if ((field = parseFieldRankIf(ctx)) != null)
return field;
else if ((field = parseFieldRoundIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "ROWNUM"))
return rownum();
else if (parseKeywordIf(ctx, "RADIAN") || parseKeywordIf(ctx, "RAD"))
return rad((Field) parseFieldSumParenthesised(ctx));
break;
case 's':
case 'S':
if (S.is(type))
if ((field = parseFieldSubstringIf(ctx)) != null)
return field;
else if ((field = parseFieldSpaceIf(ctx)) != null)
return field;
if (N.is(type))
if ((field = parseFieldSecondIf(ctx)) != null)
return field;
else if ((field = parseFieldSignIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "SQRT") || parseKeywordIf(ctx, "SQR"))
return sqrt((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "SINH"))
return sinh((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "SIN"))
return sin((Field) parseFieldSumParenthesised(ctx));
break;
case 't':
case 'T':
if (S.is(type))
if ((field = parseFieldTrimIf(ctx)) != null)
return field;
if (N.is(type))
if ((field = parseFieldTruncIf(ctx)) != null)
return field;
else if (parseKeywordIf(ctx, "TANH"))
return tanh((Field) parseFieldSumParenthesised(ctx));
else if (parseKeywordIf(ctx, "TAN"))
return tan((Field) parseFieldSumParenthesised(ctx));
if (D.is(type))
if ((field = parseFieldTimestampLiteralIf(ctx)) != null)
return field;
else if ((field = parseFieldTimeLiteralIf(ctx)) != null)
return field;
break;
case 'u':
case 'U':
if (S.is(type))
if ((field = parseFieldUpperIf(ctx)) != null)
return field;
break;
case 'y':
case 'Y':
if (N.is(type))
if ((field = parseFieldYearIf(ctx)) != null)
return field;
break;
case '+':
parse(ctx, '+');
if (N.is(type))
return parseFieldTerm(ctx, type);
else
break;
case '-':
parse(ctx, '-');
if (N.is(type))
if ((field = parseFieldUnsignedNumericLiteralIf(ctx, true)) != null)
return field;
else
return parseFieldTerm(ctx, type).neg();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
if (N.is(type))
if ((field = parseFieldUnsignedNumericLiteralIf(ctx, false)) != null)
return field;
break;
case '(':
parse(ctx, '(');
if (peekKeyword(ctx, "SELECT")) {
SelectQueryImpl select = parseSelect(ctx);
if (select.getSelect().size() > 1)
throw ctx.exception();
field = field((Select) select);
parse(ctx, ')');
return field;
}
else {
Field> r = parseField(ctx, type);
parse(ctx, ')');
return r;
}
}
if ((field = parseAggregateFunctionIf(ctx)) != null)
return field;
else if ((field = parseBooleanValueExpressionIf(ctx)) != null)
return field;
else
return parseFieldName(ctx);
}
private static final Field> parseFieldAtan2If(ParserContext ctx) {
if (parseKeywordIf(ctx, "ATN2") || parseKeywordIf(ctx, "ATAN2")) {
parse(ctx, '(');
Field> x = parseFieldSum(ctx, N);
parse(ctx, ',');
Field> y = parseFieldSum(ctx, N);
parse(ctx, ')');
return atan2((Field) x, (Field) y);
}
return null;
}
private static final Field> parseFieldLogIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LOG")) {
parse(ctx, '(');
Field> arg1 = parseFieldSum(ctx, N);
parse(ctx, ',');
long arg2 = parseUnsignedInteger(ctx);
parse(ctx, ')');
return log((Field) arg1, (int) arg2);
}
return null;
}
private static final Field> parseFieldTruncIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "TRUNC")) {
parse(ctx, '(');
Field> arg1 = parseFieldSum(ctx, N);
parse(ctx, ',');
Field> arg2 = parseFieldSum(ctx, N);
parse(ctx, ')');
return DSL.trunc((Field) arg1, (Field) arg2);
}
return null;
}
private static final Field> parseFieldRoundIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "ROUND")) {
Field> arg1 = null;
Integer arg2 = null;
parse(ctx, '(');
arg1 = parseFieldSum(ctx, N);
if (parseIf(ctx, ','))
arg2 = (int) (long) parseUnsignedInteger(ctx);
parse(ctx, ')');
return arg2 == null ? round((Field) arg1) : round((Field) arg1, arg2);
}
return null;
}
private static final Field> parseFieldPowerIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "POWER") || parseKeywordIf(ctx, "POW")) {
parse(ctx, '(');
Field> arg1 = parseFieldSum(ctx, N);
parse(ctx, ',');
Field> arg2 = parseFieldSum(ctx, N);
parse(ctx, ')');
return DSL.power((Field) arg1, (Field) arg2);
}
return null;
}
private static final Field> parseFieldModIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "MOD")) {
parse(ctx, '(');
Field> f1 = parseField(ctx, N);
parse(ctx, ',');
Field> f2 = parseField(ctx, N);
parse(ctx, ')');
return f1.mod((Field) f2);
}
return null;
}
private static Field> parseFieldLeastIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LEAST")) {
parse(ctx, '(');
List> fields = parseFields(ctx);
parse(ctx, ')');
return least(fields.get(0), fields.size() > 1 ? fields.subList(1, fields.size()).toArray(EMPTY_FIELD) : EMPTY_FIELD);
}
return null;
}
private static Field> parseFieldGreatestIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "GREATEST")) {
parse(ctx, '(');
List> fields = parseFields(ctx);
parse(ctx, ')');
return greatest(fields.get(0), fields.size() > 1 ? fields.subList(1, fields.size()).toArray(EMPTY_FIELD) : EMPTY_FIELD);
}
return null;
}
private static final Field> parseFieldGroupingIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "GROUPING")) {
parse(ctx, '(');
Field> field = parseField(ctx);
parse(ctx, ')');
return grouping(field);
}
return null;
}
private static final Field> parseFieldGroupingIdIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "GROUPING_ID")) {
parse(ctx, '(');
List> fields = parseFields(ctx);
parse(ctx, ')');
return groupingId(fields.toArray(EMPTY_FIELD));
}
return null;
}
private static final Field> parseFieldTimestampLiteralIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "TIMESTAMP")) {
if (parseKeywordIf(ctx, "WITHOUT TIME ZONE")) {
return inline(parseTimestampLiteral(ctx));
}
else if (parseIf(ctx, '(')) {
Field> f = parseField(ctx, S);
parse(ctx, ')');
return timestamp((Field) f);
}
else {
return inline(parseTimestampLiteral(ctx));
}
}
return null;
}
private static final Timestamp parseTimestampLiteral(ParserContext ctx) {
try {
return Timestamp.valueOf(parseStringLiteral(ctx));
}
catch (IllegalArgumentException e) {
throw ctx.exception();
}
}
private static final Field> parseFieldTimeLiteralIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "TIME")) {
if (parseKeywordIf(ctx, "WITHOUT TIME ZONE")) {
return inline(parseTimeLiteral(ctx));
}
else if (parseIf(ctx, '(')) {
Field> f = parseField(ctx, S);
parse(ctx, ')');
return time((Field) f);
}
else {
return inline(parseTimeLiteral(ctx));
}
}
return null;
}
private static final Time parseTimeLiteral(ParserContext ctx) {
try {
return Time.valueOf(parseStringLiteral(ctx));
}
catch (IllegalArgumentException e) {
throw ctx.exception();
}
}
private static final Field> parseFieldDateLiteralIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "DATE")) {
if (parseIf(ctx, '(')) {
Field> f = parseField(ctx, S);
parse(ctx, ')');
return date((Field) f);
}
else {
return inline(parseDateLiteral(ctx));
}
}
return null;
}
private static final Date parseDateLiteral(ParserContext ctx) {
try {
return Date.valueOf(parseStringLiteral(ctx));
}
catch (IllegalArgumentException e) {
throw ctx.exception();
}
}
private static final Field> parseFieldExtractIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "EXTRACT")) {
parse(ctx, '(');
DatePart part = parseDatePart(ctx);
parseKeyword(ctx, "FROM");
Field> field = parseField(ctx);
parse(ctx, ')');
return extract(field, part);
}
return null;
}
private static final DatePart parseDatePart(ParserContext ctx) {
for (DatePart part : DatePart.values())
if (parseKeywordIf(ctx, part.name()))
return part;
throw ctx.unexpectedToken();
}
private static final Field> parseFieldAsciiIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "ASCII")) {
parse(ctx, '(');
Field> arg = parseField(ctx, S);
parse(ctx, ')');
return ascii((Field) arg);
}
return null;
}
private static final Field> parseFieldConcatIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CONCAT")) {
parse(ctx, '(');
Field result = concat(parseFields(ctx).toArray(EMPTY_FIELD));
parse(ctx, ')');
return result;
}
return null;
}
private static final Field> parseFieldInstrIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "INSTR")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field f2 = (Field) parseField(ctx, S);
parse(ctx, ')');
return position(f1, f2);
}
return null;
}
private static final Field> parseFieldCharIndexIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CHARINDEX")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field f2 = (Field) parseField(ctx, S);
parse(ctx, ')');
return position(f2, f1);
}
return null;
}
private static final Field> parseFieldLpadIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LPAD")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field f2 = (Field) parseField(ctx, N);
Field f3 = parseIf(ctx, ',')
? (Field) parseField(ctx, S)
: null;
parse(ctx, ')');
return f3 == null
? lpad(f1, f2)
: lpad(f1, f2, f3);
}
return null;
}
private static final Field> parseFieldRpadIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "RPAD")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field f2 = (Field) parseField(ctx, N);
Field f3 = parseIf(ctx, ',')
? (Field) parseField(ctx, S)
: null;
parse(ctx, ')');
return f3 == null
? rpad(f1, f2)
: rpad(f1, f2, f3);
}
return null;
}
private static final Field> parseFieldPositionIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "POSITION")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parseKeyword(ctx, "IN");
Field f2 = (Field) parseField(ctx, S);
parse(ctx, ')');
return position(f2, f1);
}
return null;
}
private static final Field> parseFieldRepeatIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "REPEAT")) {
parse(ctx, '(');
Field field = (Field) parseField(ctx, S);
parse(ctx, ',');
Field count = (Field) parseField(ctx, N);
parse(ctx, ')');
return repeat(field, count);
}
return null;
}
private static final Field> parseFieldReplaceIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "REPLACE")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field f2 = (Field) parseField(ctx, S);
Field f3 = parseIf(ctx, ',')
? (Field) parseField(ctx, S)
: null;
parse(ctx, ')');
return f3 == null
? replace(f1, f2)
: replace(f1, f2, f3);
}
return null;
}
private static final Field> parseFieldReverseIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "REVERSE")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return reverse(f1);
}
return null;
}
private static final Field> parseFieldSpaceIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "SPACE")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, N);
parse(ctx, ')');
return space(f1);
}
return null;
}
private static final Field> parseFieldSubstringIf(ParserContext ctx) {
boolean substring = parseKeywordIf(ctx, "SUBSTRING");
boolean substr = !substring && parseKeywordIf(ctx, "SUBSTR");
if (substring || substr) {
boolean keywords = !substr;
parse(ctx, '(');
Field f1 = (Field) parseFieldConcat(ctx, S);
if (substr || !(keywords = parseKeywordIf(ctx, "FROM")))
parse(ctx, ',');
Field> f2 = parseFieldSum(ctx, N);
Field> f3 =
((keywords && parseKeywordIf(ctx, "FOR")) || (!keywords && parseIf(ctx, ',')))
? parseFieldSum(ctx, N)
: null;
parse(ctx, ')');
return f3 == null
? substring(f1, (Field) f2)
: substring(f1, (Field) f2, (Field) f3);
}
return null;
}
private static final Field> parseFieldTrimIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "TRIM")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return trim(f1);
}
return null;
}
private static final Field> parseFieldRtrimIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "RTRIM")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return rtrim(f1);
}
return null;
}
private static final Field> parseFieldLtrimIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LTRIM")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return ltrim(f1);
}
return null;
}
private static final Field> parseFieldMidIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "MID")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field extends Number> f2 = (Field) parseField(ctx, N);
parse(ctx, ',');
Field extends Number> f3 = (Field) parseField(ctx, N);
parse(ctx, ')');
return mid(f1, f2, f3);
}
return null;
}
private static final Field> parseFieldLeftIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LEFT")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field extends Number> f2 = (Field) parseField(ctx, N);
parse(ctx, ')');
return left(f1, f2);
}
return null;
}
private static final Field> parseFieldRightIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "RIGHT")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ',');
Field extends Number> f2 = (Field) parseField(ctx, N);
parse(ctx, ')');
return right(f1, f2);
}
return null;
}
private static final Field> parseFieldMd5If(ParserContext ctx) {
if (parseKeywordIf(ctx, "MD5")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return md5(f1);
}
return null;
}
private static final Field> parseFieldLengthIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LENGTH")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return length(f1);
}
return null;
}
private static final Field> parseFieldCharLengthIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CHAR_LENGTH")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return charLength(f1);
}
return null;
}
private static final Field> parseFieldBitLengthIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "BIT_LENGTH")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return bitLength(f1);
}
return null;
}
private static final Field> parseFieldOctetLengthIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "OCTET_LENGTH")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return octetLength(f1);
}
return null;
}
private static final Field> parseFieldLowerIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LOWER")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return lower(f1);
}
return null;
}
private static final Field> parseFieldUpperIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "UPPER")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, S);
parse(ctx, ')');
return DSL.upper(f1);
}
return null;
}
private static final Field> parseFieldYearIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "YEAR")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return year(f1);
}
return null;
}
private static final Field> parseFieldMonthIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "MONTH")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return month(f1);
}
return null;
}
private static final Field> parseFieldDayIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "DAY")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return day(f1);
}
return null;
}
private static final Field> parseFieldHourIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "HOUR")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return hour(f1);
}
return null;
}
private static final Field> parseFieldMinuteIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "MINUTE")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return minute(f1);
}
return null;
}
private static final Field> parseFieldSecondIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "SECOND")) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx, D);
parse(ctx, ')');
return second(f1);
}
return null;
}
private static final Field> parseFieldSignIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "SIGN")) {
parse(ctx, '(');
Field> f1 = parseField(ctx, N);
parse(ctx, ')');
return sign((Field) f1);
}
return null;
}
private static final Field> parseFieldIfnullIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "IFNULL")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
Field> f2 = parseField(ctx);
parse(ctx, ')');
return ifnull(f1, f2);
}
return null;
}
private static final Field> parseFieldIsnullIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "ISNULL")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
Field> f2 = parseField(ctx);
parse(ctx, ')');
return isnull(f1, f2);
}
return null;
}
private static final Field> parseFieldNvlIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "NVL")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
Field> f2 = parseField(ctx);
parse(ctx, ')');
return nvl(f1, f2);
}
return null;
}
private static final Field> parseFieldNvl2If(ParserContext ctx) {
if (parseKeywordIf(ctx, "NVL2")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
Field> f2 = parseField(ctx);
parse(ctx, ',');
Field> f3 = parseField(ctx);
parse(ctx, ')');
return nvl2(f1, f2, f3);
}
return null;
}
private static final Field> parseFieldNullifIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "NULLIF")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
Field> f2 = parseField(ctx);
parse(ctx, ')');
return nullif(f1, f2);
}
return null;
}
private static final Field> parseFieldCoalesceIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "COALESCE")) {
parse(ctx, '(');
List> fields = parseFields(ctx);
parse(ctx, ')');
Field[] a = EMPTY_FIELD;
return coalesce(fields.get(0), fields.size() == 1 ? a : fields.subList(1, fields.size()).toArray(a));
}
return null;
}
private static final Field> parseFieldCaseIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CASE")) {
if (parseKeywordIf(ctx, "WHEN")) {
CaseConditionStep step = null;
Field result;
do {
Condition condition = parseCondition(ctx);
parseKeyword(ctx, "THEN");
Field value = parseField(ctx);
step = step == null ? when(condition, value) : step.when(condition, value);
}
while (parseKeywordIf(ctx, "WHEN"));
if (parseKeywordIf(ctx, "ELSE"))
result = step.otherwise(parseField(ctx));
else
result = step;
parseKeyword(ctx, "END");
return result;
}
else {
CaseValueStep init = choose(parseField(ctx));
CaseWhenStep step = null;
Field result;
parseKeyword(ctx, "WHEN");
do {
Field when = parseField(ctx);
parseKeyword(ctx, "THEN");
Field then = parseField(ctx);
step = step == null ? init.when(when, then) : step.when(when, then);
}
while (parseKeywordIf(ctx, "WHEN"));
if (parseKeywordIf(ctx, "ELSE"))
result = step.otherwise(parseField(ctx));
else
result = step;
parseKeyword(ctx, "END");
return result;
}
}
return null;
}
private static final Field> parseCastIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CAST")) {
parse(ctx, '(');
Field> field = parseField(ctx);
parseKeyword(ctx, "AS");
DataType> type = parseDataType(ctx);
return cast(field, type);
}
return null;
}
private static final Field parseBooleanValueExpressionIf(ParserContext ctx) {
TruthValue truth = parseTruthValueIf(ctx);
if (truth != null) {
switch (truth) {
case TRUE:
return inline(true);
case FALSE:
return inline(false);
case NULL:
return inline((Boolean) null);
default:
throw ctx.exception();
}
}
return null;
}
private static final Field> parseAggregateFunctionIf(ParserContext ctx) {
AggregateFunction> agg;
WindowBeforeOverStep> over;
Field> result;
Condition filter;
agg = parseCountIf(ctx);
if (agg == null)
agg = parseGeneralSetFunctionIf(ctx);
if (agg == null)
agg = parseBinarySetFunctionIf(ctx);
if (agg == null)
return null;
if (parseKeywordIf(ctx, "FILTER")) {
parse(ctx, '(');
parseKeyword(ctx, "WHERE");
filter = parseCondition(ctx);
parse(ctx, ')');
result = over = agg.filterWhere(filter);
}
else {
result = over = agg;
}
// TODO parse WITHIN GROUP (ORDER BY) where applicable
if (parseKeywordIf(ctx, "OVER")) {
Object nameOrSpecification = parseWindowNameOrSpecification(ctx);
if (nameOrSpecification instanceof Name)
result = over.over((Name) nameOrSpecification);
else if (nameOrSpecification instanceof WindowSpecification)
result = over.over((WindowSpecification) nameOrSpecification);
else
result = over.over();
}
return result;
}
private static Object parseWindowNameOrSpecification(ParserContext ctx) {
Object result;
if (parseIf(ctx, '(')) {
WindowSpecificationOrderByStep s1 = null;
WindowSpecificationRowsStep s2 = null;
WindowSpecificationRowsAndStep s3 = null;
s1 = parseKeywordIf(ctx, "PARTITION BY")
? partitionBy(parseFields(ctx))
: null;
s2 = parseKeywordIf(ctx, "ORDER BY")
? s1 == null
? orderBy(parseSortSpecification(ctx))
: s1.orderBy(parseSortSpecification(ctx))
: s1;
boolean rows = parseKeywordIf(ctx, "ROWS");
if (rows || parseKeywordIf(ctx, "RANGE")) {
if (parseKeywordIf(ctx, "BETWEEN")) {
if (parseKeywordIf(ctx, "UNBOUNDED")) {
if (parseKeywordIf(ctx, "PRECEDING")) {
s3 = s2 == null
? rows
? rowsBetweenUnboundedPreceding()
: rangeBetweenUnboundedPreceding()
: rows
? s2.rowsBetweenUnboundedPreceding()
: s2.rangeBetweenUnboundedPreceding();
}
else {
parseKeyword(ctx, "FOLLOWING");
s3 = s2 == null
? rows
? rowsBetweenUnboundedFollowing()
: rangeBetweenUnboundedFollowing()
: rows
? s2.rowsBetweenUnboundedFollowing()
: s2.rangeBetweenUnboundedFollowing();
}
}
else if (parseKeywordIf(ctx, "CURRENT ROW")) {
s3 = s2 == null
? rows
? rowsBetweenCurrentRow()
: rangeBetweenCurrentRow()
: rows
? s2.rowsBetweenCurrentRow()
: s2.rangeBetweenCurrentRow();
}
else {
int number = (int) (long) parseUnsignedInteger(ctx);
if (parseKeywordIf(ctx, "PRECEDING")) {
s3 = s2 == null
? rows
? rowsBetweenPreceding(number)
: rangeBetweenPreceding(number)
: rows
? s2.rowsBetweenPreceding(number)
: s2.rangeBetweenPreceding(number);
}
else {
parseKeyword(ctx, "FOLLOWING");
s3 = s2 == null
? rows
? rowsBetweenFollowing(number)
: rangeBetweenFollowing(number)
: rows
? s2.rowsBetweenFollowing(number)
: s2.rangeBetweenFollowing(number);
}
}
parseKeyword(ctx, "AND");
if (parseKeywordIf(ctx, "UNBOUNDED")) {
if (parseKeywordIf(ctx, "PRECEDING")) {
result = s3.andUnboundedPreceding();
}
else {
parseKeyword(ctx, "FOLLOWING");
result = s3.andUnboundedFollowing();
}
}
else if (parseKeywordIf(ctx, "CURRENT ROW")) {
result = s3.andCurrentRow();
}
else {
int number = (int) (long) parseUnsignedInteger(ctx);
if (parseKeywordIf(ctx, "PRECEDING")) {
result = s3.andPreceding(number);
}
else {
parseKeyword(ctx, "FOLLOWING");
result = s3.andFollowing(number);
}
}
}
else {
if (parseKeywordIf(ctx, "UNBOUNDED")) {
if (parseKeywordIf(ctx, "PRECEDING")) {
result = s2 == null
? rows
? rowsUnboundedPreceding()
: rangeUnboundedPreceding()
: rows
? s2.rowsUnboundedPreceding()
: s2.rangeUnboundedPreceding();
}
else {
parseKeyword(ctx, "FOLLOWING");
result = s2 == null
? rows
? rowsUnboundedFollowing()
: rangeUnboundedFollowing()
: rows
? s2.rowsUnboundedFollowing()
: s2.rangeUnboundedFollowing();
}
}
else if (parseKeywordIf(ctx, "CURRENT ROW")) {
result = s2 == null
? rows
? rowsCurrentRow()
: rangeCurrentRow()
: rows
? s2.rowsCurrentRow()
: s2.rangeCurrentRow();
}
else {
int number = (int) (long) parseUnsignedInteger(ctx);
if (parseKeywordIf(ctx, "PRECEDING")) {
result = s2 == null
? rows
? rowsPreceding(number)
: rangePreceding(number)
: rows
? s2.rowsPreceding(number)
: s2.rangePreceding(number);
}
else {
parseKeyword(ctx, "FOLLOWING");
result = s2 == null
? rows
? rowsFollowing(number)
: rangeFollowing(number)
: rows
? s2.rowsFollowing(number)
: s2.rangeFollowing(number);
}
}
}
}
else {
result = s2;
}
parse(ctx, ')');
}
else {
result = name(parseIdentifier(ctx));
}
return result;
}
private static final Field> parseFieldRankIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "RANK")) {
parse(ctx, '(');
parse(ctx, ')');
return parseWindowFunction(ctx, null, rank());
}
return null;
}
private static final Field> parseFieldDenseRankIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "DENSE_RANK")) {
parse(ctx, '(');
parse(ctx, ')');
return parseWindowFunction(ctx, null, denseRank());
}
return null;
}
private static final Field> parseFieldPercentRankIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "PERCENT_RANK")) {
parse(ctx, '(');
parse(ctx, ')');
return parseWindowFunction(ctx, null, percentRank());
}
return null;
}
private static final Field> parseFieldCumeDistIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CUME_DIST")) {
parse(ctx, '(');
parse(ctx, ')');
return parseWindowFunction(ctx, null, cumeDist());
}
return null;
}
private static final Field> parseFieldRowNumberIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "ROW_NUMBER")) {
parse(ctx, '(');
parse(ctx, ')');
return parseWindowFunction(ctx, null, rowNumber());
}
return null;
}
private static final Field> parseFieldNtileIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "NTILE")) {
parse(ctx, '(');
int number = (int) (long) parseUnsignedInteger(ctx);
parse(ctx, ')');
return parseWindowFunction(ctx, null, ntile(number));
}
return null;
}
private static final Field> parseFieldLeadLagIf(ParserContext ctx) {
boolean lead = parseKeywordIf(ctx, "LEAD");
boolean lag = !lead && parseKeywordIf(ctx, "LAG");
if (lead || lag) {
parse(ctx, '(');
Field f1 = (Field) parseField(ctx);
Integer f2 = null;
Field f3 = null;
if (parseIf(ctx, ',')) {
f2 = (int) (long) parseUnsignedInteger(ctx);
if (parseIf(ctx, ',')) {
f3 = (Field) parseField(ctx);
}
}
parse(ctx, ')');
return parseWindowFunction(ctx, lead
? f2 == null
? lead(f1)
: f3 == null
? lead(f1, f2)
: lead(f1, f2, f3)
: f2 == null
? lag(f1)
: f3 == null
? lag(f1, f2)
: lag(f1, f2, f3), null);
}
return null;
}
private static final Field> parseFieldFirstValueIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "FIRST_VALUE")) {
parse(ctx, '(');
Field arg = (Field) parseField(ctx);
parse(ctx, ')');
return parseWindowFunction(ctx, firstValue(arg), null);
}
return null;
}
private static final Field> parseFieldLastValueIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "LAST_VALUE")) {
parse(ctx, '(');
Field arg = (Field) parseField(ctx);
parse(ctx, ')');
return parseWindowFunction(ctx, lastValue(arg), null);
}
return null;
}
private static final Field> parseFieldNthValueIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "NTH_VALUE")) {
parse(ctx, '(');
Field> f1 = parseField(ctx);
parse(ctx, ',');
int f2 = (int) (long) parseUnsignedInteger(ctx);
parse(ctx, ')');
return parseWindowFunction(ctx, nthValue(f1, f2), null);
}
return null;
}
private static final Field> parseWindowFunction(ParserContext ctx, WindowIgnoreNullsStep s1, WindowOverStep> s2) {
if (s1 != null) {
s2 = s1;
}
parseKeyword(ctx, "OVER");
Object nameOrSpecification = parseWindowNameOrSpecification(ctx);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=494897
Field> result = (nameOrSpecification instanceof Name)
? s2.over((Name) nameOrSpecification)
: (nameOrSpecification instanceof WindowSpecification)
? s2.over((WindowSpecification) nameOrSpecification)
: s2.over();
return result;
}
private static final AggregateFunction> parseBinarySetFunctionIf(ParserContext ctx) {
Field extends Number> arg1;
Field extends Number> arg2;
BinarySetFunctionType type = parseBinarySetFunctionTypeIf(ctx);
if (type == null)
return null;
parse(ctx, '(');
arg1 = (Field) parseFieldSum(ctx, N);
parse(ctx, ',');
arg2 = (Field) parseFieldSum(ctx, N);
parse(ctx, ')');
switch (type) {
case REGR_AVGX:
return regrAvgX(arg1, arg2);
case REGR_AVGY:
return regrAvgY(arg1, arg2);
case REGR_COUNT:
return regrCount(arg1, arg2);
case REGR_INTERCEPT:
return regrIntercept(arg1, arg2);
case REGR_R2:
return regrR2(arg1, arg2);
case REGR_SLOPE:
return regrSlope(arg1, arg2);
case REGR_SXX:
return regrSXX(arg1, arg2);
case REGR_SXY:
return regrSXY(arg1, arg2);
case REGR_SYY:
return regrSYY(arg1, arg2);
default:
throw ctx.exception();
}
}
private static final AggregateFunction> parseGeneralSetFunctionIf(ParserContext ctx) {
boolean distinct;
Field arg;
ComputationalOperation operation = parseComputationalOperationIf(ctx);
if (operation == null)
return null;
parse(ctx, '(');
distinct = parseSetQuantifier(ctx);
arg = parseField(ctx);
parse(ctx, ')');
switch (operation) {
case AVG:
return distinct ? avgDistinct(arg) : avg(arg);
case MAX:
return distinct ? maxDistinct(arg) : max(arg);
case MIN:
return distinct ? minDistinct(arg) : min(arg);
case SUM:
return distinct ? sumDistinct(arg) : sum(arg);
case EVERY:
return every(arg);
case ANY:
return boolOr(arg);
case STDDEV_POP:
return stddevPop(arg);
case STDDEV_SAMP:
return stddevSamp(arg);
case VAR_POP:
return varPop(arg);
case VAR_SAMP:
return varSamp(arg);
default:
throw ctx.unexpectedToken();
}
}
private static final AggregateFunction> parseCountIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "COUNT")) {
parse(ctx, '(');
if (parseIf(ctx, '*')) {
parse(ctx, ')');
return count();
}
boolean distinct = parseSetQuantifier(ctx);
List> fields = distinct
? parseFields(ctx)
: Collections.>singletonList(parseField(ctx));
parse(ctx, ')');
if (distinct)
if (fields.size() > 0)
return countDistinct(fields.toArray(EMPTY_FIELD));
else
return countDistinct(fields.get(0));
else
return count(fields.get(0));
}
return null;
}
private static final boolean parseSetQuantifier(ParserContext ctx) {
boolean distinct = parseKeywordIf(ctx, "DISTINCT");
if (!distinct)
parseKeywordIf(ctx, "ALL");
return distinct;
}
// -----------------------------------------------------------------------------------------------------------------
// Name parsing
// -----------------------------------------------------------------------------------------------------------------
private static final Schema parseSchemaName(ParserContext ctx) {
return schema(parseName(ctx));
}
private static final Table> parseTableName(ParserContext ctx) {
return table(parseName(ctx));
}
private static final TableField, ?> parseFieldName(ParserContext ctx) {
return (TableField, ?>) field(parseName(ctx));
}
private static final List> parseFieldNames(ParserContext ctx) {
List> result = new ArrayList>();
do {
result.add(parseFieldName(ctx));
}
while (parseIf(ctx, ','));
return result;
}
private static final Sequence> parseSequenceName(ParserContext ctx) {
return sequence(parseName(ctx));
}
private static final Name parseIndexName(ParserContext ctx) {
return parseName(ctx);
}
static final Name parseName(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (ctx.done())
throw ctx.exception();
List name = new ArrayList();
StringBuilder sb = new StringBuilder();
int i = ctx.position;
boolean identifierStart = true;
boolean identifierEnd = false;
for (;;) {
char c = ctx.character(i);
// TODO Quoted identifiers
if (c == '.') {
if (identifierStart)
throw new ParserException(ctx);
name.add(sb.toString());
sb = new StringBuilder();
identifierStart = true;
identifierEnd = false;
}
// Better way to distinguish identifier parts
else if (!identifierEnd && Character.isJavaIdentifierPart(c)) {
sb.append(c);
identifierStart = false;
}
else if (Character.isWhitespace(c)) {
identifierEnd = !identifierStart;
}
else {
name.add(sb.toString());
identifierEnd = !identifierStart;
break;
}
if (++i == ctx.sql.length) {
if (identifierStart)
throw ctx.exception();
name.add(sb.toString());
identifierEnd = !identifierStart;
break;
}
}
if (ctx.position == i)
throw ctx.exception();
ctx.position = i;
return DSL.name(name.toArray(EMPTY_STRING));
}
private static final List parseIdentifiers(ParserContext ctx) {
LinkedHashSet result = new LinkedHashSet();
do {
if (!result.add(parseIdentifier(ctx)))
throw ctx.exception();
}
while (parseIf(ctx, ','));
return new ArrayList(result);
}
private static final String parseIdentifier(ParserContext ctx) {
String alias = parseIdentifierIf(ctx);
if (alias == null)
throw ctx.exception();
return alias;
}
private static final String parseIdentifierIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
int start = ctx.position;
while (ctx.isIdentifierPart())
ctx.position = ctx.position + 1;
if (ctx.position == start)
return null;
return new String(ctx.sql, start, ctx.position - start);
}
private static final DataType> parseDataType(ParserContext ctx) {
parseWhitespaceIf(ctx);
switch (ctx.character()) {
case 'b':
case 'B':
if (parseKeywordIf(ctx, "BIGINT"))
return SQLDataType.BIGINT;
else if (parseKeywordIf(ctx, "BINARY"))
return parseDataTypeLength(ctx, SQLDataType.BINARY);
else if (parseKeywordIf(ctx, "BIT"))
return SQLDataType.BIT;
else if (parseKeywordIf(ctx, "BLOB"))
return SQLDataType.BLOB;
else if (parseKeywordIf(ctx, "BOOLEAN"))
return SQLDataType.BOOLEAN;
case 'c':
case 'C':
if (parseKeywordIf(ctx, "CHAR"))
return parseDataTypeLength(ctx, SQLDataType.CHAR);
else if (parseKeywordIf(ctx, "CLOB"))
return parseDataTypeLength(ctx, SQLDataType.CLOB);
case 'i':
case 'I':
if (parseKeywordIf(ctx, "INT") || parseKeywordIf(ctx, "INTEGER"))
return SQLDataType.INTEGER;
case 'v':
case 'V':
if (parseKeywordIf(ctx, "VARCHAR") || parseKeywordIf(ctx, "VARCHAR2") || parseKeywordIf(ctx, "CHARACTER VARYING"))
return parseDataTypeLength(ctx, SQLDataType.VARCHAR);
else if (parseKeywordIf(ctx, "VARBINARY"))
return parseDataTypeLength(ctx, SQLDataType.VARBINARY);
break;
}
throw ctx.unexpectedToken();
}
private static final DataType> parseDataTypeLength(ParserContext ctx, DataType> result) {
if (parseIf(ctx, '(')) {
result = result.length((int) (long) parseUnsignedInteger(ctx));
parse(ctx, ')');
}
return result;
}
// -----------------------------------------------------------------------------------------------------------------
// Literal parsing
// -----------------------------------------------------------------------------------------------------------------
static final char parseCharacterLiteral(ParserContext ctx) {
parseWhitespaceIf(ctx);
parse(ctx, '\'');
char c = ctx.character();
// TODO MySQL string escaping...
if (c == '\'')
parse(ctx, '\'');
ctx.position = ctx.position + 1;
parse(ctx, '\'');
return c;
}
static final Field> parseBindVariable(ParserContext ctx) {
switch (ctx.character()) {
case '?':
parse(ctx, '?');
return DSL.val(null, Object.class);
case ':':
parse(ctx, ':');
return DSL.param(parseIdentifier(ctx));
default:
throw ctx.exception();
}
}
static final String parseStringLiteral(ParserContext ctx) {
parseWhitespaceIf(ctx);
parse(ctx, '\'');
StringBuilder sb = new StringBuilder();
for (int i = ctx.position; i < ctx.sql.length; i++) {
char c = ctx.character(i);
// TODO MySQL string escaping...
if (c == '\'')
if (ctx.character(i + 1) == '\'') {
i++;
}
else {
ctx.position = i + 1;
return sb.toString();
}
sb.append(c);
}
throw ctx.exception();
}
private static final Field> parseFieldUnsignedNumericLiteralIf(ParserContext ctx, boolean minus) {
Number r = parseUnsignedNumericLiteralIf(ctx, minus);
return r == null ? null : inline(r);
}
private static final Number parseUnsignedNumericLiteralIf(ParserContext ctx, boolean minus) {
StringBuilder sb = new StringBuilder();
char c;
for (;;) {
c = ctx.character();
if (c >= '0' && c <= '9') {
sb.append(c);
ctx.position = ctx.position + 1;
}
else
break;
}
if (c == '.') {
sb.append(c);
ctx.position = ctx.position + 1;
}
else {
if (sb.length() == 0)
return null;
try {
return minus
? -Long.valueOf(sb.toString())
: Long.valueOf(sb.toString());
}
catch (Exception e1) {
return minus
? new BigInteger(sb.toString()).negate()
: new BigInteger(sb.toString());
}
}
for (;;) {
c = ctx.character();
if (c >= '0' && c <= '9') {
sb.append(c);
ctx.position = ctx.position + 1;
}
else
break;
}
if (sb.length() == 0)
return null;
return minus
? new BigDecimal(sb.toString()).negate()
: new BigDecimal(sb.toString());
// TODO add floating point support
}
private static final Long parseUnsignedInteger(ParserContext ctx) {
Long result = parseUnsignedIntegerIf(ctx);
if (result == null)
throw ctx.exception();
return result;
}
private static final Field> parseFieldUnsignedIntegerIf(ParserContext ctx, boolean minus) {
Long r = parseUnsignedIntegerIf(ctx);
return r == null
? null
: minus
? inline(-r)
: inline(r);
}
private static final Long parseUnsignedIntegerIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
StringBuilder sb = new StringBuilder();
char c;
for (;;) {
c = ctx.character();
if (c >= '0' && c <= '9') {
sb.append(c);
ctx.position = ctx.position + 1;
}
else
break;
}
if (sb.length() == 0)
return null;
return Long.valueOf(sb.toString());
}
private static final JoinType parseJoinType(ParserContext ctx) {
JoinType result = parseJoinTypeIf(ctx);
if (result == null)
ctx.unexpectedToken();
return result;
}
private static final JoinType parseJoinTypeIf(ParserContext ctx) {
if (parseKeywordIf(ctx, "CROSS JOIN"))
return JoinType.CROSS_JOIN;
else if (parseKeywordIf(ctx, "CROSS APPLY"))
return JoinType.CROSS_APPLY;
else if (parseKeywordIf(ctx, "CROSS JOIN"))
return JoinType.CROSS_JOIN;
else if (parseKeywordIf(ctx, "INNER")) {
parseKeyword(ctx, "JOIN");
return JoinType.JOIN;
}
else if (parseKeywordIf(ctx, "JOIN"))
return JoinType.JOIN;
else if (parseKeywordIf(ctx, "LEFT")) {
parseKeywordIf(ctx, "OUTER");
parseKeyword(ctx, "JOIN");
return JoinType.LEFT_OUTER_JOIN;
}
else if (parseKeywordIf(ctx, "RIGHT")) {
parseKeywordIf(ctx, "OUTER");
parseKeyword(ctx, "JOIN");
return JoinType.RIGHT_OUTER_JOIN;
}
else if (parseKeywordIf(ctx, "FULL OUTER JOIN"))
return JoinType.FULL_OUTER_JOIN;
else if (parseKeywordIf(ctx, "OUTER APPLY"))
return JoinType.OUTER_APPLY;
else if (parseKeywordIf(ctx, "NATURAL")) {
if (parseKeywordIf(ctx, "LEFT")) {
parseKeywordIf(ctx, "OUTER");
parseKeyword(ctx, "JOIN");
return JoinType.NATURAL_LEFT_OUTER_JOIN;
}
else if (parseKeywordIf(ctx, "RIGHT")) {
parseKeywordIf(ctx, "OUTER");
parseKeyword(ctx, "JOIN");
return JoinType.NATURAL_RIGHT_OUTER_JOIN;
}
else if (parseKeywordIf(ctx, "JOIN"))
return JoinType.NATURAL_JOIN;
}
return null;
// TODO partitioned join
}
static final TruthValue parseTruthValue(ParserContext ctx) {
TruthValue result = parseTruthValueIf(ctx);
if (result == null)
throw ctx.exception();
return result;
}
static final TruthValue parseTruthValueIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (parseKeywordIf(ctx, "TRUE"))
return TruthValue.TRUE;
else if (parseKeywordIf(ctx, "FALSE"))
return TruthValue.FALSE;
else if (parseKeywordIf(ctx, "NULL"))
return TruthValue.NULL;
return null;
}
private static final CombineOperator parseCombineOperatorIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (parseKeywordIf(ctx, "UNION"))
if (parseKeywordIf(ctx, "ALL"))
return CombineOperator.UNION_ALL;
else if (parseKeywordIf(ctx, "DISTINCT"))
return CombineOperator.UNION;
else
return CombineOperator.UNION;
else if (parseKeywordIf(ctx, "EXCEPT") || parseKeywordIf(ctx, "MINUS"))
if (parseKeywordIf(ctx, "ALL"))
return CombineOperator.EXCEPT_ALL;
else if (parseKeywordIf(ctx, "DISTINCT"))
return CombineOperator.EXCEPT;
else
return CombineOperator.EXCEPT;
else if (parseKeywordIf(ctx, "INTERSECT"))
if (parseKeywordIf(ctx, "ALL"))
return CombineOperator.INTERSECT_ALL;
else if (parseKeywordIf(ctx, "DISTINCT"))
return CombineOperator.INTERSECT;
else
return CombineOperator.INTERSECT;
return null;
}
private static final ComputationalOperation parseComputationalOperationIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (parseKeywordIf(ctx, "AVG"))
return ComputationalOperation.AVG;
else if (parseKeywordIf(ctx, "MAX"))
return ComputationalOperation.MAX;
else if (parseKeywordIf(ctx, "MIN"))
return ComputationalOperation.MIN;
else if (parseKeywordIf(ctx, "SUM"))
return ComputationalOperation.SUM;
else if (parseKeywordIf(ctx, "EVERY") || parseKeywordIf(ctx, "BOOL_AND"))
return ComputationalOperation.EVERY;
else if (parseKeywordIf(ctx, "ANY") || parseKeywordIf(ctx, "SOME") || parseKeywordIf(ctx, "BOOL_OR"))
return ComputationalOperation.ANY;
else if (parseKeywordIf(ctx, "STDDEV_POP"))
return ComputationalOperation.STDDEV_POP;
else if (parseKeywordIf(ctx, "STDDEV_SAMP"))
return ComputationalOperation.STDDEV_SAMP;
else if (parseKeywordIf(ctx, "VAR_POP"))
return ComputationalOperation.VAR_POP;
else if (parseKeywordIf(ctx, "VAR_SAMP"))
return ComputationalOperation.VAR_SAMP;
return null;
}
private static final BinarySetFunctionType parseBinarySetFunctionTypeIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
// TODO speed this up
for (BinarySetFunctionType type : BinarySetFunctionType.values())
if (parseKeywordIf(ctx, type.name()))
return type;
return null;
}
private static final Comparator parseComparatorIf(ParserContext ctx) {
parseWhitespaceIf(ctx);
if (parseIf(ctx, "="))
return Comparator.EQUALS;
else if (parseIf(ctx, "!=") || parseIf(ctx, "<>"))
return Comparator.NOT_EQUALS;
else if (parseIf(ctx, ">="))
return Comparator.GREATER_OR_EQUAL;
else if (parseIf(ctx, ">"))
return Comparator.GREATER;
else if (parseIf(ctx, "<="))
return Comparator.LESS_OR_EQUAL;
else if (parseIf(ctx, "<"))
return Comparator.LESS;
return null;
}
// -----------------------------------------------------------------------------------------------------------------
// Other tokens
// -----------------------------------------------------------------------------------------------------------------
private static final boolean parseIf(ParserContext ctx, String string) {
parseWhitespaceIf(ctx);
int length = string.length();
ctx.expectedTokens.add(string);
if (ctx.sql.length < ctx.position + length)
return false;
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
if (ctx.sql[ctx.position + i] != c)
return false;
}
ctx.position = ctx.position + length;
ctx.expectedTokens.clear();
return true;
}
private static final boolean parseIf(ParserContext ctx, char c) {
parseWhitespaceIf(ctx);
if (ctx.character() != c)
return false;
ctx.position = ctx.position + 1;
return true;
}
private static final void parse(ParserContext ctx, char c) {
if (!parseIf(ctx, c))
throw ctx.unexpectedToken();
}
static final void parseKeyword(ParserContext ctx, String string) {
if (!parseKeywordIf(ctx, string))
throw ctx.unexpectedToken();
}
static final boolean parseKeywordIf(ParserContext ctx, String string) {
ctx.expectedTokens.add(string);
if (peekKeyword(ctx, string, true))
ctx.expectedTokens.clear();
else
return false;
return true;
}
private static final boolean peekKeyword(ParserContext ctx, String... keywords) {
for (String keyword : keywords)
if (peekKeyword(ctx, keyword))
return true;
return false;
}
private static final boolean peekKeyword(ParserContext ctx, String keyword) {
return peekKeyword(ctx, keyword, false);
}
private static final boolean peekKeyword(ParserContext ctx, String keyword, boolean updatePosition) {
parseWhitespaceIf(ctx);
int length = keyword.length();
int skip;
if (ctx.sql.length < ctx.position + length)
return false;
// TODO is this correct?
skipLoop:
for (skip = 0; ctx.position + skip < ctx.sql.length; skip++) {
char c = ctx.character(ctx.position + skip);
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
case '(':
continue skipLoop;
default:
break skipLoop;
}
}
for (int i = 0; i < length; i++) {
char c = keyword.charAt(i);
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
skip = skip + (afterWhitespace(ctx, ctx.position + i + skip) - ctx.position - i - 1);
break;
default:
if (upper(ctx.sql[ctx.position + i + skip]) != keyword.charAt(i))
return false;
break;
}
}
if (ctx.isIdentifierPart(ctx.position + length + skip))
return false;
if (updatePosition)
ctx.position = ctx.position + length + skip;
return true;
}
static final boolean parseWhitespaceIf(ParserContext ctx) {
int position = ctx.position;
ctx.position = afterWhitespace(ctx, ctx.position);
return position != ctx.position;
}
private static final int afterWhitespace(ParserContext ctx, int position) {
loop:
for (int i = position; i < ctx.sql.length; i++)
switch (ctx.sql[i]) {
case ' ':
case '\t':
case '\r':
case '\n':
position = i + 1;
continue loop;
// TODO consume comments
default:
break loop;
}
return position;
}
private static final char upper(char c) {
return c >= 'a' && c <= 'z' ? (char) (c - ('a' - 'A')) : c;
}
public static void main(String[] args) {
System.out.println(new ParserImpl(new DefaultConfiguration()).parse(
"DROP INDEX y on a.b.c"
));
}
static class ParserContext {
final DSLContext dsl;
final String sqlString;
final char[] sql;
final List expectedTokens;
int position = 0;
ParserContext(DSLContext dsl, String sqlString) {
this.dsl = dsl;
this.sqlString = sqlString;
this.sql = sqlString.toCharArray();
this.expectedTokens = new ArrayList();
}
ParserException internalError() {
return new ParserException(this, "Internal Error");
}
ParserException exception() {
return new ParserException(this);
}
ParserException unexpectedToken() {
return new ParserException(this, "Expected tokens: " + new TreeSet(expectedTokens));
}
char character() {
return character(position);
}
char character(int pos) {
return pos >= 0 && pos < sql.length ? sql[pos] : ' ';
}
boolean isWhitespace() {
return Character.isWhitespace(character());
}
boolean isWhitespace(int pos) {
return Character.isWhitespace(character(pos));
}
boolean isIdentifierPart() {
return Character.isJavaIdentifierPart(character());
}
boolean isIdentifierPart(int pos) {
return Character.isJavaIdentifierPart(character(pos));
}
boolean done() {
return position >= sql.length;
}
String mark() {
return sqlString.substring(0, position) + "[*]" + sqlString.substring(position);
}
@Override
public String toString() {
return mark();
}
}
static class ParserException extends DataAccessException {
/**
* Generated UID
*/
private static final long serialVersionUID = -724913199583039157L;
private final ParserContext ctx;
public ParserException(ParserContext ctx) {
this(ctx, null);
}
public ParserException(ParserContext ctx, String message) {
this(ctx, message, C42000_NO_SUBCLASS);
}
public ParserException(ParserContext ctx, String message, SQLStateSubclass state) {
this(ctx, message, state, null);
}
public ParserException(ParserContext ctx, String message, SQLStateSubclass state, Throwable cause) {
super(state + ": " + (message == null ? "" : message + ": ") + ctx.mark(), cause);
this.ctx = ctx;
}
}
static enum TruthValue {
TRUE,
FALSE,
NULL;
}
static enum ComputationalOperation {
AVG,
MAX,
MIN,
SUM,
EVERY,
ANY,
SOME,
COUNT,
STDDEV_POP,
STDDEV_SAMP,
VAR_SAMP,
VAR_POP,
// COLLECT,
// FUSION,
// INTERSECTION;
}
static enum BinarySetFunctionType {
// COVAR_POP,
// COVAR_SAMP,
// CORR,
REGR_SLOPE,
REGR_INTERCEPT,
REGR_COUNT,
REGR_R2,
REGR_AVGX,
REGR_AVGY,
REGR_SXX,
REGR_SYY,
REGR_SXY,
}
private static final String[] SELECT_KEYWORDS = {
"CONNECT",
"CROSS",
"EXCEPT",
"FETCH",
"FULL",
"FROM",
"GROUP BY",
"HAVING",
"INNER",
"INTERSECT",
"JOIN",
"LEFT",
"LIMIT",
"MINUS",
"NATURAL",
"OFFSET",
"ON",
"ORDER BY",
"OUTER",
"RIGHT",
"SELECT",
"START",
"UNION",
"USING",
"WHERE",
};
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy