[#1952] Add support for SQL Server OPTION (...) query hints

This commit is contained in:
Lukas Eder 2013-06-21 22:55:40 +02:00
parent c1387f5779
commit b8371015f7
7 changed files with 190 additions and 7 deletions

View File

@ -36,6 +36,7 @@
package org.jooq.test;
import static junit.framework.Assert.assertEquals;
import static org.jooq.test.sqlserver.generatedclasses.Tables.T_639_NUMBERS_TABLE;
import static org.jooq.test.sqlserver.generatedclasses.Tables.T_725_LOB_TEST;
import static org.jooq.test.sqlserver.generatedclasses.Tables.T_785;
@ -104,6 +105,8 @@ import org.jooq.types.ULong;
import org.jooq.types.UShort;
import org.jooq.util.sqlserver.SQLServerDataType;
import org.junit.Test;
/**
* @author Lukas Eder
*/
@ -754,4 +757,13 @@ public class SQLServerTest extends jOOQAbstractTest<
SQLServerDataType.VARCHAR,
};
}
@Test
public void testSQLServerOptionHint() throws Exception {
assertEquals(4, create()
.selectFrom(T_BOOK)
.option("OPTION(OPTIMIZE FOR UNKNOWN)")
.fetch()
.size());
}
}

View File

@ -95,7 +95,7 @@ import org.jooq.api.annotation.Transition;
* @author Lukas Eder
*/
@State
public interface SelectForUpdateStep<R extends Record> extends SelectFinalStep<R> {
public interface SelectForUpdateStep<R extends Record> extends SelectOptionStep<R> {
/**
* Add a <code>FOR UPDATE</code> clause to the end of the query.
@ -125,6 +125,6 @@ public interface SelectForUpdateStep<R extends Record> extends SelectFinalStep<R
@Transition(
name = "FOR SHARE"
)
SelectFinalStep<R> forShare();
SelectOptionStep<R> forShare();
}

View File

@ -84,7 +84,7 @@ import org.jooq.api.annotation.Transition;
* @author Lukas Eder
*/
@State
public interface SelectForUpdateWaitStep<R extends Record> extends SelectFinalStep<R> {
public interface SelectForUpdateWaitStep<R extends Record> extends SelectOptionStep<R> {
/**
* Add a <code>WAIT</code> clause to the <code>FOR UPDATE</code> clause at
@ -99,7 +99,7 @@ public interface SelectForUpdateWaitStep<R extends Record> extends SelectFinalSt
name = "WAIT",
args = "Integer"
)
SelectFinalStep<R> wait(int seconds);
SelectOptionStep<R> wait(int seconds);
/**
* Add a <code>NOWAIT</code> clause to the <code>FOR UPDATE</code> clause at
@ -111,7 +111,7 @@ public interface SelectForUpdateWaitStep<R extends Record> extends SelectFinalSt
@Transition(
name = "NOWAIT"
)
SelectFinalStep<R> noWait();
SelectOptionStep<R> noWait();
/**
* Add a <code>WAIT</code> clause to the <code>FOR UPDATE</code> clause at
@ -124,5 +124,5 @@ public interface SelectForUpdateWaitStep<R extends Record> extends SelectFinalSt
@Transition(
name = "SKIP LOCKED"
)
SelectFinalStep<R> skipLocked();
SelectOptionStep<R> skipLocked();
}

View File

@ -0,0 +1,116 @@
/**
* Copyright (c) 2009-2013, 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 org.jooq.api.annotation.State;
/**
* This type is used for the {@link Select}'s DSL API when selecting generic
* {@link Record} types.
* <p>
* Example: <code><pre>
* -- get all authors' first and last names, and the number
* -- of books they've written in German, if they have written
* -- more than five books in German in the last three years
* -- (from 2011), and sort those authors by last names
* -- limiting results to the second and third row
*
* SELECT T_AUTHOR.FIRST_NAME, T_AUTHOR.LAST_NAME, COUNT(*)
* FROM T_AUTHOR
* JOIN T_BOOK ON T_AUTHOR.ID = T_BOOK.AUTHOR_ID
* WHERE T_BOOK.LANGUAGE = 'DE'
* AND T_BOOK.PUBLISHED > '2008-01-01'
* GROUP BY T_AUTHOR.FIRST_NAME, T_AUTHOR.LAST_NAME
* HAVING COUNT(*) > 5
* ORDER BY T_AUTHOR.LAST_NAME ASC NULLS FIRST
* LIMIT 2
* OFFSET 1
* FOR UPDATE
* OF FIRST_NAME, LAST_NAME
* NO WAIT
* </pre></code> Its equivalent in jOOQ <code><pre>
* create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
* .from(T_AUTHOR)
* .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID))
* .where(TBook.LANGUAGE.equal("DE"))
* .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')))
* .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
* .having(create.count().greaterThan(5))
* .orderBy(TAuthor.LAST_NAME.asc().nullsFirst())
* .limit(2)
* .offset(1)
* .forUpdate()
* .of(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
* .noWait();
* </pre></code> Refer to the manual for more details
*
* @author Lukas Eder
*/
@State
public interface SelectOptionStep<R extends Record> extends SelectFinalStep<R> {
/**
* Add a SQL Server-style query hint to the select clause.
* <p>
* Example: <code><pre>
* DSLContext create = DSL.using(configuration);
*
* create.select(field1, field2)
* .from(table1)
* .option("OPTION (OPTIMIZE FOR UNKNOWN)")
* .execute();
* </pre></code>
* <p>
* You can also use this clause for any other database, that accepts hints
* or options at the same syntactic location, e.g. for DB2's isolation clause: <code><pre>
* create.select(field1, field2)
* .from(table1)
* .option("WITH RR USE AND KEEP EXCLUSIVE LOCKS")
* .execute();
* </pre></code>
* <p>
* The outcome of such a query is this: <code><pre>
* SELECT field1, field2 FROM table1 [option]
* </pre></code>
* <p>
* For SQL Server style table hints, see {@link Table#with(String)}
*
* @see Table#with(String)
* @see SelectQuery#addOption(String)
*/
@Support
SelectFinalStep<R> option(String string);
}

View File

@ -264,7 +264,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
void addHaving(Operator operator, Collection<Condition> conditions);
/**
* Add an Oracle-style hint to the select clause
* Add an Oracle-style hint to the select clause.
* <p>
* Example: <code><pre>
* DSLContext create = DSL.using(configuration);
@ -295,6 +295,37 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
@Support
void addHint(String hint);
/**
* Add a SQL Server-style query hint to the select clause.
* <p>
* Example: <code><pre>
* DSLContext create = DSL.using(configuration);
*
* create.select(field1, field2)
* .from(table1)
* .option("OPTION (OPTIMIZE FOR UNKNOWN)")
* .execute();
* </pre></code>
* <p>
* You can also use this clause for any other database, that accepts hints
* or options at the same syntactic location, e.g. for DB2's isolation clause: <code><pre>
* create.select(field1, field2)
* .from(table1)
* .option("WITH RR USE AND KEEP EXCLUSIVE LOCKS")
* .execute();
* </pre></code>
* <p>
* The outcome of such a query is this: <code><pre>
* SELECT field1, field2 FROM table1 [option]
* </pre></code>
* <p>
* For SQL Server style table hints, see {@link Table#with(String)}
*
* @see Table#with(String)
*/
@Support
void addOption(String option);
/**
* Add an Oracle-specific <code>CONNECT BY</code> clause to the query
*/

View File

@ -186,6 +186,12 @@ class SelectImpl<R extends Record> extends AbstractDelegatingQuery<Select<R>> im
return this;
}
@Override
public final SelectImpl<R> option(String hint) {
getQuery().addOption(hint);
return this;
}
@Override
public final SelectImpl<R> from(TableLike<?>... tables) {
getQuery().addFrom(tables);

View File

@ -98,6 +98,7 @@ class SelectQueryImpl<R extends Record> extends AbstractSelect<R> implements Sel
private final SelectFieldList select;
private String hint;
private String option;
private boolean distinct;
private boolean forUpdate;
private final QueryPartList<Field<?>> forUpdateOf;
@ -310,6 +311,14 @@ class SelectQueryImpl<R extends Record> extends AbstractSelect<R> implements Sel
break;
}
}
// [#1952] SQL Server OPTION() clauses as well as many other optional
// end-of-query clauses are appended to the end of a query
if (!StringUtils.isBlank(option)) {
context.formatSeparator()
.sql(option);
}
}
/**
@ -905,6 +914,10 @@ class SelectQueryImpl<R extends Record> extends AbstractSelect<R> implements Sel
this.hint = hint;
}
final void setOption(String option) {
this.option = option;
}
@Override
final boolean isForUpdate() {
return forUpdate;
@ -1184,6 +1197,11 @@ class SelectQueryImpl<R extends Record> extends AbstractSelect<R> implements Sel
setHint(h);
}
@Override
public final void addOption(String o) {
setOption(o);
}
// -------------------------------------------------------------------------
// Utility classes
// -------------------------------------------------------------------------