[#5955] Add support for WITH

This commit is contained in:
lukaseder 2017-03-25 22:28:05 +01:00
parent cb3375f45a
commit 06736f4aee
2 changed files with 69 additions and 19 deletions

View File

@ -195,6 +195,7 @@ 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_COMMON_TABLE_EXPRESSION;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.EMPTY_NAME;
@ -224,6 +225,7 @@ import org.jooq.AlterTableStep;
import org.jooq.CaseConditionStep;
import org.jooq.CaseValueStep;
import org.jooq.CaseWhenStep;
import org.jooq.CommonTableExpression;
import org.jooq.Comparator;
import org.jooq.Condition;
import org.jooq.Configuration;
@ -243,6 +245,7 @@ import org.jooq.DatePart;
import org.jooq.Delete;
import org.jooq.DeleteFinalStep;
import org.jooq.DeleteWhereStep;
import org.jooq.DerivedColumnList;
import org.jooq.DropIndexFinalStep;
import org.jooq.DropIndexOnStep;
import org.jooq.DropSchemaFinalStep;
@ -453,6 +456,13 @@ class ParserImpl implements Parser {
break;
case 'w':
case 'W':
if (peekKeyword(ctx, "WITH"))
return parseWith(ctx);
break;
case '(':
// TODO are there other possible statement types?
return parseSelect(ctx);
@ -473,8 +483,42 @@ class ParserImpl implements Parser {
// Statement parsing
// -----------------------------------------------------------------------------------------------------------------
private static final Query parseWith(ParserContext ctx) {
parseKeyword(ctx, "WITH");
List<CommonTableExpression<?>> cte = new ArrayList<CommonTableExpression<?>>();
do {
Name table = parseIdentifier(ctx);
// [#6022] Allow unquoted identifiers for columns
parse(ctx, '(');
List<Name> columnNames = parseIdentifiers(ctx);
String[] columns = new String[columnNames.size()];
for (int i = 0; i < columns.length; i++)
columns[i] = columnNames.get(i).last();
parse(ctx, ')');
DerivedColumnList dcl = table.fields(columns);
parseKeyword(ctx, "AS");
parse(ctx, '(');
cte.add(dcl.as(parseSelect(ctx)));
parse(ctx, ')');
}
while (parseIf(ctx, ','));
// TODO Better model API for WITH clause
return parseSelect(ctx, (WithImpl) new WithImpl(ctx.dsl.configuration(), false).with(cte.toArray(EMPTY_COMMON_TABLE_EXPRESSION)));
// TODO Other statements than SELECT
}
private static final SelectQueryImpl<Record> parseSelect(ParserContext ctx) {
SelectQueryImpl<Record> result = parseQueryPrimary(ctx);
return parseSelect(ctx, null);
}
private static final SelectQueryImpl<Record> parseSelect(ParserContext ctx, WithImpl with) {
SelectQueryImpl<Record> result = parseQueryPrimary(ctx, with);
CombineOperator combine;
while ((combine = parseCombineOperatorIf(ctx)) != null) {
switch (combine) {
@ -577,8 +621,12 @@ class ParserImpl implements Parser {
}
private static final SelectQueryImpl<Record> parseQueryPrimary(ParserContext ctx) {
return parseQueryPrimary(ctx, null);
}
private static final SelectQueryImpl<Record> parseQueryPrimary(ParserContext ctx, WithImpl with) {
if (parseIf(ctx, '(')) {
SelectQueryImpl<Record> result = parseSelect(ctx);
SelectQueryImpl<Record> result = parseSelect(ctx, with);
parse(ctx, ')');
return result;
}
@ -689,7 +737,7 @@ class ParserImpl implements Parser {
// TODO support WINDOW
SelectQueryImpl<Record> result = (SelectQueryImpl<Record>) ctx.dsl.selectQuery();
SelectQueryImpl<Record> result = new SelectQueryImpl<Record>(ctx.dsl.configuration(), with);
if (distinct)
result.setDistinct(distinct);

View File

@ -123,6 +123,7 @@ import org.jooq.AttachableInternal;
import org.jooq.BindContext;
import org.jooq.Catalog;
import org.jooq.Clause;
import org.jooq.CommonTableExpression;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
@ -178,27 +179,28 @@ import org.jooq.types.UShort;
*/
final class Tools {
static final JooqLogger log = JooqLogger.getLogger(Tools.class);
static final JooqLogger log = JooqLogger.getLogger(Tools.class);
// ------------------------------------------------------------------------
// Empty arrays for use with Collection.toArray()
// ------------------------------------------------------------------------
static final Class<?>[] EMPTY_CLASS = {};
static final Clause[] EMPTY_CLAUSE = {};
static final Collection<?>[] EMPTY_COLLECTION = {};
static final ExecuteListener[] EMPTY_EXECUTE_LISTENER = {};
static final Field<?>[] EMPTY_FIELD = {};
static final int[] EMPTY_INT = {};
static final Param<?>[] EMPTY_PARAM = {};
static final Query[] EMPTY_QUERY = {};
static final QueryPart[] EMPTY_QUERYPART = {};
static final Record[] EMPTY_RECORD = {};
static final RowN[] EMPTY_ROWN = {};
static final String[] EMPTY_STRING = {};
static final Name[] EMPTY_NAME = {};
static final TableRecord<?>[] EMPTY_TABLE_RECORD = {};
static final UpdatableRecord<?>[] EMPTY_UPDATABLE_RECORD = {};
static final Class<?>[] EMPTY_CLASS = {};
static final Clause[] EMPTY_CLAUSE = {};
static final Collection<?>[] EMPTY_COLLECTION = {};
static final ExecuteListener[] EMPTY_EXECUTE_LISTENER = {};
static final Field<?>[] EMPTY_FIELD = {};
static final int[] EMPTY_INT = {};
static final Param<?>[] EMPTY_PARAM = {};
static final Query[] EMPTY_QUERY = {};
static final QueryPart[] EMPTY_QUERYPART = {};
static final Record[] EMPTY_RECORD = {};
static final RowN[] EMPTY_ROWN = {};
static final CommonTableExpression<?>[] EMPTY_COMMON_TABLE_EXPRESSION = {};
static final String[] EMPTY_STRING = {};
static final Name[] EMPTY_NAME = {};
static final TableRecord<?>[] EMPTY_TABLE_RECORD = {};
static final UpdatableRecord<?>[] EMPTY_UPDATABLE_RECORD = {};
// ------------------------------------------------------------------------
// Some constants for use with Context.data()