[#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:
parent
08108d7a6e
commit
abb9fba1ce
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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'/
|
||||
|
||||
71
jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java
Normal file
71
jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java
Normal 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);
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
71
jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java
Normal file
71
jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java
Normal 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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
71
jOOQ/src/main/java/org/jooq/MergeNotMatchedWhereStep.java
Normal file
71
jOOQ/src/main/java/org/jooq/MergeNotMatchedWhereStep.java
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user