diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index cb23ac2280..6862e14f17 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -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> cte = new ArrayList>(); + do { + + Name table = parseIdentifier(ctx); + + // [#6022] Allow unquoted identifiers for columns + parse(ctx, '('); + List 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 parseSelect(ParserContext ctx) { - SelectQueryImpl result = parseQueryPrimary(ctx); + return parseSelect(ctx, null); + } + + private static final SelectQueryImpl parseSelect(ParserContext ctx, WithImpl with) { + SelectQueryImpl 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 parseQueryPrimary(ParserContext ctx) { + return parseQueryPrimary(ctx, null); + } + + private static final SelectQueryImpl parseQueryPrimary(ParserContext ctx, WithImpl with) { if (parseIf(ctx, '(')) { - SelectQueryImpl result = parseSelect(ctx); + SelectQueryImpl result = parseSelect(ctx, with); parse(ctx, ')'); return result; } @@ -689,7 +737,7 @@ class ParserImpl implements Parser { // TODO support WINDOW - SelectQueryImpl result = (SelectQueryImpl) ctx.dsl.selectQuery(); + SelectQueryImpl result = new SelectQueryImpl(ctx.dsl.configuration(), with); if (distinct) result.setDistinct(distinct); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index af055530a4..02a622ff73 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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()