[jOOQ/jOOQ#13325] Refactor internal FieldMapForUpdate to support the row assignments, too
This includes: - [jOOQ/jOOQ#13329] Add a FieldOrRowOrSelect nominal union type
This commit is contained in:
parent
953501b380
commit
c93aa4b840
@ -151,6 +151,7 @@ extends
|
||||
GroupField,
|
||||
OrderField<T>,
|
||||
FieldOrRow,
|
||||
FieldOrRowOrSelect,
|
||||
FieldOrConstraint,
|
||||
TableElement
|
||||
{
|
||||
|
||||
57
jOOQ/src/main/java/org/jooq/FieldOrRowOrSelect.java
Normal file
57
jOOQ/src/main/java/org/jooq/FieldOrRowOrSelect.java
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A common base type for {@link Field}, {@link Row}, and {@link Select} where
|
||||
* DSL API accepts all types alike.
|
||||
* <p>
|
||||
* Instances of this type cannot be created directly, only of its subtypes.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public /* sealed */ interface FieldOrRowOrSelect
|
||||
extends
|
||||
QueryPart
|
||||
/* permits
|
||||
Field,
|
||||
Row,
|
||||
Select */
|
||||
{
|
||||
|
||||
}
|
||||
@ -80,7 +80,12 @@ import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public /* non-sealed */ interface Row extends Fields, FieldOrRow {
|
||||
public /* non-sealed */ interface Row
|
||||
extends
|
||||
Fields,
|
||||
FieldOrRow,
|
||||
FieldOrRowOrSelect
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the degree of this row value expression.
|
||||
|
||||
@ -97,7 +97,13 @@ import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
* @param <R> The record type being returned by this query
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public /* non-sealed */ interface Select<R extends Record> extends ResultQuery<R>, TableLike<R>, FieldLike {
|
||||
public /* non-sealed */ interface Select<R extends Record>
|
||||
extends
|
||||
ResultQuery<R>,
|
||||
TableLike<R>,
|
||||
FieldLike,
|
||||
FieldOrRowOrSelect
|
||||
{
|
||||
|
||||
/**
|
||||
* Apply the <code>UNION</code> set operation.
|
||||
|
||||
@ -46,7 +46,6 @@ import org.jooq.CloseableQuery;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.conf.ParamType;
|
||||
import org.jooq.impl.QOM.UProxy;
|
||||
|
||||
@ -49,8 +49,11 @@ import org.jooq.QueryPart;
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
abstract class AbstractQueryPartMap<K extends QueryPart, V extends QueryPart>
|
||||
extends AbstractQueryPart
|
||||
implements Map<K, V> {
|
||||
extends
|
||||
AbstractQueryPart
|
||||
implements
|
||||
Map<K, V>
|
||||
{
|
||||
|
||||
private final Map<K, V> map;
|
||||
|
||||
|
||||
@ -43,10 +43,11 @@ import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.StoreQuery;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.impl.QOM.UEmpty;
|
||||
import org.jooq.impl.QOM.UTransient;
|
||||
|
||||
/**
|
||||
@ -54,13 +55,18 @@ import org.jooq.impl.QOM.UTransient;
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
abstract class AbstractStoreQuery<R extends Record> extends AbstractDMLQuery<R> implements StoreQuery<R> {
|
||||
abstract class AbstractStoreQuery<R extends Record, K extends FieldOrRow, V extends FieldOrRowOrSelect>
|
||||
extends
|
||||
AbstractDMLQuery<R>
|
||||
implements
|
||||
StoreQuery<R>
|
||||
{
|
||||
|
||||
AbstractStoreQuery(Configuration configuration, WithImpl with, Table<R> table) {
|
||||
super(configuration, with, table);
|
||||
}
|
||||
|
||||
protected abstract Map<Field<?>, Field<?>> getValues();
|
||||
protected abstract Map<K, V> getValues();
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
@ -87,7 +93,7 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractDMLQuery<R>
|
||||
else
|
||||
addValue(new UnknownField<T>(getValues().size()), value);
|
||||
else
|
||||
getValues().put(field, Tools.field(value, field));
|
||||
getValues().put((K) field, (V) Tools.field(value, field));
|
||||
}
|
||||
|
||||
final <T> void addValue(Field<T> field, int index, Field<T> value) {
|
||||
@ -97,7 +103,7 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractDMLQuery<R>
|
||||
else
|
||||
addValue(new UnknownField<T>(getValues().size()), value);
|
||||
else
|
||||
getValues().put(field, Tools.field(value, field));
|
||||
getValues().put((K) field, (V) Tools.field(value, field));
|
||||
}
|
||||
|
||||
static class UnknownField<T> extends AbstractField<T> implements UTransient {
|
||||
|
||||
@ -37,25 +37,47 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.Clause.UPDATE_SET_ASSIGNMENT;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.HSQLDB;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.YUGABYTEDB;
|
||||
import static org.jooq.conf.WriteIfReadonly.IGNORE;
|
||||
import static org.jooq.conf.WriteIfReadonly.THROW;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.table;
|
||||
import static org.jooq.impl.DSL.when;
|
||||
import static org.jooq.impl.Keywords.K_ROW;
|
||||
import static org.jooq.impl.Tools.anyMatch;
|
||||
import static org.jooq.impl.Tools.collect;
|
||||
import static org.jooq.impl.Tools.fieldName;
|
||||
import static org.jooq.impl.Tools.filter;
|
||||
import static org.jooq.impl.Tools.flattenEntrySet;
|
||||
import static org.jooq.impl.Tools.map;
|
||||
import static org.jooq.impl.Tools.row0;
|
||||
import static org.jooq.impl.Tools.unqualified;
|
||||
import static org.jooq.impl.Tools.visitSubquery;
|
||||
import static org.jooq.impl.Tools.DataKey.DATA_ON_DUPLICATE_KEY_WHERE;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -63,10 +85,13 @@ import org.jooq.Clause;
|
||||
import org.jooq.Condition;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
// ...
|
||||
import org.jooq.RenderContext.CastMode;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.exception.DataTypeException;
|
||||
import org.jooq.impl.QOM.UNotYetImplemented;
|
||||
@ -74,9 +99,18 @@ import org.jooq.impl.QOM.UNotYetImplemented;
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> implements UNotYetImplemented {
|
||||
private static final Set<SQLDialect> CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
|
||||
private static final Set<SQLDialect> NO_SUPPORT_QUALIFY = SQLDialect.supportedBy(POSTGRES, SQLITE, YUGABYTEDB);
|
||||
final class FieldMapForUpdate extends AbstractQueryPartMap<FieldOrRow, FieldOrRowOrSelect> implements UNotYetImplemented {
|
||||
|
||||
static final Set<SQLDialect> CASTS_NEEDED = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
|
||||
static final Set<SQLDialect> NO_SUPPORT_QUALIFY = SQLDialect.supportedBy(POSTGRES, SQLITE, YUGABYTEDB);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static final Set<SQLDialect> SUPPORT_RVE_SET = SQLDialect.supportedBy(H2, HSQLDB, POSTGRES, YUGABYTEDB);
|
||||
static final Set<SQLDialect> REQUIRE_RVE_ROW_CLAUSE = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
|
||||
|
||||
private final Table<?> table;
|
||||
private final Clause assignmentClause;
|
||||
@ -86,7 +120,6 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> i
|
||||
this.assignmentClause = assignmentClause;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
if (size() > 0) {
|
||||
@ -106,24 +139,29 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> i
|
||||
if (!CASTS_NEEDED.contains(ctx.dialect()))
|
||||
ctx.castMode(CastMode.NEVER);
|
||||
|
||||
for (Entry<Field<?>, Field<?>> entry : removeReadonly(ctx, flattenEntrySet(entrySet(), true))) {
|
||||
entryLoop:
|
||||
for (Entry<FieldOrRow, FieldOrRowOrSelect> entry : flattenEntrySet(entrySet(), true)) {
|
||||
FieldOrRow key = entry.getKey();
|
||||
FieldOrRowOrSelect value = entry.getValue();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!"".equals(separator))
|
||||
ctx.sql(separator)
|
||||
.formatSeparator();
|
||||
|
||||
ctx.start(assignmentClause)
|
||||
.qualify(supportsQualify, c -> c.visit(entry.getKey()))
|
||||
.sql(" = ");
|
||||
|
||||
// [#8479] Emulate WHERE clause using CASE
|
||||
Condition condition = (Condition) ctx.data(DATA_ON_DUPLICATE_KEY_WHERE);
|
||||
if (condition != null)
|
||||
ctx.visit(when(condition, (Field) entry.getValue()).else_(entry.getKey()));
|
||||
else
|
||||
ctx.visit(entry.getValue());
|
||||
|
||||
ctx.end(assignmentClause);
|
||||
|
||||
acceptAssignmentClause(ctx, supportsQualify, key, value);
|
||||
separator = ",";
|
||||
}
|
||||
|
||||
@ -134,12 +172,114 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> i
|
||||
ctx.sql("[ no fields are updated ]");
|
||||
}
|
||||
|
||||
static final Iterable<Entry<Field<?>, Field<?>>> removeReadonly(Context<?> ctx, Iterable<Entry<Field<?>, Field<?>>> it) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final void acceptAssignmentClause(
|
||||
Context<?> ctx,
|
||||
boolean supportsQualify,
|
||||
FieldOrRow key,
|
||||
FieldOrRowOrSelect value
|
||||
) {
|
||||
ctx.start(assignmentClause);
|
||||
|
||||
// A multi-row update was specified
|
||||
if (key instanceof Row) { Row multiRow = (Row) key;
|
||||
Row multiValue = value instanceof Row ? (Row) value : null;
|
||||
Select<?> multiSelect = value instanceof Select ? (Select<?>) value : null;
|
||||
|
||||
// [#6884] This syntax can be emulated trivially, if the RHS is not a SELECT subquery
|
||||
if (multiValue != null && !SUPPORT_RVE_SET.contains(ctx.dialect())) {
|
||||
FieldMapForUpdate map = new FieldMapForUpdate(table(), UPDATE_SET_ASSIGNMENT);
|
||||
|
||||
for (int i = 0; i < multiRow.size(); i++) {
|
||||
Field<?> k = multiRow.field(i);
|
||||
Field<?> v = multiValue.field(i);
|
||||
|
||||
map.put(k, Tools.field(v, k));
|
||||
}
|
||||
|
||||
toSQLUpdateMap(ctx, map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return it;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else {
|
||||
Row row = removeReadonly(ctx, multiRow);
|
||||
|
||||
ctx.start(UPDATE_SET_ASSIGNMENT)
|
||||
.formatIndentStart()
|
||||
.formatSeparator()
|
||||
.qualify(false, c -> c.visit(row))
|
||||
.sql(" = ");
|
||||
|
||||
// Some dialects don't really support row value expressions on the
|
||||
// right hand side of a SET clause
|
||||
if (multiValue != null
|
||||
|
||||
|
||||
|
||||
) {
|
||||
|
||||
// [#6763] Incompatible change in PostgreSQL 10 requires ROW() constructor for
|
||||
// single-degree rows. Let's just always render it, here.
|
||||
if (REQUIRE_RVE_ROW_CLAUSE.contains(ctx.dialect()))
|
||||
ctx.visit(K_ROW).sql(" ");
|
||||
|
||||
ctx.visit(removeReadonly(ctx, multiRow, multiValue));
|
||||
}
|
||||
|
||||
// Subselects or subselect emulations of row value expressions
|
||||
else if (multiSelect != null) {
|
||||
Select<?> select;
|
||||
|
||||
if (multiValue != null)
|
||||
select = select(removeReadonly(ctx, multiRow, multiValue).fields());
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
select = multiSelect;
|
||||
|
||||
visitSubquery(ctx, select, false, false, false);
|
||||
}
|
||||
|
||||
ctx.formatIndentEnd().end(UPDATE_SET_ASSIGNMENT);
|
||||
}
|
||||
}
|
||||
|
||||
// A regular (non-multi-row) update was specified
|
||||
else {
|
||||
ctx.qualify(supportsQualify, c -> c.visit(key))
|
||||
.sql(" = ");
|
||||
|
||||
// [#8479] Emulate WHERE clause using CASE
|
||||
Condition condition = (Condition) ctx.data(DATA_ON_DUPLICATE_KEY_WHERE);
|
||||
if (condition != null)
|
||||
ctx.visit(when(condition, (Field) value).else_(key));
|
||||
else
|
||||
ctx.visit(value);
|
||||
}
|
||||
|
||||
ctx.end(assignmentClause);
|
||||
}
|
||||
|
||||
static final void toSQLUpdateMap(Context<?> ctx, FieldMapForUpdate updateMap) {
|
||||
ctx.formatIndentStart()
|
||||
.formatSeparator()
|
||||
.visit(updateMap)
|
||||
.formatIndentEnd();
|
||||
}
|
||||
|
||||
static final Row removeReadonly(Context<?> ctx, Row row) {
|
||||
@ -177,4 +317,5 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> i
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -38,7 +38,6 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static org.jooq.Clause.FIELD_ROW;
|
||||
import static org.jooq.Clause.INSERT_SELECT;
|
||||
@ -56,10 +55,8 @@ import static org.jooq.impl.Keywords.K_VALUES;
|
||||
import static org.jooq.impl.QueryPartCollectionView.wrap;
|
||||
import static org.jooq.impl.Tools.EMPTY_FIELD;
|
||||
import static org.jooq.impl.Tools.anyMatch;
|
||||
import static org.jooq.impl.Tools.collect;
|
||||
import static org.jooq.impl.Tools.filter;
|
||||
import static org.jooq.impl.Tools.flatten;
|
||||
import static org.jooq.impl.Tools.flattenCollection;
|
||||
import static org.jooq.impl.Tools.lazy;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_EMULATE_BULK_INSERT_RETURNING;
|
||||
|
||||
@ -67,7 +64,6 @@ import java.util.AbstractList;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@ -83,21 +79,19 @@ import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.Param;
|
||||
// ...
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RenderContext.CastMode;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.SelectJoinStep;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.conf.WriteIfReadonly;
|
||||
import org.jooq.exception.DataTypeException;
|
||||
import org.jooq.impl.AbstractStoreQuery.UnknownField;
|
||||
import org.jooq.impl.QOM.UNotYetImplemented;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@ -219,7 +213,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
|
||||
.end(INSERT_SELECT);
|
||||
}
|
||||
|
||||
static final Set<Field<?>> keysAndComputedOnClient(Set<Field<?>> keys, Table<?> table) {
|
||||
static final <K extends FieldOrRow> Set<K> keysAndComputedOnClient(Set<K> keys, Table<?> table) {
|
||||
|
||||
|
||||
|
||||
@ -289,6 +283,8 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -56,7 +56,6 @@ import org.jooq.FieldLike;
|
||||
import org.jooq.InsertOnConflictConditionStep;
|
||||
import org.jooq.InsertOnConflictWhereIndexPredicateStep;
|
||||
import org.jooq.InsertOnDuplicateSetMoreStep;
|
||||
import org.jooq.InsertQuery;
|
||||
import org.jooq.InsertResultStep;
|
||||
import org.jooq.InsertSetMoreStep;
|
||||
import org.jooq.InsertSetStep;
|
||||
@ -822,21 +821,21 @@ final class InsertImpl<R extends Record, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
|
||||
return values(values.toArray());
|
||||
}
|
||||
|
||||
private final <T> void addValue(InsertQuery<R> delegate, Field<T> field, int index, Object object) {
|
||||
private final <T> void addValue(InsertQueryImpl<R> delegate, Field<T> field, int index, Object object) {
|
||||
|
||||
// [#1343] Only convert non-jOOQ objects
|
||||
// [#8606] The column index is relevant when adding a value to a plain SQL multi row INSERT
|
||||
// statement that does not have any field list.
|
||||
if (object instanceof Field)
|
||||
((AbstractStoreQuery<R>) delegate).addValue(field, index, (Field) object);
|
||||
delegate.addValue(field, index, (Field) object);
|
||||
else if (object instanceof FieldLike)
|
||||
((AbstractStoreQuery<R>) delegate).addValue(field, index, ((FieldLike) object).asField());
|
||||
delegate.addValue(field, index, ((FieldLike) object).asField());
|
||||
else if (field != null)
|
||||
((AbstractStoreQuery<R>) delegate).addValue(field, index, field.getDataType().convert(object));
|
||||
delegate.addValue(field, index, field.getDataType().convert(object));
|
||||
|
||||
// [#4629] Plain SQL INSERT INTO t VALUES (a, b, c) statements don't know the insert columns
|
||||
else
|
||||
((AbstractStoreQuery<R>) delegate).addValue(field, index, (T) object);
|
||||
delegate.addValue(field, index, (T) object);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -48,7 +48,6 @@ import static org.jooq.Clause.INSERT_INSERT_INTO;
|
||||
import static org.jooq.Clause.INSERT_ON_DUPLICATE_KEY_UPDATE;
|
||||
import static org.jooq.Clause.INSERT_ON_DUPLICATE_KEY_UPDATE_ASSIGNMENT;
|
||||
import static org.jooq.Clause.INSERT_RETURNING;
|
||||
import static org.jooq.Clause.INSERT_SELECT;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.DERBY;
|
||||
@ -102,7 +101,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jooq.Clause;
|
||||
import org.jooq.Condition;
|
||||
@ -112,7 +110,6 @@ import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Identity;
|
||||
import org.jooq.InsertQuery;
|
||||
import org.jooq.Merge;
|
||||
import org.jooq.MergeNotMatchedStep;
|
||||
import org.jooq.MergeOnConditionStep;
|
||||
import org.jooq.Name;
|
||||
@ -134,7 +131,13 @@ import org.jooq.tools.StringUtils;
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class InsertQueryImpl<R extends Record> extends AbstractStoreQuery<R> implements InsertQuery<R>, UNotYetImplemented {
|
||||
final class InsertQueryImpl<R extends Record>
|
||||
extends
|
||||
AbstractStoreQuery<R, Field<?>, Field<?>>
|
||||
implements
|
||||
InsertQuery<R>,
|
||||
UNotYetImplemented
|
||||
{
|
||||
|
||||
private static final Clause[] CLAUSES = { INSERT };
|
||||
private static final Set<SQLDialect> SUPPORT_INSERT_IGNORE = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
|
||||
@ -109,6 +109,8 @@ import org.jooq.Condition;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
import org.jooq.MergeKeyStep1;
|
||||
import org.jooq.MergeKeyStep10;
|
||||
import org.jooq.MergeKeyStep11;
|
||||
@ -1627,11 +1629,13 @@ implements
|
||||
if (update == null)
|
||||
update = new MatchedClause(noCondition());
|
||||
|
||||
for (Entry<Field<?>, Field<?>> e : m.updateMap.entrySet()) {
|
||||
Field<?> exp = update.updateMap.get(e.getKey());
|
||||
for (Entry<FieldOrRow, FieldOrRowOrSelect> e : m.updateMap.entrySet()) {
|
||||
FieldOrRowOrSelect exp = update.updateMap.get(e.getKey());
|
||||
|
||||
if (exp instanceof CaseConditionStepImpl)
|
||||
((CaseConditionStepImpl) exp).when(negate.and(condition), e.getValue());
|
||||
|
||||
// [#10523] [#13325] TODO: ClassCastException when we're using Row here, once supported
|
||||
else
|
||||
update.updateMap.put(e.getKey(), when(negate.and(condition), (Field) e.getValue()).else_(e.getKey()));
|
||||
}
|
||||
|
||||
@ -268,6 +268,7 @@ import org.jooq.ExecuteContext;
|
||||
import org.jooq.ExecuteListener;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
import org.jooq.Fields;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.JSON;
|
||||
@ -5676,6 +5677,13 @@ final class Tools {
|
||||
return map(orderFields, (OrderField<?> f) -> field(f));
|
||||
}
|
||||
|
||||
static final List<Field<?>> fields(FieldOrRow fr) {
|
||||
if (fr instanceof Field)
|
||||
return singletonList((Field<?>) fr);
|
||||
else
|
||||
return asList(((Row) fr).fields());
|
||||
}
|
||||
|
||||
static final <T> Field<T> unalias(Field<T> field) {
|
||||
Field<T> result = aliased(field);
|
||||
return result != null ? result : field;
|
||||
@ -6099,23 +6107,25 @@ final class Tools {
|
||||
* set iterable, making sure no duplicate keys resulting from overlapping
|
||||
* embeddables will be produced.
|
||||
*/
|
||||
static final Iterable<Entry<Field<?>, Field<?>>> flattenEntrySet(
|
||||
final Iterable<Entry<Field<?>, Field<?>>> iterable,
|
||||
static final Iterable<Entry<FieldOrRow, FieldOrRowOrSelect>> flattenEntrySet(
|
||||
final Iterable<Entry<FieldOrRow, FieldOrRowOrSelect>> iterable,
|
||||
final boolean removeDuplicates
|
||||
) {
|
||||
// [#2530] [#6124] [#10481] TODO: Refactor and optimise these flattening algorithms
|
||||
// [#11729] Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=572873
|
||||
return () -> new FlatteningIterator<>(iterable.iterator(), (e, duplicates) -> {
|
||||
if (e.getKey() instanceof EmbeddableTableField) {
|
||||
List<Entry<Field<?>, Field<?>>> result = new ArrayList<>();
|
||||
Field<?>[] keys = embeddedFields(e.getKey());
|
||||
Field<?>[] values = embeddedFields(e.getValue());
|
||||
|
||||
// [#9879] [#13325] TODO: Support also UPDATE .. SET ROW = ...
|
||||
if (e.getKey() instanceof EmbeddableTableField) { EmbeddableTableField<?, ?> key = (EmbeddableTableField<?, ?>) e.getKey();
|
||||
List<Entry<FieldOrRow, FieldOrRowOrSelect>> result = new ArrayList<>();
|
||||
Field<?>[] keys = embeddedFields(key);
|
||||
Field<?>[] values = embeddedFields((Field<?>) e.getValue());
|
||||
|
||||
for (int i = 0; i < keys.length; i++)
|
||||
|
||||
|
||||
|
||||
result.add(new SimpleImmutableEntry<Field<?>, Field<?>>(
|
||||
result.add(new SimpleImmutableEntry<FieldOrRow, FieldOrRowOrSelect>(
|
||||
keys[i], values[i]
|
||||
));
|
||||
|
||||
|
||||
@ -52,7 +52,6 @@ import static org.jooq.Clause.UPDATE_WHERE;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.DERBY;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.FIREBIRD;
|
||||
@ -68,8 +67,6 @@ import static org.jooq.SQLDialect.POSTGRES;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
@ -77,30 +74,22 @@ import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.YUGABYTEDB;
|
||||
import static org.jooq.conf.SettingsTools.getExecuteUpdateWithoutWhere;
|
||||
import static org.jooq.conf.WriteIfReadonly.THROW;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.trueCondition;
|
||||
import static org.jooq.impl.FieldMapForUpdate.removeReadonly;
|
||||
import static org.jooq.impl.FieldMapsForInsert.keysAndComputedOnClient;
|
||||
import static org.jooq.impl.Keywords.K_FROM;
|
||||
import static org.jooq.impl.Keywords.K_LIMIT;
|
||||
import static org.jooq.impl.Keywords.K_ORDER_BY;
|
||||
import static org.jooq.impl.Keywords.K_ROW;
|
||||
import static org.jooq.impl.Keywords.K_SET;
|
||||
import static org.jooq.impl.Keywords.K_UPDATE;
|
||||
import static org.jooq.impl.Keywords.K_WHERE;
|
||||
import static org.jooq.impl.Tools.EMPTY_FIELD;
|
||||
import static org.jooq.impl.Tools.fieldName;
|
||||
import static org.jooq.impl.Tools.map;
|
||||
import static org.jooq.impl.Tools.unqualified;
|
||||
import static org.jooq.impl.Tools.visitSubquery;
|
||||
import static org.jooq.impl.Tools.anyMatch;
|
||||
import static org.jooq.impl.Tools.findAny;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
@ -110,6 +99,8 @@ import org.jooq.Condition;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
import org.jooq.Operator;
|
||||
import org.jooq.OrderField;
|
||||
// ...
|
||||
@ -166,13 +157,18 @@ import org.jooq.Select;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableLike;
|
||||
import org.jooq.UpdateQuery;
|
||||
import org.jooq.exception.DataTypeException;
|
||||
import org.jooq.impl.QOM.UNotYetImplemented;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> implements UpdateQuery<R>, UNotYetImplemented {
|
||||
final class UpdateQueryImpl<R extends Record>
|
||||
extends
|
||||
AbstractStoreQuery<R, FieldOrRow, FieldOrRowOrSelect>
|
||||
implements
|
||||
UpdateQuery<R>,
|
||||
UNotYetImplemented
|
||||
{
|
||||
|
||||
private static final Clause[] CLAUSES = { UPDATE };
|
||||
|
||||
@ -182,13 +178,6 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static final Set<SQLDialect> SUPPORT_RVE_SET = SQLDialect.supportedBy(H2, HSQLDB, POSTGRES, YUGABYTEDB);
|
||||
private static final Set<SQLDialect> REQUIRE_RVE_ROW_CLAUSE = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
|
||||
|
||||
// LIMIT is not supported at all
|
||||
private static final Set<SQLDialect> NO_SUPPORT_LIMIT = SQLDialect.supportedUntil(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, POSTGRES, SQLITE, YUGABYTEDB);
|
||||
|
||||
@ -198,9 +187,6 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
private final FieldMapForUpdate updateMap;
|
||||
private final TableList from;
|
||||
private final ConditionProviderImpl condition;
|
||||
private Row multiRow;
|
||||
private Row multiValue;
|
||||
private Select<?> multiSelect;
|
||||
private final SortFieldList orderBy;
|
||||
private Field<? extends Number> limit;
|
||||
|
||||
@ -453,13 +439,11 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
final void addValues0(Row row, Row value) {
|
||||
multiRow = row;
|
||||
multiValue = value;
|
||||
updateMap.put(row, value);
|
||||
}
|
||||
|
||||
final void addValues0(Row row, Select<?> select) {
|
||||
multiRow = row;
|
||||
multiSelect = select;
|
||||
updateMap.put(row, select);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -564,9 +548,6 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -580,9 +561,6 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
u.setReturning(returning);
|
||||
|
||||
u.updateMap.putAll(updateMap);
|
||||
u.multiRow = multiRow;
|
||||
u.multiSelect = multiSelect;
|
||||
u.multiValue = multiValue;
|
||||
u.from.addAll(from);
|
||||
u.condition.setWhere(condition.getWhere());
|
||||
u.orderBy.addAll(orderBy);
|
||||
@ -622,86 +600,7 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
.visit(K_SET)
|
||||
.separatorRequired(true);
|
||||
|
||||
// A multi-row update was specified
|
||||
if (multiRow != null) {
|
||||
|
||||
// [#6884] This syntax can be emulated trivially, if the RHS is not a SELECT subquery
|
||||
if (multiValue != null && !SUPPORT_RVE_SET.contains(ctx.dialect())) {
|
||||
FieldMapForUpdate map = new FieldMapForUpdate(table(), UPDATE_SET_ASSIGNMENT);
|
||||
|
||||
for (int i = 0; i < multiRow.size(); i++) {
|
||||
Field<?> k = multiRow.field(i);
|
||||
Field<?> v = multiValue.field(i);
|
||||
|
||||
map.put(k, Tools.field(v, k));
|
||||
}
|
||||
|
||||
toSQLUpdateMap(ctx, map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
else {
|
||||
Row row = removeReadonly(ctx, multiRow);
|
||||
|
||||
ctx.start(UPDATE_SET_ASSIGNMENT)
|
||||
.formatIndentStart()
|
||||
.formatSeparator()
|
||||
.qualify(false, c -> c.visit(row))
|
||||
.sql(" = ");
|
||||
|
||||
// Some dialects don't really support row value expressions on the
|
||||
// right hand side of a SET clause
|
||||
if (multiValue != null
|
||||
|
||||
|
||||
|
||||
) {
|
||||
|
||||
// [#6763] Incompatible change in PostgreSQL 10 requires ROW() constructor for
|
||||
// single-degree rows. Let's just always render it, here.
|
||||
if (REQUIRE_RVE_ROW_CLAUSE.contains(ctx.dialect()))
|
||||
ctx.visit(K_ROW).sql(" ");
|
||||
|
||||
ctx.visit(removeReadonly(ctx, multiRow, multiValue));
|
||||
}
|
||||
|
||||
// Subselects or subselect emulations of row value expressions
|
||||
else {
|
||||
Select<?> select;
|
||||
|
||||
if (multiValue != null)
|
||||
select = select(removeReadonly(ctx, multiRow, multiValue).fields());
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
select = multiSelect;
|
||||
|
||||
visitSubquery(ctx, select, false, false, false);
|
||||
}
|
||||
|
||||
ctx.formatIndentEnd().end(UPDATE_SET_ASSIGNMENT);
|
||||
}
|
||||
}
|
||||
|
||||
// A regular (non-multi-row) update was specified
|
||||
else
|
||||
toSQLUpdateMap(ctx, updateMap);
|
||||
|
||||
FieldMapForUpdate.toSQLUpdateMap(ctx, updateMap);
|
||||
ctx.end(UPDATE_SET);
|
||||
|
||||
|
||||
@ -763,32 +662,6 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
ctx.end(UPDATE_RETURNING);
|
||||
}
|
||||
|
||||
private static final void toSQLUpdateMap(Context<?> ctx, FieldMapForUpdate updateMap) {
|
||||
ctx.formatIndentStart()
|
||||
.formatSeparator()
|
||||
.visit(updateMap)
|
||||
.formatIndentEnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private final void acceptFrom(Context<?> ctx) {
|
||||
ctx.start(UPDATE_FROM);
|
||||
|
||||
@ -801,6 +674,8 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
f = from;
|
||||
|
||||
if (!f.isEmpty())
|
||||
@ -823,7 +698,7 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
if (!condition.hasWhere())
|
||||
executeWithoutWhere("UPDATE without WHERE", getExecuteUpdateWithoutWhere(configuration().settings()));
|
||||
|
||||
return updateMap.size() > 0 || multiRow != null;
|
||||
return updateMap.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
@ -835,4 +710,11 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user