diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java index 503da13646..8e782c5048 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java @@ -7312,6 +7312,34 @@ public class JavaGenerator extends AbstractGenerator { } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + // [#1596] Updatable tables can provide fields for optimistic locking if properly configured. // [#7904] Records being updatable isn't a strict requirement. Version and timestamp values // can still be generated diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java index faea1ad9c6..4ba2f9a052 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java @@ -89,6 +89,7 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -316,6 +317,7 @@ public abstract class AbstractDatabase implements Database { + private transient Map> xmlSchemaCollectionsBySchema; private transient Map> udtsBySchema; private transient Map> udtsByPackage; @@ -2918,6 +2920,14 @@ public abstract class AbstractDatabase implements Database { + + + + + + + + @@ -3069,16 +3079,26 @@ public abstract class AbstractDatabase implements Database { if (indexesByTable == null) indexesByTable = new HashMap<>(); - List list = indexesByTable.get(table); + return getTableObjects(table, indexesByTable, this::getIndexes, IndexDefinition::getTable); + } + + private final List getTableObjects( + TableDefinition table, + Map> map, + Function> f, + Function t + ) { + List list = map.get(table); + if (list == null) { - indexesByTable.put(table, list = new ArrayList<>()); + map.put(table, list = new ArrayList<>()); for (TableDefinition otherTable : getTables(table.getSchema())) - if (!indexesByTable.containsKey(otherTable)) - indexesByTable.put(otherTable, new ArrayList<>()); + if (!map.containsKey(otherTable)) + map.put(otherTable, new ArrayList<>()); - for (IndexDefinition index : getIndexes(table.getSchema())) - indexesByTable.computeIfAbsent(index.getTable(), k -> new ArrayList<>()).add(index); + for (D d : f.apply(table.getSchema())) + map.computeIfAbsent(t.apply(d), k -> new ArrayList<>()).add(d); } return list; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java index 2260c45939..63c3833318 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractTableDefinition.java @@ -236,6 +236,15 @@ implements return getDatabase().getRelations().getCheckConstraints(this); } + + + + + + + + + @Override public final IdentityDefinition getIdentity() { for (ColumnDefinition column : getColumns()) diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/Database.java b/jOOQ-meta/src/main/java/org/jooq/meta/Database.java index f824eba08e..5aaaf7f5f4 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/Database.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/Database.java @@ -342,6 +342,12 @@ public interface Database extends AutoCloseable { + + + + + + diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java index 1a4495f8e0..46f3b45eec 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/TableDefinition.java @@ -40,6 +40,7 @@ package org.jooq.meta; import java.util.List; +// ... import org.jooq.Record; import org.jooq.Table; import org.jooq.TableOptions; @@ -155,6 +156,16 @@ public interface TableDefinition extends Definition { */ List getCheckConstraints(); + + + + + + + + + + /** * Get the IDENTITY column of this table, or null, * if no such column exists. diff --git a/jOOQ/src/main/java/org/jooq/Table.java b/jOOQ/src/main/java/org/jooq/Table.java index 17bd8e5ebd..71e7a4b993 100644 --- a/jOOQ/src/main/java/org/jooq/Table.java +++ b/jOOQ/src/main/java/org/jooq/Table.java @@ -350,6 +350,14 @@ extends + + + + + + + + diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index 7edd012713..ce751e182f 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -127,6 +127,9 @@ public class Settings protected Boolean bindOffsetTimeType = false; @XmlElement(defaultValue = "true") protected Boolean fetchTriggerValuesAfterSQLServerOutput = true; + @XmlElement(defaultValue = "WHEN_NEEDED") + @XmlSchemaType(name = "string") + protected FetchTriggerValuesAfterReturning fetchTriggerValuesAfterReturning = FetchTriggerValuesAfterReturning.WHEN_NEEDED; @XmlElement(defaultValue = "WHEN_RESULT_REQUESTED") @XmlSchemaType(name = "string") protected FetchIntermediateResult fetchIntermediateResult = FetchIntermediateResult.WHEN_RESULT_REQUESTED; @@ -1145,7 +1148,7 @@ public class Settings * is only supported for single row inserts. *

* This OUTPUT clause does not support fetching trigger generated values. In order - * to fetch trigger generated values, {@link #fetchTriggerValuesAfterSQLServerOutput} needs to + * to fetch trigger generated values, {@link #fetchTriggerValuesAfterReturning} needs to * be enabled as well. *

* For details, see https://github.com/jOOQ/jOOQ/issues/4498. @@ -1461,12 +1464,15 @@ public class Settings * included in the OUTPUT clause. *

* For details, see https://github.com/jOOQ/jOOQ/issues/4498. + *

+ * @deprecated - 3.18.0 - [#13912] [#15316] - Use {@link #fetchTriggerValuesAfterReturning} instead. * * @return * possible object is * {@link Boolean } * */ + @Deprecated public Boolean isFetchTriggerValuesAfterSQLServerOutput() { return fetchTriggerValuesAfterSQLServerOutput; } @@ -1479,10 +1485,47 @@ public class Settings * {@link Boolean } * */ + @Deprecated public void setFetchTriggerValuesAfterSQLServerOutput(Boolean value) { this.fetchTriggerValuesAfterSQLServerOutput = value; } + /** + * Fetch trigger values after a RETURNING clause in dialects that don't have native support for this. + *

+ * SQL Server OUTPUT clauses do not support fetching trigger generated values. + * Neither do SQLite RETURNING clauses. An additional + * MERGE statement can run a second query if (and only if) the primary key has been + * included in the OUTPUT clause. + *

+ * Trigger meta data is only available in jOOQ's commercial editions. If setting this flag to + * WHEN_NEEDED in the jOOQ Open Source Edition, jOOQ will assume triggers are present. + *

+ * For details, see https://github.com/jOOQ/jOOQ/issues/4498. + * + */ + public FetchTriggerValuesAfterReturning getFetchTriggerValuesAfterReturning() { + return fetchTriggerValuesAfterReturning; + } + + /** + * Fetch trigger values after a RETURNING clause in dialects that don't have native support for this. + *

+ * SQL Server OUTPUT clauses do not support fetching trigger generated values. + * Neither do SQLite RETURNING clauses. An additional + * MERGE statement can run a second query if (and only if) the primary key has been + * included in the OUTPUT clause. + *

+ * Trigger meta data is only available in jOOQ's commercial editions. If setting this flag to + * WHEN_NEEDED in the jOOQ Open Source Edition, jOOQ will assume triggers are present. + *

+ * For details, see https://github.com/jOOQ/jOOQ/issues/4498. + * + */ + public void setFetchTriggerValuesAfterReturning(FetchTriggerValuesAfterReturning value) { + this.fetchTriggerValuesAfterReturning = value; + } + /** * Whether to fetch data into intermediate {@link org.jooq.Result} instances. *

@@ -6373,6 +6416,25 @@ public class Settings return this; } + /** + * Fetch trigger values after a RETURNING clause in dialects that don't have native support for this. + *

+ * SQL Server OUTPUT clauses do not support fetching trigger generated values. + * Neither do SQLite RETURNING clauses. An additional + * MERGE statement can run a second query if (and only if) the primary key has been + * included in the OUTPUT clause. + *

+ * Trigger meta data is only available in jOOQ's commercial editions. If setting this flag to + * WHEN_NEEDED in the jOOQ Open Source Edition, jOOQ will assume triggers are present. + *

+ * For details, see https://github.com/jOOQ/jOOQ/issues/4498. + * + */ + public Settings withFetchTriggerValuesAfterReturning(FetchTriggerValuesAfterReturning value) { + setFetchTriggerValuesAfterReturning(value); + return this; + } + /** * Whether to fetch data into intermediate {@link org.jooq.Result} instances. *

@@ -7705,6 +7767,7 @@ public class Settings builder.append("bindOffsetDateTimeType", bindOffsetDateTimeType); builder.append("bindOffsetTimeType", bindOffsetTimeType); builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput); + builder.append("fetchTriggerValuesAfterReturning", fetchTriggerValuesAfterReturning); builder.append("fetchIntermediateResult", fetchIntermediateResult); builder.append("diagnosticsDuplicateStatements", diagnosticsDuplicateStatements); builder.append("diagnosticsDuplicateStatementsUsingTransformPatterns", diagnosticsDuplicateStatementsUsingTransformPatterns); @@ -8253,6 +8316,15 @@ public class Settings return false; } } + if (fetchTriggerValuesAfterReturning == null) { + if (other.fetchTriggerValuesAfterReturning!= null) { + return false; + } + } else { + if (!fetchTriggerValuesAfterReturning.equals(other.fetchTriggerValuesAfterReturning)) { + return false; + } + } if (fetchIntermediateResult == null) { if (other.fetchIntermediateResult!= null) { return false; @@ -9963,6 +10035,7 @@ public class Settings result = ((prime*result)+((bindOffsetDateTimeType == null)? 0 :bindOffsetDateTimeType.hashCode())); result = ((prime*result)+((bindOffsetTimeType == null)? 0 :bindOffsetTimeType.hashCode())); result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode())); + result = ((prime*result)+((fetchTriggerValuesAfterReturning == null)? 0 :fetchTriggerValuesAfterReturning.hashCode())); result = ((prime*result)+((fetchIntermediateResult == null)? 0 :fetchIntermediateResult.hashCode())); result = ((prime*result)+((diagnosticsDuplicateStatements == null)? 0 :diagnosticsDuplicateStatements.hashCode())); result = ((prime*result)+((diagnosticsDuplicateStatementsUsingTransformPatterns == null)? 0 :diagnosticsDuplicateStatementsUsingTransformPatterns.hashCode())); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index fe66f84726..48523f7303 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -45,10 +45,8 @@ import static java.lang.Boolean.FALSE; import static org.jooq.SQLDialect.CUBRID; // ... import static org.jooq.SQLDialect.DERBY; -import static org.jooq.SQLDialect.DUCKDB; // ... import static org.jooq.SQLDialect.FIREBIRD; -// ... import static org.jooq.SQLDialect.H2; // ... // ... @@ -77,7 +75,6 @@ import static org.jooq.impl.CommonTableExpressionList.markTopLevelCteAndAccept; import static org.jooq.impl.DSL.name; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.unquotedName; -import static org.jooq.impl.InlineDerivedTable.inlineDerivedTable; import static org.jooq.impl.Keywords.K_BEGIN; import static org.jooq.impl.Keywords.K_BULK_COLLECT_INTO; import static org.jooq.impl.Keywords.K_DECLARE; @@ -98,7 +95,6 @@ import static org.jooq.impl.Names.N_DELETED; import static org.jooq.impl.Names.N_INSERTED; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.EMPTY_STRING; -import static org.jooq.impl.Tools.aliased; import static org.jooq.impl.Tools.anyMatch; import static org.jooq.impl.Tools.autoAlias; import static org.jooq.impl.Tools.flattenCollection; @@ -107,11 +103,13 @@ import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.reference; import static org.jooq.impl.Tools.removeGenerator; import static org.jooq.impl.Tools.unalias; +import static org.jooq.impl.Tools.updateQueryImpl; import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASED_EXPRESSIONS; import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_TARGET_TABLE; import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_USING_TABLES; import static org.jooq.impl.Tools.SimpleDataKey.DATA_RENDERING_DATA_CHANGE_DELTA_TABLE; import static org.jooq.impl.Tools.SimpleDataKey.DATA_TOP_LEVEL_CTE; +import static org.jooq.tools.StringUtils.defaultIfNull; import static org.jooq.util.sqlite.SQLiteDSL.rowid; import java.sql.CallableStatement; @@ -123,6 +121,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -160,9 +159,12 @@ import org.jooq.Select; import org.jooq.SelectFieldOrAsterisk; import org.jooq.Table; import org.jooq.TableField; +// ... +// ... import org.jooq.UniqueKey; import org.jooq.Update; import org.jooq.conf.ExecuteWithoutWhere; +import org.jooq.conf.FetchTriggerValuesAfterReturning; import org.jooq.conf.RenderNameCase; import org.jooq.conf.SettingsTools; import org.jooq.exception.DataAccessException; @@ -673,6 +675,44 @@ abstract class AbstractDMLQuery extends AbstractRowCountQuery ctx.data().remove(DATA_DML_TARGET_TABLE); } + @Pro + private final boolean fetchTriggerValuesAfterReturning(Scope ctx) { + if (this instanceof Delete) + return false; + + if (FALSE.equals(ctx.settings().isFetchTriggerValuesAfterSQLServerOutput())) + return false; + + switch (defaultIfNull(ctx.settings().getFetchTriggerValuesAfterReturning(), FetchTriggerValuesAfterReturning.WHEN_NEEDED)) { + case ALWAYS: + return true; + case NEVER: + return false; + case WHEN_NEEDED: + if (ctx.configuration().commercial()) { + + + + } + + return true; + default: + throw new IllegalStateException("Unsupported value: " + ctx.settings().getFetchTriggerValuesAfterReturning()); + } + } + + + + + + + + + + + + + @@ -857,6 +897,10 @@ abstract class AbstractDMLQuery extends AbstractRowCountQuery + + + + diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index dd379e6173..1e81448f81 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -122,6 +122,7 @@ import org.jooq.TableOptions; import org.jooq.TableOptions.TableType; import org.jooq.TableOuterJoinStep; import org.jooq.TablePartitionByStep; +// ... import org.jooq.UniqueKey; // ... // ... @@ -624,6 +625,20 @@ implements return Collections.emptyList(); } + + + + + + + + + + + + + + /** * Subclasses may call this method to create {@link TableField} objects that * are linked to this table. diff --git a/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java b/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java index fc52633e36..aa1b640dc6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java @@ -913,6 +913,13 @@ package org.jooq.impl; + + + + + + + diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index c7187ca0b0..4cbcc87eb5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -98,6 +98,7 @@ 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.SQLDataType.INTEGER; +import static org.jooq.impl.Tools.anyMatch; import static org.jooq.impl.Tools.containsDeclaredTable; import static org.jooq.impl.Tools.findAny; @@ -695,6 +696,14 @@ implements ctx.visit(mergeInto(table).using(s).on(c).whenMatchedThenUpdate().set(um)); } + final boolean updatesField(Field field) { + return anyMatch(updateMap.keySet(), fr -> field.equals(fr) || fr instanceof Row && ((Row) fr).field(field) != null); + } + + final boolean updatesAnyField(Collection> fields) { + return anyMatch(fields, this::updatesField); + } + final UpdateQueryImpl copy(Consumer> finisher) { return copy(finisher, table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Updating.java b/jOOQ/src/main/java/org/jooq/impl/Updating.java index 6342b40e9e..4bf87c50ca 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Updating.java +++ b/jOOQ/src/main/java/org/jooq/impl/Updating.java @@ -137,6 +137,5 @@ package org.jooq.impl; - diff --git a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.19.0.xsd b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.19.0.xsd index 55681ae34a..265d11d564 100644 --- a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.19.0.xsd +++ b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.19.0.xsd @@ -215,7 +215,7 @@ to revert to jOOQ calling {@code java.sql.Statement#getGeneratedKeys()} instead, is only supported for single row inserts.

This OUTPUT clause does not support fetching trigger generated values. In order -to fetch trigger generated values, {@link #fetchTriggerValuesAfterSQLServerOutput} needs to +to fetch trigger generated values, {@link #fetchTriggerValuesAfterReturning} needs to be enabled as well.

For details, see https://github.com/jOOQ/jOOQ/issues/4498.]]> @@ -308,13 +308,37 @@ For details, see https://gith - OUTPUT clause. + + + + OUTPUT clause.

SQL Server OUTPUT statements do not support fetching trigger generated values. This is a limitation of the {@link #renderOutputForSQLServerReturningClause}. An additional MERGE statement can run a second query if (and only if) the primary key has been included in the OUTPUT clause.

+For details, see https://github.com/jOOQ/jOOQ/issues/4498. +

+@deprecated - 3.18.0 - [#13912] [#15316] - Use {@link #fetchTriggerValuesAfterReturning} instead.]]> + + @java.lang.Deprecated + @java.lang.Deprecated + + + + + + RETURNING clause in dialects that don't have native support for this. +

+SQL Server OUTPUT clauses do not support fetching trigger generated values. +Neither do SQLite RETURNING clauses. An additional +MERGE statement can run a second query if (and only if) the primary key has been +included in the OUTPUT clause. +

+Trigger meta data is only available in jOOQ's commercial editions. If setting this flag to +WHEN_NEEDED in the jOOQ Open Source Edition, jOOQ will assume triggers are present. +

For details, see https://github.com/jOOQ/jOOQ/issues/4498.]]> @@ -2412,4 +2436,19 @@ Either <input/> or <inputExpression/> must be provided]]> + + + + + + + + + + + + + + \ No newline at end of file