[jOOQ/jOOQ#9061] Implemented lookups for DML statements
This commit is contained in:
parent
17c8f59ca5
commit
86615abb78
@ -67,6 +67,8 @@ import org.jooq.Result;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.SortField;
|
||||
import org.jooq.SortOrder;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableField;
|
||||
import org.jooq.WindowIgnoreNullsStep;
|
||||
import org.jooq.WindowPartitionByStep;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
@ -78,7 +80,7 @@ import org.jooq.exception.DataAccessException;
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
final class FieldProxy<T> implements Field<T>, QueryPartInternal {
|
||||
final class FieldProxy<T> implements TableField<Record, T>, QueryPartInternal {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
@ -1831,4 +1833,9 @@ final class FieldProxy<T> implements Field<T>, QueryPartInternal {
|
||||
public final Field<T> coalesce(Field<T> option, Field<?>... options) {
|
||||
return delegate.coalesce(option, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Table<Record> getTable() {
|
||||
return delegate instanceof TableField ? ((TableField<Record, ?>) delegate).getTable() : null;
|
||||
}
|
||||
}
|
||||
@ -1437,7 +1437,7 @@ final class ParserImpl implements Parser {
|
||||
// TODO: Move this into parseTables() so lateral joins can profit from lookups (?)
|
||||
if (from != null)
|
||||
for (Table<?> table : from)
|
||||
ctx.tableScope.set(table.getName(), table);
|
||||
ctx.scope(table);
|
||||
|
||||
if (parseKeywordIf(ctx, "WHERE"))
|
||||
where = parseCondition(ctx);
|
||||
@ -1819,6 +1819,8 @@ final class ParserImpl implements Parser {
|
||||
table = table.as(parseIdentifierIf(ctx));
|
||||
}
|
||||
|
||||
ctx.scope(table);
|
||||
|
||||
DeleteUsingStep<?> s1 = with == null ? ctx.dsl.delete(table) : with.delete(table);
|
||||
DeleteWhereStep<?> s2 = parseKeywordIf(ctx, "USING") ? s1.using(parseTables(ctx)) : s1;
|
||||
DeleteOrderByStep<?> s3 = parseKeywordIf(ctx, "WHERE") ? s2.where(parseCondition(ctx)) : s2;
|
||||
@ -1832,6 +1834,7 @@ final class ParserImpl implements Parser {
|
||||
}
|
||||
|
||||
private static final Insert<?> parseInsert(ParserContext ctx, WithImpl with) {
|
||||
ctx.scopeStart();
|
||||
if (!parseKeywordIf(ctx, "INS"))
|
||||
parseKeyword(ctx, "INSERT");
|
||||
|
||||
@ -1847,130 +1850,139 @@ final class ParserImpl implements Parser {
|
||||
&& (alias = parseIdentifierIf(ctx)) != null)
|
||||
table = table.as(alias);
|
||||
|
||||
ctx.scope(table);
|
||||
|
||||
InsertSetStep<?> s1 = (with == null ? ctx.dsl.insertInto(table) : with.insertInto(table));
|
||||
Field<?>[] fields = null;
|
||||
|
||||
if (parseIf(ctx, '(')) {
|
||||
fields = Tools.fieldsByName(parseIdentifiers(ctx).toArray(EMPTY_NAME));
|
||||
fields = parseFieldNames(ctx).toArray(EMPTY_FIELD);
|
||||
parse(ctx, ')');
|
||||
}
|
||||
|
||||
InsertOnDuplicateStep<?> onDuplicate;
|
||||
InsertReturningStep<?> returning;
|
||||
|
||||
if (parseKeywordIf(ctx, "VALUES")) {
|
||||
List<List<Field<?>>> allValues = new ArrayList<>();
|
||||
ctx.scopeEnd();
|
||||
ctx.scopeStart();
|
||||
try {
|
||||
if (parseKeywordIf(ctx, "VALUES")) {
|
||||
List<List<Field<?>>> allValues = new ArrayList<>();
|
||||
|
||||
valuesLoop:
|
||||
do {
|
||||
parse(ctx, '(');
|
||||
|
||||
// [#6936] MySQL treats an empty VALUES() clause as the same thing as the standard DEFAULT VALUES
|
||||
if (fields == null && parseIf(ctx, ')'))
|
||||
break valuesLoop;
|
||||
|
||||
List<Field<?>> values = new ArrayList<>();
|
||||
valuesLoop:
|
||||
do {
|
||||
Field<?> value = parseKeywordIf(ctx, "DEFAULT") ? default_() : parseField(ctx);
|
||||
values.add(value);
|
||||
parse(ctx, '(');
|
||||
|
||||
// [#6936] MySQL treats an empty VALUES() clause as the same thing as the standard DEFAULT VALUES
|
||||
if (fields == null && parseIf(ctx, ')'))
|
||||
break valuesLoop;
|
||||
|
||||
List<Field<?>> values = new ArrayList<>();
|
||||
do {
|
||||
Field<?> value = parseKeywordIf(ctx, "DEFAULT") ? default_() : parseField(ctx);
|
||||
values.add(value);
|
||||
}
|
||||
while (parseIf(ctx, ','));
|
||||
|
||||
if (fields != null && fields.length != values.size())
|
||||
throw ctx.exception("Insert field size (" + fields.length + ") must match values size (" + values.size() + ")");
|
||||
|
||||
allValues.add(values);
|
||||
parse(ctx, ')');
|
||||
}
|
||||
while (parseIf(ctx, ','));
|
||||
|
||||
if (fields != null && fields.length != values.size())
|
||||
throw ctx.exception("Insert field size (" + fields.length + ") must match values size (" + values.size() + ")");
|
||||
|
||||
allValues.add(values);
|
||||
parse(ctx, ')');
|
||||
}
|
||||
while (parseIf(ctx, ','));
|
||||
|
||||
if (allValues.isEmpty()) {
|
||||
returning = onDuplicate = s1.defaultValues();
|
||||
}
|
||||
else {
|
||||
InsertValuesStepN<?> step2 = (fields != null)
|
||||
? s1.columns(fields)
|
||||
: (InsertValuesStepN<?>) s1;
|
||||
|
||||
for (List<Field<?>> values : allValues)
|
||||
step2 = step2.values(values);
|
||||
|
||||
returning = onDuplicate = step2;
|
||||
}
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "SET")) {
|
||||
Map<Field<?>, Object> map = parseSetClauseList(ctx);
|
||||
|
||||
returning = onDuplicate = s1.set(map);
|
||||
}
|
||||
else if (peekKeyword(ctx, "SELECT", false, true, false)
|
||||
|| peekKeyword(ctx, "SEL", false, true, false)){
|
||||
SelectQueryImpl<Record> select = parseSelect(ctx);
|
||||
|
||||
returning = onDuplicate = (fields == null)
|
||||
? s1.select(select)
|
||||
: s1.columns(fields).select(select);
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "DEFAULT VALUES")) {
|
||||
if (fields != null)
|
||||
throw ctx.notImplemented("DEFAULT VALUES without INSERT field list");
|
||||
else
|
||||
returning = onDuplicate = s1.defaultValues();
|
||||
}
|
||||
else
|
||||
throw ctx.expected("DEFAULT VALUES", "SELECT", "SET", "VALUES");
|
||||
|
||||
if (parseKeywordIf(ctx, "ON")) {
|
||||
if (parseKeywordIf(ctx, "DUPLICATE KEY UPDATE")) {
|
||||
parseKeywordIf(ctx, "SET");
|
||||
|
||||
InsertOnConflictWhereStep<?> where = onDuplicate.onDuplicateKeyUpdate().set(parseSetClauseList(ctx));
|
||||
|
||||
if (parseKeywordIf(ctx, "WHERE"))
|
||||
returning = where.where(parseCondition(ctx));
|
||||
else
|
||||
returning = where;
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "DUPLICATE KEY IGNORE")) {
|
||||
returning = onDuplicate.onDuplicateKeyIgnore();
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "CONFLICT")) {
|
||||
InsertOnConflictDoUpdateStep<?> doUpdate;
|
||||
|
||||
if (parseKeywordIf(ctx, "ON CONSTRAINT")) {
|
||||
doUpdate = onDuplicate.onConflictOnConstraint(parseName(ctx));
|
||||
}
|
||||
else if (parseIf(ctx, '(')) {
|
||||
doUpdate = onDuplicate.onConflict(parseFieldNames(ctx));
|
||||
parse(ctx, ')');
|
||||
if (allValues.isEmpty()) {
|
||||
returning = onDuplicate = s1.defaultValues();
|
||||
}
|
||||
else {
|
||||
doUpdate = onDuplicate.onConflict();
|
||||
}
|
||||
InsertValuesStepN<?> step2 = (fields != null)
|
||||
? s1.columns(fields)
|
||||
: (InsertValuesStepN<?>) s1;
|
||||
|
||||
parseKeyword(ctx, "DO");
|
||||
if (parseKeywordIf(ctx, "NOTHING")) {
|
||||
returning = doUpdate.doNothing();
|
||||
for (List<Field<?>> values : allValues)
|
||||
step2 = step2.values(values);
|
||||
|
||||
returning = onDuplicate = step2;
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "UPDATE SET")) {
|
||||
InsertOnConflictWhereStep<?> where = doUpdate.doUpdate().set(parseSetClauseList(ctx));
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "SET")) {
|
||||
Map<Field<?>, Object> map = parseSetClauseList(ctx);
|
||||
|
||||
returning = onDuplicate = s1.set(map);
|
||||
}
|
||||
else if (peekKeyword(ctx, "SELECT", false, true, false)
|
||||
|| peekKeyword(ctx, "SEL", false, true, false)){
|
||||
SelectQueryImpl<Record> select = parseSelect(ctx);
|
||||
|
||||
returning = onDuplicate = (fields == null)
|
||||
? s1.select(select)
|
||||
: s1.columns(fields).select(select);
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "DEFAULT VALUES")) {
|
||||
if (fields != null)
|
||||
throw ctx.notImplemented("DEFAULT VALUES without INSERT field list");
|
||||
else
|
||||
returning = onDuplicate = s1.defaultValues();
|
||||
}
|
||||
else
|
||||
throw ctx.expected("DEFAULT VALUES", "SELECT", "SET", "VALUES");
|
||||
|
||||
if (parseKeywordIf(ctx, "ON")) {
|
||||
if (parseKeywordIf(ctx, "DUPLICATE KEY UPDATE")) {
|
||||
parseKeywordIf(ctx, "SET");
|
||||
|
||||
InsertOnConflictWhereStep<?> where = onDuplicate.onDuplicateKeyUpdate().set(parseSetClauseList(ctx));
|
||||
|
||||
if (parseKeywordIf(ctx, "WHERE"))
|
||||
returning = where.where(parseCondition(ctx));
|
||||
else
|
||||
returning = where;
|
||||
}
|
||||
else
|
||||
throw ctx.expected("NOTHING", "UPDATE");
|
||||
}
|
||||
else
|
||||
throw ctx.expected("CONFLICT", "DUPLICATE");
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "DUPLICATE KEY IGNORE")) {
|
||||
returning = onDuplicate.onDuplicateKeyIgnore();
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "CONFLICT")) {
|
||||
InsertOnConflictDoUpdateStep<?> doUpdate;
|
||||
|
||||
if (parseKeywordIf(ctx, "RETURNING"))
|
||||
return returning.returning(parseSelectList(ctx));
|
||||
else
|
||||
return returning;
|
||||
if (parseKeywordIf(ctx, "ON CONSTRAINT")) {
|
||||
doUpdate = onDuplicate.onConflictOnConstraint(parseName(ctx));
|
||||
}
|
||||
else if (parseIf(ctx, '(')) {
|
||||
doUpdate = onDuplicate.onConflict(parseFieldNames(ctx));
|
||||
parse(ctx, ')');
|
||||
}
|
||||
else {
|
||||
doUpdate = onDuplicate.onConflict();
|
||||
}
|
||||
|
||||
parseKeyword(ctx, "DO");
|
||||
if (parseKeywordIf(ctx, "NOTHING")) {
|
||||
returning = doUpdate.doNothing();
|
||||
}
|
||||
else if (parseKeywordIf(ctx, "UPDATE SET")) {
|
||||
InsertOnConflictWhereStep<?> where = doUpdate.doUpdate().set(parseSetClauseList(ctx));
|
||||
|
||||
if (parseKeywordIf(ctx, "WHERE"))
|
||||
returning = where.where(parseCondition(ctx));
|
||||
else
|
||||
returning = where;
|
||||
}
|
||||
else
|
||||
throw ctx.expected("NOTHING", "UPDATE");
|
||||
}
|
||||
else
|
||||
throw ctx.expected("CONFLICT", "DUPLICATE");
|
||||
}
|
||||
|
||||
if (parseKeywordIf(ctx, "RETURNING"))
|
||||
return returning.returning(parseSelectList(ctx));
|
||||
else
|
||||
return returning;
|
||||
}
|
||||
finally {
|
||||
ctx.scopeEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Update<?> parseUpdate(ParserContext ctx, WithImpl with) {
|
||||
@ -1996,6 +2008,8 @@ final class ParserImpl implements Parser {
|
||||
else if (!peekKeyword(ctx, "SET"))
|
||||
table = table.as(parseIdentifierIf(ctx));
|
||||
|
||||
ctx.scope(table);
|
||||
|
||||
UpdateSetFirstStep<?> s1 = (with == null ? ctx.dsl.update(table) : with.update(table));
|
||||
|
||||
parseKeyword(ctx, "SET");
|
||||
@ -9716,7 +9730,7 @@ final class ParserImpl implements Parser {
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
return table(name);
|
||||
return ctx.lookupTable(name);
|
||||
}
|
||||
|
||||
private static final Field<?> parseFieldNameOrSequenceExpression(ParserContext ctx) {
|
||||
@ -9760,7 +9774,7 @@ final class ParserImpl implements Parser {
|
||||
}
|
||||
|
||||
private static final TableField<?, ?> parseFieldName(ParserContext ctx) {
|
||||
return (TableField<?, ?>) field(parseName(ctx));
|
||||
return (TableField<?, ?>) ctx.lookupField(parseName(ctx));
|
||||
}
|
||||
|
||||
private static final List<Field<?>> parseFieldNames(ParserContext ctx) {
|
||||
@ -11597,22 +11611,22 @@ final class ParserImpl implements Parser {
|
||||
}
|
||||
|
||||
final class ParserContext {
|
||||
private static final boolean PRO_EDITION = false ;
|
||||
private static final boolean PRO_EDITION = false ;
|
||||
|
||||
final DSLContext dsl;
|
||||
final Locale locale;
|
||||
final Meta meta;
|
||||
final char[] sql;
|
||||
private final ParseWithMetaLookups metaLookups;
|
||||
private boolean metaLookupsForceIgnore;
|
||||
private int position = 0;
|
||||
private boolean ignoreHints = true;
|
||||
private final Object[] bindings;
|
||||
private int bindIndex = 0;
|
||||
private String delimiter = ";";
|
||||
final ScopeStack<String, Table<?>> tableScope = new ScopeStack<>(null);
|
||||
final ScopeStack<String, FieldProxy<?>> lookupFields = new ScopeStack<>(null);
|
||||
private boolean scopeClear = false;
|
||||
final DSLContext dsl;
|
||||
final Locale locale;
|
||||
final Meta meta;
|
||||
final char[] sql;
|
||||
private final ParseWithMetaLookups metaLookups;
|
||||
private boolean metaLookupsForceIgnore;
|
||||
private int position = 0;
|
||||
private boolean ignoreHints = true;
|
||||
private final Object[] bindings;
|
||||
private int bindIndex = 0;
|
||||
private String delimiter = ";";
|
||||
private final ScopeStack<String, Table<?>> tableScope = new ScopeStack<>(null);
|
||||
private final ScopeStack<String, FieldProxy<?>> lookupFields = new ScopeStack<>(null);
|
||||
private boolean scopeClear = false;
|
||||
|
||||
|
||||
|
||||
@ -11880,6 +11894,10 @@ final class ParserContext {
|
||||
+ (sql.length > position + 80 ? "..." : "");
|
||||
}
|
||||
|
||||
void scope(Table<?> table) {
|
||||
tableScope.set(table.getName(), table);
|
||||
}
|
||||
|
||||
void scopeStart() {
|
||||
tableScope.scopeStart();
|
||||
lookupFields.scopeStart();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user