diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java
index 75c8438ebc..ffe2692d89 100644
--- a/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java
+++ b/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java
@@ -795,7 +795,15 @@ extends BaseTest authors4 = create().selectFrom(TAuthor()).orderBy(TAuthor_ID()).fetch();
@@ -858,6 +875,28 @@ extends BaseTest authors5 = create().selectFrom(TAuthor()).orderBy(TAuthor_ID()).fetch();
+ assertEquals(4, authors5.size());
+ assertEquals(3, (int) authors5.get(2).getValue(TAuthor_ID()));
+ assertEquals("Eder", authors5.get(2).getValue(TAuthor_LAST_NAME()));
+ assertEquals("John", authors5.get(2).getValue(TAuthor_FIRST_NAME()));
+ assertEquals(4, (int) authors5.get(3).getValue(TAuthor_ID()));
+ assertEquals("Eder", authors5.get(3).getValue(TAuthor_LAST_NAME()));
+ assertEquals("John", authors5.get(3).getValue(TAuthor_FIRST_NAME()));
}
@Test
diff --git a/jOOQ/src/main/java/org/jooq/FactoryOperations.java b/jOOQ/src/main/java/org/jooq/FactoryOperations.java
index 72c07d86e3..2b57ab2a0e 100644
--- a/jOOQ/src/main/java/org/jooq/FactoryOperations.java
+++ b/jOOQ/src/main/java/org/jooq/FactoryOperations.java
@@ -630,9 +630,15 @@ public interface FactoryOperations extends Configuration {
*
www.h2database.com/html/grammar.html#merge |
*
+ *
+ * | DB2, HSQLDB, Oracle, SQL Server, Sybase SQL Anywhere |
+ * These databases can simulate the H2-specific MERGE statement using a
+ * standard SQL MERGE statement, without restrictions |
+ * See {@link #mergeInto(Table)} for the standard MERGE statement |
+ *
*
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
MergeKeyStep mergeInto(Table table, Field>... fields);
/**
@@ -640,7 +646,7 @@ public interface FactoryOperations extends Configuration {
*
* @see #mergeInto(Table, Field...)
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
MergeKeyStep mergeInto(Table table, Collection extends Field>> fields);
/**
diff --git a/jOOQ/src/main/java/org/jooq/MergeKeyStep.java b/jOOQ/src/main/java/org/jooq/MergeKeyStep.java
index 998410c25f..5ea015da54 100644
--- a/jOOQ/src/main/java/org/jooq/MergeKeyStep.java
+++ b/jOOQ/src/main/java/org/jooq/MergeKeyStep.java
@@ -35,7 +35,12 @@
*/
package org.jooq;
+import static org.jooq.SQLDialect.DB2;
import static org.jooq.SQLDialect.H2;
+import static org.jooq.SQLDialect.HSQLDB;
+import static org.jooq.SQLDialect.ORACLE;
+import static org.jooq.SQLDialect.SQLSERVER;
+import static org.jooq.SQLDialect.SYBASE;
import java.util.Collection;
@@ -61,7 +66,7 @@ public interface MergeKeyStep extends MergeValuesStep {
* Use this optional clause in order to override using the underlying
* PRIMARY KEY.
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
MergeValuesStep key(Field>... keys);
/**
@@ -70,6 +75,6 @@ public interface MergeKeyStep extends MergeValuesStep {
* Use this optional clause in order to override using the underlying
* PRIMARY KEY.
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
MergeValuesStep key(Collection extends Field>> keys);
}
diff --git a/jOOQ/src/main/java/org/jooq/MergeValuesStep.java b/jOOQ/src/main/java/org/jooq/MergeValuesStep.java
index f255f9cf54..6f87dfbcf7 100644
--- a/jOOQ/src/main/java/org/jooq/MergeValuesStep.java
+++ b/jOOQ/src/main/java/org/jooq/MergeValuesStep.java
@@ -35,7 +35,12 @@
*/
package org.jooq;
+import static org.jooq.SQLDialect.DB2;
import static org.jooq.SQLDialect.H2;
+import static org.jooq.SQLDialect.HSQLDB;
+import static org.jooq.SQLDialect.ORACLE;
+import static org.jooq.SQLDialect.SQLSERVER;
+import static org.jooq.SQLDialect.SYBASE;
import java.util.Collection;
@@ -58,19 +63,19 @@ public interface MergeValuesStep {
/**
* Specify a VALUES clause
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
Merge values(Object... values);
/**
* Specify a VALUES clause
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
Merge values(Field>... values);
/**
* Specify a VALUES clause
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
Merge values(Collection> values);
/**
@@ -83,6 +88,6 @@ public interface MergeValuesStep {
* {@link FactoryOperations#mergeInto(Table, Field...)} or
* {@link FactoryOperations#mergeInto(Table, Collection)}
*/
- @Support(H2)
+ @Support({ DB2, H2, HSQLDB, ORACLE, SQLSERVER, SYBASE })
Merge select(Select> select);
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java
index 5201e30e99..a1cac772c4 100644
--- a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java
@@ -48,8 +48,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.jooq.Attachable;
import org.jooq.BindContext;
@@ -70,6 +73,8 @@ import org.jooq.RenderContext;
import org.jooq.Select;
import org.jooq.Table;
import org.jooq.TableLike;
+import org.jooq.UniqueKey;
+import org.jooq.UpdatableTable;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.tools.StringUtils;
@@ -395,10 +400,127 @@ implements
// QueryPart API
// -------------------------------------------------------------------------
+ /**
+ * Return a standard MERGE statement simulating the H2-specific syntax
+ */
+ private final QueryPart getStandardMerge(Configuration config) {
+ switch (config.getDialect()) {
+ case DB2:
+ case HSQLDB:
+ case ORACLE:
+ case SQLSERVER:
+ case SYBASE: {
+
+ // The SRC for the USING() clause:
+ // ------------------------------
+ Table> src;
+ if (h2Select != null) {
+ FieldList v = new FieldList();
+
+ for (int i = 0; i < h2Select.getFields().size(); i++) {
+ v.add(h2Select.getField(i).as("s" + (i + 1)));
+ }
+
+ // [#579] TODO: Currently, this syntax may require aliasing
+ // on the call-site
+ src = create().select(v).from(h2Select).asTable("src");
+ }
+ else {
+ FieldList v = new FieldList();
+
+ for (int i = 0; i < getH2Values().size(); i++) {
+ v.add(getH2Values().get(i).as("s" + (i + 1)));
+ }
+
+ src = create().select(v).asTable("src");
+ }
+
+ // The condition for the ON clause:
+ // --------------------------------
+ Set> onFields = new HashSet>();
+ Condition condition = null;
+ if (getH2Keys().isEmpty()) {
+ if (table instanceof UpdatableTable) {
+ UniqueKey> key = ((UpdatableTable>) table).getMainKey();
+ onFields.addAll(key.getFields());
+
+ for (int i = 0; i < key.getFields().size(); i++) {
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ Condition rhs = key.getFields().get(i).equal((Field) src.getField(i));
+
+ if (condition == null) {
+ condition = rhs;
+ }
+ else {
+ condition = condition.and(rhs);
+ }
+ }
+ }
+
+ // This should probably execute an INSERT statement
+ else {
+ throw new IllegalStateException("Cannot omit KEY() clause on a non-Updatable Table");
+ }
+ }
+ else {
+ for (int i = 0; i < getH2Keys().size(); i++) {
+ int matchIndex = getH2Fields().indexOf(getH2Keys().get(i));
+ if (matchIndex == -1) {
+ throw new IllegalStateException("Fields in KEY() clause must be part of the fields specified in MERGE INTO table (...)");
+ }
+
+ onFields.addAll(getH2Keys());
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ Condition rhs = getH2Keys().get(i).equal((Field) src.getField(matchIndex));
+
+ if (condition == null) {
+ condition = rhs;
+ }
+ else {
+ condition = condition.and(rhs);
+ }
+ }
+ }
+
+ // INSERT and UPDATE clauses
+ // -------------------------
+ Map, Field>> update = new LinkedHashMap, Field>>();
+ Map, Field>> insert = new LinkedHashMap, Field>>();
+
+ for (int i = 0; i < src.getFields().size(); i++) {
+
+ // Oracle does not allow to update fields from the ON clause
+ if (!onFields.contains(getH2Fields().get(i))) {
+ update.put(getH2Fields().get(i), src.getField(i));
+ }
+
+ insert.put(getH2Fields().get(i), src.getField(i));
+ }
+
+ return create().mergeInto(table)
+ .using(src)
+ .on(condition)
+ .whenMatchedThenUpdate()
+ .set(update)
+ .whenNotMatchedThenInsert()
+ .set(insert);
+ }
+ default:
+ throw new SQLDialectNotSupportedException("The H2-specific MERGE syntax is not supported in dialect : " + config.getDialect());
+ }
+ }
+
@Override
public final void toSQL(RenderContext context) {
if (h2Style) {
- toSQLH2(context);
+ if (context.getDialect() == H2) {
+ toSQLH2(context);
+ }
+ else {
+ context.sql(getStandardMerge(context));
+ }
}
else {
toSQLStandard(context);
@@ -406,10 +528,6 @@ implements
}
private final void toSQLH2(RenderContext context) {
- if (context.getDialect() != H2) {
- throw new SQLDialectNotSupportedException("The H2-specific MERGE syntax is only supported in H2");
- }
-
context.keyword("merge into ")
.declareTables(true)
.sql(table)
@@ -529,7 +647,12 @@ implements
@Override
public final void bind(BindContext context) {
if (h2Style) {
- bindH2(context);
+ if (context.getDialect() == H2) {
+ bindH2(context);
+ }
+ else {
+ context.bind(getStandardMerge(context));
+ }
}
else {
bindStandard(context);