From d2512c9d145eab88097f344880478d184d250126 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sat, 25 Mar 2017 20:58:27 +0100 Subject: [PATCH 01/13] [#5955] Support Ingres OFFSET syntax --- jOOQ/src/main/java/org/jooq/impl/ParserImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 33ee2235cc..b1bf6a6004 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -518,6 +518,10 @@ class ParserImpl implements Parser { if (parseKeywordIf(ctx, "ROWS") || parseKeywordIf(ctx, "ROW")) offsetStandard = true; + + // Ingres doesn't have a ROWS keyword after offset + else if (peekKeyword(ctx, "FETCH")) + offsetStandard = true; else offsetPostgres = true; } From 63becdec46865809cee6cd17ec213db59cdce961 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sat, 25 Mar 2017 21:10:45 +0100 Subject: [PATCH 02/13] [#5955] Support Informix SKIP m FIRST n syntax --- jOOQ/src/main/java/org/jooq/impl/ParserImpl.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index b1bf6a6004..7ec4ff54c3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -588,6 +588,7 @@ class ParserImpl implements Parser { if (!distinct) parseKeywordIf(ctx, "ALL"); + // T-SQL style TOP .. START AT if (parseKeywordIf(ctx, "TOP")) { limit = parseUnsignedInteger(ctx); @@ -595,6 +596,17 @@ class ParserImpl implements Parser { offset = parseUnsignedInteger(ctx); } + // Informix style SKIP .. FIRST + else if (parseKeywordIf(ctx, "SKIP")) { + offset = parseUnsignedInteger(ctx); + + if (parseKeywordIf(ctx, "FIRST")) + limit = parseUnsignedInteger(ctx); + } + else if (parseKeywordIf(ctx, "FIRST")) { + limit = parseUnsignedInteger(ctx); + } + List> select = parseSelectList(ctx); List> from = null; Condition startWith = null; From 7cbf0390f57145f05eb1c0195c588a60a94e0cc8 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sat, 25 Mar 2017 21:41:29 +0100 Subject: [PATCH 03/13] [#6020] values(RowN...) columns are named c0, c1, ... c[N-1] instead of c1, c2, ... cN --- jOOQ/src/main/java/org/jooq/impl/DSL.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index d35109fc1d..a7b1316d4e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -18134,7 +18134,7 @@ public class DSL { String[] columns = new String[size]; for (int i = 0; i < size; i++) - columns[i] = "c" + i; + columns[i] = "c" + (i + 1); return new Values(rows).as("v", columns); } From cb3375f45abe25508c46c7ba32ae29fb89014d41 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sat, 25 Mar 2017 21:42:03 +0100 Subject: [PATCH 04/13] [#5955] Support VALUES constructor --- .../main/java/org/jooq/impl/ParserImpl.java | 25 +++++++++++++++++++ jOOQ/src/main/java/org/jooq/impl/Tools.java | 2 ++ 2 files changed, 27 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 7ec4ff54c3..cb23ac2280 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -150,6 +150,7 @@ 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.row; import static org.jooq.impl.DSL.rowNumber; import static org.jooq.impl.DSL.rownum; import static org.jooq.impl.DSL.rowsBetweenCurrentRow; @@ -184,6 +185,7 @@ 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.values; import static org.jooq.impl.DSL.varPop; import static org.jooq.impl.DSL.varSamp; import static org.jooq.impl.DSL.when; @@ -267,6 +269,7 @@ import org.jooq.Queries; import org.jooq.Query; import org.jooq.QueryPart; import org.jooq.Record; +import org.jooq.RowN; import org.jooq.Schema; import org.jooq.Select; import org.jooq.Sequence; @@ -1784,6 +1787,10 @@ class ParserImpl implements Parser { result = table(parseSelect(ctx)); parse(ctx, ')'); } + else if (peekKeyword(ctx, "VALUES")) { + result = parseTableValueConstructor(ctx); + parse(ctx, ')'); + } else { int parens = 0; @@ -1826,6 +1833,24 @@ class ParserImpl implements Parser { return result; } + private static final Table parseTableValueConstructor(ParserContext ctx) { + parseKeyword(ctx, "VALUES"); + + List rows = new ArrayList(); + do { + rows.add(parseRow(ctx)); + } + while (parseIf(ctx, ',')); + return values(rows.toArray(Tools.EMPTY_ROWN)); + } + + private static final RowN parseRow(ParserContext ctx) { + parse(ctx, '('); + RowN row = row(parseFields(ctx)); + parse(ctx, ')'); + return row; + } + private static final Table parseJoinedTable(ParserContext ctx) { Table result = parseTableFactor(ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 020a1eacb5..af055530a4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -144,6 +144,7 @@ import org.jooq.RenderContext.CastMode; import org.jooq.Result; import org.jooq.Results; import org.jooq.Row; +import org.jooq.RowN; import org.jooq.SQLDialect; import org.jooq.Schema; import org.jooq.Select; @@ -193,6 +194,7 @@ final class Tools { 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 = {}; From 06736f4aeeb142df7d2d88116016d914f8730c6f Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sat, 25 Mar 2017 22:28:05 +0100 Subject: [PATCH 05/13] [#5955] Add support for WITH --- .../main/java/org/jooq/impl/ParserImpl.java | 54 +++++++++++++++++-- jOOQ/src/main/java/org/jooq/impl/Tools.java | 34 ++++++------ 2 files changed, 69 insertions(+), 19 deletions(-) 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() From 8e9a82ae5f5742775b3a46b0522b3766abdd305e Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 21:55:34 +0200 Subject: [PATCH 06/13] [#5955] Support CREATE UNIQUE INDEX --- jOOQ/src/main/java/org/jooq/impl/ParserImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 6862e14f17..1ccc1b5f27 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -948,8 +948,10 @@ class ParserImpl implements Parser { if (parseKeywordIf(ctx, "TABLE")) return parseCreateTable(ctx); - if (parseKeywordIf(ctx, "INDEX")) - return parseCreateIndex(ctx); + else if (parseKeywordIf(ctx, "INDEX")) + return parseCreateIndex(ctx, false); + else if (parseKeywordIf(ctx, "UNIQUE INDEX")) + return parseCreateIndex(ctx, true); else if (parseKeywordIf(ctx, "SCHEMA")) return parseCreateSchema(ctx); else if (parseKeywordIf(ctx, "VIEW")) @@ -1559,7 +1561,7 @@ class ParserImpl implements Parser { return s2; } - private static final DDLQuery parseCreateIndex(ParserContext ctx) { + private static final DDLQuery parseCreateIndex(ParserContext ctx, boolean unique) { boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS"); Name indexName = parseIndexName(ctx); parseKeyword(ctx, "ON"); @@ -1573,8 +1575,12 @@ class ParserImpl implements Parser { CreateIndexStep s1 = ifNotExists - ? ctx.dsl.createIndexIfNotExists(indexName) - : ctx.dsl.createIndex(indexName); + ? unique + ? ctx.dsl.createUniqueIndexIfNotExists(indexName) + : ctx.dsl.createIndexIfNotExists(indexName) + : unique + ? ctx.dsl.createUniqueIndex(indexName) + : ctx.dsl.createIndex(indexName); CreateIndexWhereStep s2 = s1.on(tableName, fieldNames); CreateIndexFinalStep s3 = condition != null ? s2.where(condition) From 302b37df51b06520effb7faf2ea9d4dd182a9689 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 22:06:08 +0200 Subject: [PATCH 07/13] [#5955] OUTER APPLY bug --- jOOQ/src/main/java/org/jooq/impl/ParserImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 1ccc1b5f27..f789e5ac05 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -1934,8 +1934,7 @@ class ParserImpl implements Parser { case JOIN: case LEFT_OUTER_JOIN: case FULL_OUTER_JOIN: - case RIGHT_OUTER_JOIN: - case OUTER_APPLY: { + case RIGHT_OUTER_JOIN: { boolean on = parseKeywordIf(ctx, "ON"); if (on) { From 977b6381b3cf74ad8022533d985c924bc34820a6 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 22:09:54 +0200 Subject: [PATCH 08/13] [#5955] Add support for CREATE SEQUENCE --- .../main/java/org/jooq/impl/ParserImpl.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index f789e5ac05..35acc9ddcf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -250,7 +250,6 @@ 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; @@ -954,6 +953,8 @@ class ParserImpl implements Parser { return parseCreateIndex(ctx, true); else if (parseKeywordIf(ctx, "SCHEMA")) return parseCreateSchema(ctx); + else if (parseKeywordIf(ctx, "SEQUENCE")) + return parseCreateSequence(ctx); else if (parseKeywordIf(ctx, "VIEW")) return parseCreateView(ctx); else @@ -1069,17 +1070,22 @@ class ParserImpl implements Parser { return s1; } + private static final DDLQuery parseCreateSequence(ParserContext ctx) { + boolean ifNotExists = parseKeywordIf(ctx, "IF NOT EXISTS"); + Sequence schemaName = parseSequenceName(ctx); + + return ifNotExists + ? ctx.dsl.createSequenceIfNotExists(schemaName) + : ctx.dsl.createSequence(schemaName); + } + private static final DDLQuery parseDropSequence(ParserContext ctx) { boolean ifExists = parseKeywordIf(ctx, "IF EXISTS"); Sequence sequenceName = parseSequenceName(ctx); - DropSequenceFinalStep s1; - - s1 = ifExists + return ifExists ? ctx.dsl.dropSequenceIfExists(sequenceName) : ctx.dsl.dropSequence(sequenceName); - - return s1; } private static final DDLQuery parseCreateTable(ParserContext ctx) { From 2919ef8222a2bc394f1f984c6b9ffd3dbbd8f1a0 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 22:17:29 +0200 Subject: [PATCH 09/13] [#5955] Add support for ALTER SEQUENCE --- .../main/java/org/jooq/impl/ParserImpl.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 35acc9ddcf..ae207cc0ba 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -219,6 +219,7 @@ import org.jooq.AlterIndexFinalStep; import org.jooq.AlterIndexStep; import org.jooq.AlterSchemaFinalStep; import org.jooq.AlterSchemaStep; +import org.jooq.AlterSequenceStep; import org.jooq.AlterTableDropStep; import org.jooq.AlterTableFinalStep; import org.jooq.AlterTableStep; @@ -970,6 +971,8 @@ class ParserImpl implements Parser { return parseAlterIndex(ctx); else if (parseKeywordIf(ctx, "SCHEMA")) return parseAlterSchema(ctx); + else if (parseKeywordIf(ctx, "SEQUENCE")) + return parseAlterSequence(ctx); else if (parseKeywordIf(ctx, "VIEW")) return parseAlterView(ctx); else @@ -1079,6 +1082,25 @@ class ParserImpl implements Parser { : ctx.dsl.createSequence(schemaName); } + private static final DDLQuery parseAlterSequence(ParserContext ctx) { + boolean ifExists = parseKeywordIf(ctx, "IF EXISTS"); + Sequence sequenceName = parseSequenceName(ctx); + + AlterSequenceStep s1 = ifExists + ? ctx.dsl.alterSequenceIfExists(sequenceName) + : ctx.dsl.alterSequence(sequenceName); + + if (parseKeywordIf(ctx, "RENAME TO")) + return s1.renameTo(parseSequenceName(ctx)); + else if (parseKeywordIf(ctx, "RESTART")) + if (parseKeywordIf(ctx, "WITH")) + return s1.restartWith(parseUnsignedInteger(ctx)); + else + return s1.restart(); + else + throw ctx.unexpectedToken(); + } + private static final DDLQuery parseDropSequence(ParserContext ctx) { boolean ifExists = parseKeywordIf(ctx, "IF EXISTS"); Sequence sequenceName = parseSequenceName(ctx); From fe31e6d2a785ef979500293c817a7288a041ecf5 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 22:39:27 +0200 Subject: [PATCH 10/13] [#5955] ARRAY constructor (by enumeration) --- .../main/java/org/jooq/impl/ParserImpl.java | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index ae207cc0ba..18525e23d5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -190,6 +190,7 @@ 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.A; import static org.jooq.impl.ParserImpl.Type.B; import static org.jooq.impl.ParserImpl.Type.D; import static org.jooq.impl.ParserImpl.Type.N; @@ -2044,10 +2045,17 @@ class ParserImpl implements Parser { } static enum Type { - D, - S, - N, - B; + A("array"), + D("date"), + S("string"), + N("numeric"), + B("boolean"); + + private final String name; + + private Type(String name) { + this.name = name; + } boolean is(Type type) { return type == null || type == this; @@ -2159,6 +2167,10 @@ class ParserImpl implements Parser { else if ((field = parseFieldAtan2If(ctx)) != null) return field; + if (A.is(type)) + if ((field = parseArrayValueConstructorIf(ctx)) != null) + return field; + break; case 'b': @@ -2530,6 +2542,25 @@ class ParserImpl implements Parser { return parseFieldName(ctx); } + private static final Field parseArrayValueConstructorIf(ParserContext ctx) { + if (parseKeywordIf(ctx, "ARRAY")) { + parse(ctx, '['); + + List> fields; + if (parseIf(ctx, ']')) { + fields = Collections.emptyList(); + } + else { + fields = (List) parseFields(ctx); + parse(ctx, ']'); + } + + return DSL.array(fields); + } + + return null; + } + private static final Field parseFieldAtan2If(ParserContext ctx) { if (parseKeywordIf(ctx, "ATN2") || parseKeywordIf(ctx, "ATAN2")) { parse(ctx, '('); From c13273036b3bfd756bfe4ba800c60b9464740fa6 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 22:53:38 +0200 Subject: [PATCH 11/13] [#5955] Support MEDIAN() aggregate function --- .../main/java/org/jooq/impl/ParserImpl.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 18525e23d5..ca9d11e4bc 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -104,6 +104,7 @@ 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.median; import static org.jooq.impl.DSL.mid; import static org.jooq.impl.DSL.min; import static org.jooq.impl.DSL.minDistinct; @@ -3880,7 +3881,19 @@ class ParserImpl implements Parser { return null; parse(ctx, '('); - distinct = parseSetQuantifier(ctx); + + switch (operation) { + case AVG: + case MAX: + case MIN: + case SUM: + distinct = parseSetQuantifier(ctx); + break; + default: + distinct = false; + break; + } + arg = parseField(ctx); parse(ctx, ')'); @@ -3893,6 +3906,8 @@ class ParserImpl implements Parser { return distinct ? minDistinct(arg) : min(arg); case SUM: return distinct ? sumDistinct(arg) : sum(arg); + case MEDIAN: + return median(arg); case EVERY: return every(arg); case ANY: @@ -4425,6 +4440,8 @@ class ParserImpl implements Parser { return ComputationalOperation.MIN; else if (parseKeywordIf(ctx, "SUM")) return ComputationalOperation.SUM; + else if (parseKeywordIf(ctx, "MEDIAN")) + return ComputationalOperation.MEDIAN; else if (parseKeywordIf(ctx, "EVERY") || parseKeywordIf(ctx, "BOOL_AND")) return ComputationalOperation.EVERY; else if (parseKeywordIf(ctx, "ANY") || parseKeywordIf(ctx, "SOME") || parseKeywordIf(ctx, "BOOL_OR")) @@ -4782,6 +4799,7 @@ class ParserImpl implements Parser { STDDEV_SAMP, VAR_SAMP, VAR_POP, + MEDIAN, // COLLECT, // FUSION, // INTERSECTION; From 6fdcaf2dcc52b5608d92d3fb8d1e85280b2c7cbc Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 23:01:54 +0200 Subject: [PATCH 12/13] [#5955] Support MODE() --- .../main/java/org/jooq/impl/ParserImpl.java | 90 +++++++++++++------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index ca9d11e4bc..1462e9c206 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -109,6 +109,7 @@ 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.mode; import static org.jooq.impl.DSL.month; import static org.jooq.impl.DSL.nthValue; import static org.jooq.impl.DSL.ntile; @@ -268,6 +269,7 @@ import org.jooq.MergeMatchedStep; import org.jooq.MergeNotMatchedStep; import org.jooq.Name; import org.jooq.OrderedAggregateFunction; +import org.jooq.OrderedAggregateFunctionOfDeferredType; import org.jooq.Param; import org.jooq.Parser; import org.jooq.Queries; @@ -2011,25 +2013,29 @@ class ParserImpl implements Parser { 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); + result.add(parseSortField(ctx)); } while (parseIf(ctx, ',')); return result; } + private static final SortField parseSortField(ParserContext ctx) { + 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(); + + return sort; + } + private static final List> parseFields(ParserContext ctx) { parseWhitespaceIf(ctx); @@ -3587,7 +3593,7 @@ class ParserImpl implements Parser { // Hypothetical set function List> args = parseFields(ctx); parse(ctx, ')'); - AggregateFilterStep result = parseWithinGroup(ctx, rank(args)); + AggregateFilterStep result = parseWithinGroupN(ctx, rank(args)); return result; } @@ -3604,7 +3610,7 @@ class ParserImpl implements Parser { // Hypothetical set function List> args = parseFields(ctx); parse(ctx, ')'); - AggregateFilterStep result = parseWithinGroup(ctx, denseRank(args)); + AggregateFilterStep result = parseWithinGroupN(ctx, denseRank(args)); return result; } @@ -3621,7 +3627,7 @@ class ParserImpl implements Parser { // Hypothetical set function List> args = parseFields(ctx); parse(ctx, ')'); - AggregateFilterStep result = parseWithinGroup(ctx, percentRank(args)); + AggregateFilterStep result = parseWithinGroupN(ctx, percentRank(args)); return result; } @@ -3638,7 +3644,7 @@ class ParserImpl implements Parser { // Hypothetical set function List> args = parseFields(ctx); parse(ctx, ')'); - AggregateFilterStep result = parseWithinGroup(ctx, cumeDist(args)); + AggregateFilterStep result = parseWithinGroupN(ctx, cumeDist(args)); return result; } @@ -3801,18 +3807,23 @@ class ParserImpl implements Parser { private static final WindowBeforeOverStep parseOrderedSetFunctionIf(ParserContext ctx) { // TODO Listagg set function - OrderedAggregateFunction ordered; + OrderedAggregateFunction orderedN; + OrderedAggregateFunctionOfDeferredType ordered1; - ordered = parseHypotheticalSetFunctionif(ctx); - if (ordered == null) - ordered = parseInverseDistributionFunctionif(ctx); - if (ordered == null) - return null; + orderedN = parseHypotheticalSetFunctionIf(ctx); + if (orderedN == null) + orderedN = parseInverseDistributionFunctionIf(ctx); + if (orderedN != null) + return parseWithinGroupN(ctx, orderedN); - return parseWithinGroup(ctx, ordered); + ordered1 = parseModeIf(ctx); + if (ordered1 != null) + return parseWithinGroup1(ctx, ordered1); + + return null; } - private static final AggregateFilterStep parseWithinGroup(ParserContext ctx, OrderedAggregateFunction ordered) { + private static final AggregateFilterStep parseWithinGroupN(ParserContext ctx, OrderedAggregateFunction ordered) { parseKeyword(ctx, "WITHIN GROUP"); parse(ctx, '('); parseKeyword(ctx, "ORDER BY"); @@ -3821,7 +3832,16 @@ class ParserImpl implements Parser { return result; } - private static final OrderedAggregateFunction parseHypotheticalSetFunctionif(ParserContext ctx) { + private static final AggregateFilterStep parseWithinGroup1(ParserContext ctx, OrderedAggregateFunctionOfDeferredType ordered) { + parseKeyword(ctx, "WITHIN GROUP"); + parse(ctx, '('); + parseKeyword(ctx, "ORDER BY"); + AggregateFilterStep result = ordered.withinGroupOrderBy(parseSortField(ctx)); + parse(ctx, ')'); + return result; + } + + private static final OrderedAggregateFunction parseHypotheticalSetFunctionIf(ParserContext ctx) { // This currently never parses hypothetical set functions, as the function names are already // consumed earlier in parseFieldTerm(). We should implement backtracking... @@ -3853,7 +3873,7 @@ class ParserImpl implements Parser { return ordered; } - private static final OrderedAggregateFunction parseInverseDistributionFunctionif(ParserContext ctx) { + private static final OrderedAggregateFunction parseInverseDistributionFunctionIf(ParserContext ctx) { OrderedAggregateFunction ordered; if (parseKeywordIf(ctx, "PERCENTILE_CONT")) { @@ -3872,6 +3892,20 @@ class ParserImpl implements Parser { return ordered; } + private static final OrderedAggregateFunctionOfDeferredType parseModeIf(ParserContext ctx) { + OrderedAggregateFunctionOfDeferredType ordered; + + if (parseKeywordIf(ctx, "MODE")) { + parse(ctx, '('); + parse(ctx, ')'); + ordered = mode(); + } + else + ordered = null; + + return ordered; + } + private static final AggregateFunction parseGeneralSetFunctionIf(ParserContext ctx) { boolean distinct; Field arg; From 1efae757d44ffbf350a1cfea4d38f510442a41fa Mon Sep 17 00:00:00 2001 From: lukaseder Date: Sun, 26 Mar 2017 23:15:41 +0200 Subject: [PATCH 13/13] [#5955] MySQL NOT DISTINCT predicate --- jOOQ/src/main/java/org/jooq/impl/ParserImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 1462e9c206..132063274f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -4514,6 +4514,10 @@ class ParserImpl implements Parser { return Comparator.GREATER_OR_EQUAL; else if (parseIf(ctx, ">")) return Comparator.GREATER; + + // MySQL DISTINCT operator + else if (parseIf(ctx, "<=>")) + return Comparator.IS_NOT_DISTINCT_FROM; else if (parseIf(ctx, "<=")) return Comparator.LESS_OR_EQUAL; else if (parseIf(ctx, "<"))