[#998] Enhance MERGE statement for Oracle extensions: WHEN MATCHED THEN UPDATE .. WHERE .. DELETE WHERE .. / WHEN NOT MATCHED THEN INSERT .. WHERE ..

This commit is contained in:
Lukas Eder 2011-12-16 15:40:45 +00:00
parent 08108d7a6e
commit abb9fba1ce
13 changed files with 462 additions and 84 deletions

View File

@ -219,13 +219,13 @@ public abstract class jOOQAbstractTest<
T639 extends UpdatableRecord<T639>,
T785 extends TableRecord<T785>> {
private static final List<Short> BOOK_IDS_SHORT = Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4);
private static final List<Integer> BOOK_IDS = Arrays.asList(1, 2, 3, 4);
private static final List<String> BOOK_TITLES = Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida");
private static final List<String> BOOK_FIRST_NAMES = Arrays.asList("George", "George", "Paulo", "Paulo");
private static final List<String> BOOK_LAST_NAMES = Arrays.asList("Orwell", "Orwell", "Coelho", "Coelho");
private static final List<String> AUTHOR_FIRST_NAMES = Arrays.asList("George", "Paulo");
private static final List<String> AUTHOR_LAST_NAMES = Arrays.asList("Orwell", "Coelho");
protected static final List<Short> BOOK_IDS_SHORT = Arrays.asList((short) 1, (short) 2, (short) 3, (short) 4);
protected static final List<Integer> BOOK_IDS = Arrays.asList(1, 2, 3, 4);
protected static final List<String> BOOK_TITLES = Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida");
protected static final List<String> BOOK_FIRST_NAMES = Arrays.asList("George", "George", "Paulo", "Paulo");
protected static final List<String> BOOK_LAST_NAMES = Arrays.asList("Orwell", "Orwell", "Coelho", "Coelho");
protected static final List<String> AUTHOR_FIRST_NAMES = Arrays.asList("George", "Paulo");
protected static final List<String> AUTHOR_LAST_NAMES = Arrays.asList("Orwell", "Coelho");
private static final String JDBC_SCHEMA = "jdbc.Schema";
private static final String JDBC_PASSWORD = "jdbc.Password";
@ -4315,16 +4315,18 @@ public abstract class jOOQAbstractTest<
q.execute();
assertEquals(Arrays.asList("John", "Alfred", "Dan"),
create().selectFrom(TAuthor())
.orderBy(TAuthor_ID())
.fetch(TAuthor_FIRST_NAME()));
create().selectFrom(TAuthor())
.orderBy(TAuthor_ID())
.fetch(TAuthor_FIRST_NAME()));
q.execute();
assertEquals(Arrays.asList("John", "Alfred", "James"),
create().selectFrom(TAuthor())
.orderBy(TAuthor_ID())
.fetch(TAuthor_FIRST_NAME()));
create().selectFrom(TAuthor())
.orderBy(TAuthor_ID())
.fetch(TAuthor_FIRST_NAME()));
// TODO: Add more sophisticated MERGE statement tests
// Especially for SQL Server and Sybase, some bugs could be expected
}
@Test

View File

@ -38,6 +38,7 @@ package org.jooq.test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.jooq.impl.Factory.falseCondition;
import static org.jooq.impl.Factory.one;
import static org.jooq.impl.Factory.substring;
import static org.jooq.impl.Factory.trueCondition;
@ -742,7 +743,7 @@ public class jOOQOracleTest extends jOOQAbstractTest<
}
@Test
public void testMemberProcedures() throws Exception {
public void testOracleMemberProcedures() throws Exception {
UAuthorTypeRecord author1;
UAuthorTypeRecord author2;
@ -801,16 +802,93 @@ public class jOOQOracleTest extends jOOQAbstractTest<
}
@Test
public void testCursorINOUT() throws Exception {
public void testOracleCursorINOUT() throws Exception {
assertEquals(4, (int) create().select(f691cursorIn(f691cursorOut())).fetchOne(0, Integer.class));
}
@Test
public void testTypedSequences() throws Exception {
public void testOracleTypedSequences() throws Exception {
assertEquals(Byte.valueOf("1"), ora().nextval(Sequences.S_961_BYTE));
assertEquals(Short.valueOf("1"), ora().nextval(Sequences.S_961_SHORT));
assertEquals(Integer.valueOf("1"), ora().nextval(Sequences.S_961_INT));
assertEquals(Long.valueOf("1"), ora().nextval(Sequences.S_961_LONG));
assertEquals(BigInteger.valueOf(1), ora().nextval(Sequences.S_961_BIG_INTEGER));
}
@Test
public void testOracleMergeStatementExtensions() throws Exception {
reset = false;
TAuthorRecord author;
// Test updating with a positive condition
// ---------------------------------------
assertEquals(1,
ora().mergeInto(T_AUTHOR)
.usingDual()
.on(T_AUTHOR.ID.equal(1))
.whenMatchedThenUpdate()
.set(T_AUTHOR.LAST_NAME, "Frisch")
.where(T_AUTHOR.ID.equal(1))
.execute());
author = create().fetchOne(T_AUTHOR, T_AUTHOR.ID.equal(1));
assertEquals(2, create().selectCount().from(T_AUTHOR).fetchOne(0));
assertEquals(1, (int) author.getId());
assertEquals(AUTHOR_FIRST_NAMES.get(0), author.getFirstName());
assertEquals("Frisch", author.getLastName());
// Test updating with a negative condition
// ---------------------------------------
assertEquals(0,
ora().mergeInto(T_AUTHOR)
.usingDual()
.on(T_AUTHOR.ID.equal(1))
.whenMatchedThenUpdate()
.set(T_AUTHOR.LAST_NAME, "Frisch")
.where(T_AUTHOR.ID.equal(3))
.execute());
author = create().fetchOne(T_AUTHOR, T_AUTHOR.ID.equal(1));
assertEquals(2, create().selectCount().from(T_AUTHOR).fetchOne(0));
assertEquals(1, (int) author.getId());
assertEquals(AUTHOR_FIRST_NAMES.get(0), author.getFirstName());
assertEquals("Frisch", author.getLastName());
// Test deleting
// -------------
// ON DELETE CASCADE doesn't work with MERGE...?
ora().delete(T_BOOK).execute();
assertEquals(1,
ora().mergeInto(T_AUTHOR)
.usingDual()
.on(trueCondition())
.whenMatchedThenUpdate()
.set(T_AUTHOR.LAST_NAME, "Frisch")
.where(T_AUTHOR.ID.equal(2))
.deleteWhere(T_AUTHOR.ID.equal(2))
.execute());
author = create().fetchOne(T_AUTHOR, T_AUTHOR.ID.equal(1));
assertEquals(1, create().selectCount().from(T_AUTHOR).fetchOne(0));
assertEquals(1, (int) author.getId());
assertEquals(AUTHOR_FIRST_NAMES.get(0), author.getFirstName());
assertEquals("Frisch", author.getLastName());
// Test inserting
// --------------
assertEquals(0,
ora().mergeInto(T_AUTHOR)
.usingDual()
.on(trueCondition())
.whenNotMatchedThenInsert(
T_AUTHOR.ID,
T_AUTHOR.FIRST_NAME,
T_AUTHOR.LAST_NAME)
.values(3, "Yvette", "Z'Graggen")
.where(falseCondition())
.execute());
// No tests on results
}
}

View File

@ -384,10 +384,10 @@ CREATE TABLE t_book (
content_pdf BLOB,
CONSTRAINT pk_t_book PRIMARY KEY (ID),
CONSTRAINT fk_t_book_author_id FOREIGN KEY (AUTHOR_ID) REFERENCES T_AUTHOR(ID),
CONSTRAINT fk_t_book_co_author_id FOREIGN KEY (CO_AUTHOR_ID) REFERENCES T_AUTHOR(ID),
CONSTRAINT fk_t_book_details_id FOREIGN KEY (DETAILS_ID) REFERENCES T_BOOK_DETAILS(ID),
CONSTRAINT fk_t_book_language_id FOREIGN KEY (LANGUAGE_ID) REFERENCES T_LANGUAGE(ID)
CONSTRAINT fk_t_book_author_id FOREIGN KEY (AUTHOR_ID) REFERENCES T_AUTHOR(ID) ON DELETE CASCADE,
CONSTRAINT fk_t_book_co_author_id FOREIGN KEY (CO_AUTHOR_ID) REFERENCES T_AUTHOR(ID) ON DELETE CASCADE,
CONSTRAINT fk_t_book_details_id FOREIGN KEY (DETAILS_ID) REFERENCES T_BOOK_DETAILS(ID) ON DELETE CASCADE,
CONSTRAINT fk_t_book_language_id FOREIGN KEY (LANGUAGE_ID) REFERENCES T_LANGUAGE(ID) ON DELETE CASCADE
)
/
COMMENT ON TABLE t_book IS 'An entity holding books'/

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq;
/**
* This type is used for the {@link Merge}'s DSL API.
* <p>
* Example: <code><pre>
* Factory create = new Factory();
*
* create.mergeInto(table)
* .using(select)
* .on(condition)
* .whenMatchedThenUpdate()
* .set(field1, value1)
* .set(field2, value2)
* .whenNotMatchedThenInsert(field1, field2)
* .values(value1, value2)
* .execute();
* </pre></code>
*
* @author Lukas Eder
*/
public interface MergeMatchedDeleteStep<R extends Record> extends MergeNotMatchedStep<R> {
/**
* Add an additional <code>DELETE WHERE</code> clause to the preceding
* <code>WHEN MATCHED THEN UPDATE</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#ORACLE} database!
*
* @see <a
* href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm">http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm</a>
* for a full definition of the Oracle <code>MERGE</code> statement
*/
MergeNotMatchedStep<R> deleteWhere(Condition condition);
}

View File

@ -54,6 +54,9 @@ package org.jooq;
*
* @author Lukas Eder
*/
public interface MergeMatchedSetMoreStep<R extends Record> extends MergeMatchedSetStep<R>, MergeNotMatchedStep<R> {
public interface MergeMatchedSetMoreStep<R extends Record>
extends
MergeMatchedSetStep<R>,
MergeMatchedWhereStep<R> {
}

View File

@ -54,7 +54,7 @@ package org.jooq;
*
* @author Lukas Eder
*/
public interface MergeMatchedStep<R extends Record> {
public interface MergeMatchedStep<R extends Record> extends MergeNotMatchedStep<R> {
/**
* Add the <code>WHEN MATCHED THEN UPDATE</code> clause to the

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq;
/**
* This type is used for the {@link Merge}'s DSL API.
* <p>
* Example: <code><pre>
* Factory create = new Factory();
*
* create.mergeInto(table)
* .using(select)
* .on(condition)
* .whenMatchedThenUpdate()
* .set(field1, value1)
* .set(field2, value2)
* .whenNotMatchedThenInsert(field1, field2)
* .values(value1, value2)
* .execute();
* </pre></code>
*
* @author Lukas Eder
*/
public interface MergeMatchedWhereStep<R extends Record> extends MergeNotMatchedStep<R> {
/**
* Add an additional <code>WHERE</code> clause to the preceding
* <code>WHEN MATCHED THEN UPDATE</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#ORACLE} database!
*
* @see <a
* href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm">http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm</a>
* for a full definition of the Oracle <code>MERGE</code> statement
*/
MergeMatchedDeleteStep<R> where(Condition condition);
}

View File

@ -56,7 +56,7 @@ import java.util.Collection;
*
* @author Lukas Eder
*/
public interface MergeNotMatchedStep<R extends Record> {
public interface MergeNotMatchedStep<R extends Record> extends MergeFinalStep<R> {
/**
* Add the <code>WHEN NOT MATCHED THEN INSERT</code> clause to the

View File

@ -62,17 +62,17 @@ public interface MergeNotMatchedValuesStep<R extends Record> {
* Set <code>VALUES</code> for <code>INSERT</code> in the <code>MERGE</code>
* statement's <code>WHEN NOT MATCHED THEN INSERT</code> clause.
*/
MergeFinalStep<R> values(Object... values);
MergeNotMatchedWhereStep<R> values(Object... values);
/**
* Set <code>VALUES</code> for <code>INSERT</code> in the <code>MERGE</code>
* statement's <code>WHEN NOT MATCHED THEN INSERT</code> clause.
*/
MergeFinalStep<R> values(Field<?>... values);
MergeNotMatchedWhereStep<R> values(Field<?>... values);
/**
* Set <code>VALUES</code> for <code>INSERT</code> in the <code>MERGE</code>
* statement's <code>WHEN NOT MATCHED THEN INSERT</code> clause.
*/
MergeFinalStep<R> values(Collection<?> values);
MergeNotMatchedWhereStep<R> values(Collection<?> values);
}

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq;
/**
* This type is used for the {@link Merge}'s DSL API.
* <p>
* Example: <code><pre>
* Factory create = new Factory();
*
* create.mergeInto(table)
* .using(select)
* .on(condition)
* .whenMatchedThenUpdate()
* .set(field1, value1)
* .set(field2, value2)
* .whenNotMatchedThenInsert(field1, field2)
* .values(value1, value2)
* .execute();
* </pre></code>
*
* @author Lukas Eder
*/
public interface MergeNotMatchedWhereStep<R extends Record> extends MergeFinalStep<R> {
/**
* Add an additional <code>WHERE</code> clause to the preceding
* <code>WHEN NOT MATCHED THEN INSERT</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#ORACLE} database!
*
* @see <a
* href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm">http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm</a>
* for a full definition of the Oracle <code>MERGE</code> statement
*/
MergeFinalStep<R> where(Condition condition);
}

View File

@ -73,8 +73,10 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
@Override
public final BindContext bind(Collection<? extends QueryPart> parts) {
for (QueryPart part : parts) {
bind(part);
if (parts != null) {
for (QueryPart part : parts) {
bind(part);
}
}
return this;
@ -82,34 +84,39 @@ abstract class AbstractBindContext extends AbstractContext<BindContext> implemen
@Override
public final BindContext bind(QueryPart[] parts) {
bind(Arrays.asList(parts));
if (parts != null) {
bind(Arrays.asList(parts));
}
return this;
}
@Override
public final BindContext bind(QueryPart part) {
QueryPartInternal internal = part.internalAPI(QueryPartInternal.class);
if (part != null) {
QueryPartInternal internal = part.internalAPI(QueryPartInternal.class);
// If this is supposed to be a declaration section and the part isn't
// able to declare anything, then disable declaration temporarily
// If this is supposed to be a declaration section and the part isn't
// able to declare anything, then disable declaration temporarily
// We're declaring fields, but "part" does not declare fields
if (declareFields() && !internal.declaresFields()) {
declareFields(false);
bindInternal(internal);
declareFields(true);
}
// We're declaring fields, but "part" does not declare fields
if (declareFields() && !internal.declaresFields()) {
declareFields(false);
bindInternal(internal);
declareFields(true);
}
// We're declaring tables, but "part" does not declare tables
else if (declareTables() && !internal.declaresTables()) {
declareTables(false);
bindInternal(internal);
declareTables(true);
}
// We're declaring tables, but "part" does not declare tables
else if (declareTables() && !internal.declaresTables()) {
declareTables(false);
bindInternal(internal);
declareTables(true);
}
// We're not declaring, or "part" can declare
else {
bindInternal(internal);
// We're not declaring, or "part" can declare
else {
bindInternal(internal);
}
}
return this;

View File

@ -151,28 +151,30 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
@Override
public final RenderContext sql(QueryPart part) {
QueryPartInternal internal = part.internalAPI(QueryPartInternal.class);
if (part != null) {
QueryPartInternal internal = part.internalAPI(QueryPartInternal.class);
// If this is supposed to be a declaration section and the part isn't
// able to declare anything, then disable declaration temporarily
// If this is supposed to be a declaration section and the part isn't
// able to declare anything, then disable declaration temporarily
// We're declaring fields, but "part" does not declare fields
if (declareFields() && !internal.declaresFields()) {
declareFields(false);
internal.toSQL(this);
declareFields(true);
}
// We're declaring fields, but "part" does not declare fields
if (declareFields() && !internal.declaresFields()) {
declareFields(false);
internal.toSQL(this);
declareFields(true);
}
// We're declaring tables, but "part" does not declare tables
else if (declareTables() && !internal.declaresTables()) {
declareTables(false);
internal.toSQL(this);
declareTables(true);
}
// We're declaring tables, but "part" does not declare tables
else if (declareTables() && !internal.declaresTables()) {
declareTables(false);
internal.toSQL(this);
declareTables(true);
}
// We're not declaring, or "part" can declare
else {
internal.toSQL(this);
// We're not declaring, or "part" can declare
else {
internal.toSQL(this);
}
}
return this;

View File

@ -52,9 +52,10 @@ import org.jooq.BindContext;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Field;
import org.jooq.MergeFinalStep;
import org.jooq.MergeMatchedDeleteStep;
import org.jooq.MergeMatchedSetMoreStep;
import org.jooq.MergeNotMatchedValuesStep;
import org.jooq.MergeNotMatchedWhereStep;
import org.jooq.MergeOnConditionStep;
import org.jooq.MergeOnStep;
import org.jooq.MergeUsingStep;
@ -77,27 +78,35 @@ implements
MergeOnStep<R>,
MergeOnConditionStep<R>,
MergeMatchedSetMoreStep<R>,
MergeMatchedDeleteStep<R>,
MergeNotMatchedValuesStep<R>,
MergeFinalStep<R> {
MergeNotMatchedWhereStep<R> {
/**
* Generated UID
*/
private static final long serialVersionUID = -8835479296876774391L;
private static final long serialVersionUID = -8835479296876774391L;
private final Table<R> table;
private final ConditionProviderImpl on;
private final FieldMapForUpdate updateMap;
private final FieldMapForInsert insertMap;
private TableLike<?> using;
// [#998] Oracle extensions to the MERGE statement
private Condition matchedWhere;
private Condition matchedDeleteWhere;
private Condition notMatchedWhere;
// Flags to keep track of DSL object creation state
private boolean matchedClause;
private FieldMapForUpdate matchedUpdate;
private boolean notMatchedClause;
private FieldMapForInsert notMatchedInsert;
MergeImpl(Configuration configuration, Table<R> table) {
super(configuration);
this.table = table;
this.on = new ConditionProviderImpl();
this.updateMap = new FieldMapForUpdate();
this.insertMap = new FieldMapForInsert();
}
// -------------------------------------------------------------------------
@ -196,6 +205,10 @@ implements
@Override
public final MergeImpl<R> whenMatchedThenUpdate() {
matchedClause = true;
matchedUpdate = new FieldMapForUpdate();
notMatchedClause = false;
return this;
}
@ -206,13 +219,13 @@ implements
@Override
public final <T> MergeImpl<R> set(Field<T> field, Field<T> value) {
updateMap.put(field, nullSafe(value));
matchedUpdate.put(field, nullSafe(value));
return this;
}
@Override
public final MergeImpl<R> set(Map<? extends Field<?>, ?> map) {
updateMap.set(map);
matchedUpdate.set(map);
return this;
}
@ -223,7 +236,11 @@ implements
@Override
public final MergeImpl<R> whenNotMatchedThenInsert(Collection<? extends Field<?>> fields) {
insertMap.putFields(fields);
notMatchedClause = true;
notMatchedInsert = new FieldMapForInsert();
notMatchedInsert.putFields(fields);
matchedClause = false;
return this;
}
@ -239,7 +256,28 @@ implements
@Override
public final MergeImpl<R> values(Collection<?> values) {
insertMap.putValues(vals(values.toArray()));
notMatchedInsert.putValues(vals(values.toArray()));
return this;
}
@Override
public final MergeImpl<R> where(Condition condition) {
if (matchedClause) {
matchedWhere = condition;
}
else if (notMatchedClause) {
notMatchedWhere = condition;
}
else {
throw new IllegalStateException("Cannot call where() on the current state of the MERGE statement");
}
return this;
}
@Override
public final MergeImpl<R> deleteWhere(Condition condition) {
matchedDeleteWhere = condition;
return this;
}
@ -288,11 +326,37 @@ implements
}
context.sql(" on ")
.sql(Util.wrapInParentheses(context.render(on)))
.sql(" when matched then update set ")
.sql(updateMap)
.sql(" when not matched then insert ")
.sql(insertMap);
.sql(Util.wrapInParentheses(context.render(on)));
// [#999] WHEN MATCHED clause is optional
if (matchedUpdate != null) {
context.sql(" when matched then update set ")
.sql(matchedUpdate);
}
// [#998] Oracle MERGE extension: WHEN MATCHED THEN UPDATE .. WHERE
if (matchedWhere != null) {
context.sql(" where ")
.sql(matchedWhere);
}
// [#998] Oracle MERGE extension: WHEN MATCHED THEN UPDATE .. DELETE WHERE
if (matchedDeleteWhere != null) {
context.sql(" delete where ")
.sql(matchedDeleteWhere);
}
// [#999] WHEN NOT MATCHED clause is optional
if (notMatchedInsert != null) {
context.sql(" when not matched then insert ")
.sql(notMatchedInsert);
}
// [#998] Oracle MERGE extension: WHEN NOT MATCHED THEN INSERT .. WHERE
if (notMatchedWhere != null) {
context.sql(" where ")
.sql(notMatchedWhere);
}
switch (context.getDialect()) {
case SQLSERVER:
@ -308,12 +372,21 @@ implements
.bind(using)
.declareTables(false)
.bind(on)
.bind(updateMap)
.bind(insertMap);
.bind(matchedUpdate)
.bind(matchedWhere)
.bind(matchedDeleteWhere)
.bind(notMatchedInsert)
.bind(notMatchedWhere);
}
@Override
public final List<Attachable> getAttachables() {
return getAttachables(table, using, on, updateMap, insertMap);
return getAttachables(
table, using, on,
matchedUpdate,
matchedWhere,
matchedDeleteWhere,
notMatchedInsert,
notMatchedWhere);
}
}