diff --git a/jOOQ-test/src/org/jooq/test/BaseTest.java b/jOOQ-test/src/org/jooq/test/BaseTest.java index 0ba2a1a30c..36018d1b8e 100644 --- a/jOOQ-test/src/org/jooq/test/BaseTest.java +++ b/jOOQ-test/src/org/jooq/test/BaseTest.java @@ -730,6 +730,24 @@ public abstract class BaseTest< return record; } + /** + * Convenience method to get an author + */ + protected final A getAuthor(int id) { + return create().selectFrom(TAuthor()) + .where(TAuthor_ID().equal(id)) + .fetchOne(); + } + + /** + * Convenience method to get a book + */ + protected final B getBook(int id) { + return create().selectFrom(TBook()) + .where(TBook_ID().equal(id)) + .fetchOne(); + } + @SuppressWarnings("unchecked") protected Sequence SAuthorID() throws IllegalAccessException, NoSuchFieldException { return (Sequence) cSequences().getField("S_AUTHOR_ID").get(cSequences()); diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java index 6e3b735fd4..cb0af62724 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/InsertUpdateTests.java @@ -35,6 +35,7 @@ */ package org.jooq.test._.testcases; +import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -46,6 +47,7 @@ import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.FIREBIRD; import static org.jooq.SQLDialect.H2; import static org.jooq.SQLDialect.INGRES; +import static org.jooq.SQLDialect.MYSQL; import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.SQLDialect.SQLITE; import static org.jooq.SQLDialect.SQLSERVER; @@ -55,6 +57,7 @@ import static org.jooq.impl.Factory.castNull; import static org.jooq.impl.Factory.count; import static org.jooq.impl.Factory.inline; import static org.jooq.impl.Factory.max; +import static org.jooq.impl.Factory.row; import static org.jooq.impl.Factory.select; import static org.jooq.impl.Factory.selectOne; import static org.jooq.impl.Factory.val; @@ -444,6 +447,96 @@ extends BaseTest extends StoreQuery, ConditionProvider, Update { + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row1 row, Row1 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row2 row, Row2 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row3 row, Row3 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row4 row, Row4 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row5 row, Row5 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row6 row, Row6 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row7 row, Row7 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + void addValues(Row8 row, Row8 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row1 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row2 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row3 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row4 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row5 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row6 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row7 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + void addValues(Row8 row, Select> select); + // ------------------------------------------------------------------------ // Methods from ConditionProvider // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/UpdateSetFirstStep.java b/jOOQ/src/main/java/org/jooq/UpdateSetFirstStep.java new file mode 100644 index 0000000000..43e796091d --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/UpdateSetFirstStep.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2009-2012, 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; + +import static org.jooq.SQLDialect.DB2; +import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.INGRES; +import static org.jooq.SQLDialect.ORACLE; +import static org.jooq.SQLDialect.POSTGRES; + +/** + * This type is used for the {@link Update}'s DSL API. + *

+ * Example:

+ * Factory create = new Factory();
+ *
+ * create.update(table)
+ *       .set(field1, value1)
+ *       .set(field2, value2)
+ *       .where(field1.greaterThan(100))
+ *       .execute();
+ * 
+ * + * @author Lukas Eder + */ +public interface UpdateSetFirstStep extends UpdateSetStep { + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row1 row, Row1 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row2 row, Row2 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row3 row, Row3 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row4 row, Row4 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row5 row, Row5 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row6 row, Row6 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row7 row, Row7 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + *

+ * This is simulated using a subquery for the value, where row + * value expressions aren't supported. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE, POSTGRES }) + UpdateWhereStep set(Row8 row, Row8 value); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row1 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row2 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row3 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row4 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row5 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row6 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row7 row, Select> select); + + /** + * Specify a multi-column set clause for the UPDATE statement. + */ + @Support({ DB2, H2, HSQLDB, INGRES, ORACLE }) + UpdateWhereStep set(Row8 row, Select> select); + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/Executor.java b/jOOQ/src/main/java/org/jooq/impl/Executor.java index e0c871025b..843010ce57 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Executor.java +++ b/jOOQ/src/main/java/org/jooq/impl/Executor.java @@ -102,7 +102,7 @@ import org.jooq.UDT; import org.jooq.UDTRecord; import org.jooq.UpdatableRecord; import org.jooq.UpdateQuery; -import org.jooq.UpdateSetStep; +import org.jooq.UpdateSetFirstStep; import org.jooq.conf.Settings; import org.jooq.conf.SettingsTools; import org.jooq.exception.DataAccessException; @@ -1196,7 +1196,7 @@ public class Executor implements FactoryOperations { * {@inheritDoc} */ @Override - public final UpdateSetStep update(Table table) { + public final UpdateSetFirstStep update(Table table) { return new UpdateImpl(this, table); } diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java index d7f53d1b2a..442b31cd6a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateImpl.java @@ -48,11 +48,29 @@ import org.jooq.Field; import org.jooq.Operator; import org.jooq.QueryPart; import org.jooq.Record; +import org.jooq.Record1; +import org.jooq.Record2; +import org.jooq.Record3; +import org.jooq.Record4; +import org.jooq.Record5; +import org.jooq.Record6; +import org.jooq.Record7; +import org.jooq.Record8; +import org.jooq.Row1; +import org.jooq.Row2; +import org.jooq.Row3; +import org.jooq.Row4; +import org.jooq.Row5; +import org.jooq.Row6; +import org.jooq.Row7; +import org.jooq.Row8; import org.jooq.Select; import org.jooq.Table; import org.jooq.UpdateConditionStep; import org.jooq.UpdateQuery; +import org.jooq.UpdateSetFirstStep; import org.jooq.UpdateSetMoreStep; +import org.jooq.UpdateWhereStep; /** * A wrapper for an {@link UpdateQuery} @@ -64,6 +82,7 @@ final class UpdateImpl implements // Cascading interface implementations for Update behaviour + UpdateSetFirstStep, UpdateSetMoreStep, UpdateConditionStep { @@ -94,6 +113,102 @@ final class UpdateImpl return this; } + @Override + public final UpdateWhereStep set(Row1 row, Row1 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row2 row, Row2 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row3 row, Row3 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row4 row, Row4 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row5 row, Row5 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row6 row, Row6 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row7 row, Row7 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row8 row, Row8 value) { + getDelegate().addValues(row, value); + return this; + } + + @Override + public final UpdateWhereStep set(Row1 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row2 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row3 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row4 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row5 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row6 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row7 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + + @Override + public final UpdateWhereStep set(Row8 row, Select> select) { + getDelegate().addValues(row, select); + return this; + } + @Override public final UpdateImpl where(Condition... conditions) { getDelegate().addConditions(conditions); diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index fa578c2d1a..150ae4046b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -36,6 +36,11 @@ package org.jooq.impl; +import static java.util.Arrays.asList; +import static org.jooq.SQLDialect.INGRES; +import static org.jooq.SQLDialect.ORACLE; +import static org.jooq.impl.Factory.select; + import java.util.Collection; import java.util.Map; @@ -45,7 +50,25 @@ import org.jooq.Configuration; import org.jooq.Field; import org.jooq.Operator; import org.jooq.Record; +import org.jooq.Record1; +import org.jooq.Record2; +import org.jooq.Record3; +import org.jooq.Record4; +import org.jooq.Record5; +import org.jooq.Record6; +import org.jooq.Record7; +import org.jooq.Record8; import org.jooq.RenderContext; +import org.jooq.Row; +import org.jooq.Row1; +import org.jooq.Row2; +import org.jooq.Row3; +import org.jooq.Row4; +import org.jooq.Row5; +import org.jooq.Row6; +import org.jooq.Row7; +import org.jooq.Row8; +import org.jooq.Select; import org.jooq.Table; import org.jooq.UpdateQuery; @@ -58,6 +81,9 @@ class UpdateQueryImpl extends AbstractStoreQuery implements private final FieldMapForUpdate updateMap; private final ConditionProviderImpl condition; + private Row multiRow; + private Row multiValue; + private Select multiSelect; UpdateQueryImpl(Configuration configuration, Table table) { super(configuration, table); @@ -81,13 +107,98 @@ class UpdateQueryImpl extends AbstractStoreQuery implements } @Override - public final void addValues(Map, ?> map) { - updateMap.set(map); + public final void addValues(Row1 row, Row1 value) { + addValues0(row, value); } @Override - public final void bind(BindContext context) { - context.bind(getInto()).bind(updateMap).bind(condition); + public final void addValues(Row2 row, Row2 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row3 row, Row3 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row4 row, Row4 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row5 row, Row5 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row6 row, Row6 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row7 row, Row7 value) { + addValues0(row, value); + } + + @Override + public final void addValues(Row8 row, Row8 value) { + addValues0(row, value); + } + + private final void addValues0(Row row, Row value) { + multiRow = row; + multiValue = value; + } + + @Override + public final void addValues(Row1 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row2 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row3 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row4 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row5 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row6 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row7 row, Select> select) { + addValues0(row, select); + } + + @Override + public final void addValues(Row8 row, Select> select) { + addValues0(row, select); + } + + private final void addValues0(Row row, Select select) { + multiRow = row; + multiSelect = select; + } + + @Override + public final void addValues(Map, ?> map) { + updateMap.set(map); } @Override @@ -121,10 +232,49 @@ class UpdateQueryImpl extends AbstractStoreQuery implements .sql(getInto()) .declareTables(false) .formatSeparator() - .keyword("set ") - .formatIndentLockStart() - .sql(updateMap) - .formatIndentLockEnd(); + .keyword("set "); + + // A multi-row update was specified + if (multiRow != null) { + boolean qualify = context.qualify(); + + context.qualify(false) + .sql(multiRow) + .qualify(qualify) + .sql(" = "); + + // Some dialects don't really support row value expressions on the + // right hand side of a SET clause + if (multiValue != null && !asList(INGRES, ORACLE).contains(context.getDialect())) { + context.sql(multiValue); + } + + // Subselects or subselect simulatinos of row value expressions + else { + Select select = multiSelect; + + if (multiValue != null) { + select = select(multiValue.getFields()); + } + + context.sql("(") + .formatIndentStart() + .formatNewLine() + .subquery(true) + .sql(select) + .subquery(false) + .formatIndentEnd() + .formatNewLine() + .sql(")"); + } + } + + // A regular (non-multi-row) update was specified + else { + context.formatIndentLockStart() + .sql(updateMap) + .formatIndentLockEnd(); + } if (!(getWhere() instanceof TrueCondition)) { context.formatSeparator() @@ -134,7 +284,33 @@ class UpdateQueryImpl extends AbstractStoreQuery implements } @Override - public boolean isExecutable() { - return updateMap.size() > 0; + public final void bind(BindContext context) { + context.bind(getInto()); + + // A multi-row update was specified + if (multiRow != null) { + context.bind(multiRow); + + if (multiValue != null) { + context.bind(multiValue); + } + else { + context.subquery(true) + .bind(multiSelect) + .subquery(false); + } + } + + // A regular (non-multi-row) update was specified + else { + context.bind(updateMap); + } + + context.bind(condition); + } + + @Override + public final boolean isExecutable() { + return updateMap.size() > 0 || multiRow != null; } }