[#914] Allow for modifying bind values of existing QueryParts / Queries

[#980] Add support for named parameters, to better interact with Spring
This commit is contained in:
Lukas Eder 2011-12-11 16:06:50 +00:00
parent de42b79cb5
commit ea48c9dcd0
23 changed files with 1155 additions and 1020 deletions

View File

@ -185,6 +185,11 @@ public class FactoryProxy implements FactoryOperations, MethodInterceptor {
return getDelegate().render(part);
}
@Override
public final String renderNamedParams(QueryPart part) {
return getDelegate().renderNamedParams(part);
}
@Override
public final String renderInlined(QueryPart part) {
return getDelegate().renderInlined(part);

View File

@ -1561,9 +1561,9 @@ public abstract class jOOQAbstractTest<
// CRUD with plain SQL
Table<Record> table = table(TAuthor().getName());
Field<Object> id = field(TAuthor_ID().getName());
Field<Object> firstName = field(TAuthor_FIRST_NAME().getName());
Field<Object> lastName = field(TAuthor_LAST_NAME().getName());
Field<Integer> id = field(TAuthor_ID().getName(), Integer.class);
Field<String> firstName = field(TAuthor_FIRST_NAME().getName(), String.class);
Field<String> lastName = field(TAuthor_LAST_NAME().getName(), String.class);
assertEquals(2,
create().insertInto(table, id, firstName, lastName)
@ -1579,8 +1579,8 @@ public abstract class jOOQAbstractTest<
.fetch();
assertEquals(2, authors1.size());
assertEquals(10, authors1.getValue(0, id));
assertEquals(11, authors1.getValue(1, id));
assertEquals(10, (int) authors1.getValue(0, id));
assertEquals(11, (int) authors1.getValue(1, id));
assertEquals("Herbert", authors1.getValue(0, firstName));
assertEquals("Friedrich", authors1.getValue(1, firstName));
assertEquals("Meier", authors1.getValue(0, lastName));
@ -1601,8 +1601,8 @@ public abstract class jOOQAbstractTest<
.fetch();
assertEquals(2, authors2.size());
assertEquals(10, authors2.getValue(0, id));
assertEquals(11, authors2.getValue(1, id));
assertEquals(10, (int) authors2.getValue(0, id));
assertEquals(11, (int) authors2.getValue(1, id));
assertEquals("Friedrich", authors2.getValue(0, firstName));
assertEquals("Friedrich", authors2.getValue(1, firstName));
assertEquals("Schiller", authors2.getValue(0, lastName));
@ -8788,6 +8788,40 @@ public abstract class jOOQAbstractTest<
.fetch(TAuthor_LAST_NAME()));
}
@Test
public void testNamedParams() throws Exception {
Select<?> select =
create().select(
TAuthor_ID(),
param("p1", String.class))
.from(TAuthor())
.where(TAuthor_ID().in(
param("p2", Integer.class),
param("p3", Integer.class)))
.orderBy(TAuthor_ID().asc());
// Should execute fine, but no results due to IN (null, null) filter
assertEquals(0, select.fetch().size());
// Set both parameters to the same value
select.getParam("p2").setConverted(1L);
select.getParam("p3").setConverted("1");
Result<?> result1 = select.fetch();
assertEquals(1, result1.size());
assertEquals(1, result1.getValue(0, 0));
assertNull(result1.getValue(0, 1));
// Set more parameters
select.getParam("p1").setConverted("asdf");
select.getParam("p3").setConverted("2");
Result<?> result2 = select.fetch();
assertEquals(2, result2.size());
assertEquals(1, result2.getValue(0, 0));
assertEquals(2, result2.getValue(1, 0));
assertEquals("asdf", result2.getValue(0, 1));
assertEquals("asdf", result2.getValue(1, 1));
}
@Test
public void testLoader() throws Exception {
reset = false;

View File

@ -47,7 +47,7 @@ import org.jooq.exception.DataAccessException;
* will then pass the same context to their components
* <p>
* This interface is for JOOQ INTERNAL USE only. Do not reference directly
*
*
* @author Lukas Eder
* @see RenderContext
*/
@ -58,23 +58,10 @@ public interface BindContext extends Context<BindContext> {
*/
PreparedStatement statement();
/**
* Get the next bind index. This increments an internal counter. Client code
* must assure that calling {@link #nextIndex()} is followed by setting a
* bind value to {@link #statement()}
*/
int nextIndex();
/**
* Peek the next bind index. This won't increment the internal counter,
* unlike {@link #nextIndex()}
*/
int peekIndex();
/**
* Bind values from a {@link QueryPart}. This will also increment the
* internal counter.
*
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/
@ -83,7 +70,7 @@ public interface BindContext extends Context<BindContext> {
/**
* Bind values from several {@link QueryPart}'s. This will also increment
* the internal counter.
*
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/
@ -92,7 +79,7 @@ public interface BindContext extends Context<BindContext> {
/**
* Bind values from several {@link QueryPart}'s. This will also increment
* the internal counter.
*
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/
@ -101,7 +88,7 @@ public interface BindContext extends Context<BindContext> {
/**
* Bind a value using a specific type. This will also increment the internal
* counter.
*
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/
@ -109,7 +96,7 @@ public interface BindContext extends Context<BindContext> {
/**
* Bind several values. This will also increment the internal counter.
*
*
* @throws DataAccessException If something went wrong while binding a
* variable
*/

View File

@ -35,6 +35,8 @@
*/
package org.jooq;
import java.sql.PreparedStatement;
/**
* A context type that is used for rendering SQL or for binding
* <p>
@ -78,4 +80,24 @@ public interface Context<C extends Context<C>> extends Configuration {
* Set the new context value for {@link #subquery()}
*/
C subquery(boolean subquery);
/**
* Get the next bind index. This increments an internal counter. This is
* relevant for two use-cases:
* <ul>
* <li>When binding variables to a {@link PreparedStatement}. Client code
* must assure that calling {@link #nextIndex()} is followed by setting a
* bind value to {@link #statement()}</li>
* <li>When rendering unnamed bind variables with
* {@link RenderContext#namedParams()} being to <code>true</code></li>
* </ul>
*/
int nextIndex();
/**
* Peek the next bind index. This won't increment the internal counter,
* unlike {@link #nextIndex()}
*/
int peekIndex();
}

View File

@ -69,6 +69,18 @@ public interface FactoryOperations extends Configuration {
*/
String render(QueryPart part);
/**
* Render a QueryPart in the context of this factory, rendering bind
* variables as named parameters.
* <p>
* This is the same as calling
* <code>renderContext().namedParams(true).render(part)</code>
*
* @param part The {@link QueryPart} to be rendered
* @return The rendered SQL
*/
String renderNamedParams(QueryPart part);
/**
* Render a QueryPart in the context of this factory, inlining all bind
* variables.

View File

@ -62,6 +62,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
* <li>The formal name of the field, if it is a <i>physical table/view field</i></li>
* <li>The alias of an <i>aliased field</i></li>
* <li>A generated / unspecified value for any other <i>expression</i></li>
* <li>The name of a parameter if it is a named {@link Param}</li>
* </ul>
*/
@Override

View File

@ -0,0 +1,91 @@
/**
* 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;
import org.jooq.impl.Factory;
import org.jooq.tools.Convert;
/**
* A named parameter
*
* @author Lukas Eder
* @see Factory#param(String, Object)
*/
public interface Param<T> extends Field<T> {
/**
* {@inheritDoc}
* <hr/>
* The <code>Param</code>'s value for {@link #getName()} coincides with
* {@link #getParamName()}
*/
@Override
String getName();
/**
* The parameter name. This name is useful for two things:
* <ul>
* <li>Named parameters in frameworks that support them, such as Spring's
* <code>JdbcTemplate</code></li>
* <li>Accessing the parameter from the {@link Query} API, with
* {@link Query#getParam(String)}, {@link Query#getParams()}</li>
* </ul>
*/
String getParamName();
/**
* Get the parameter's underlying value. This returns <code>null</code> if
* no value has been set yet.
*/
T getValue();
/**
* Set the parameter's underlying value. This is the same as
* {@link #setConverted(Object)}, but ensures generic type-safety.
*
* @see #setConverted(Object)
*/
void setValue(T value);
/**
* Sets a converted value, using this {@link Param}'s underlying
* {@link DataType}, obtained from {@link #getDataType()}
*
* @see DataType#convert(Object)
* @see Convert#convert(Object, Class)
*/
void setConverted(Object value);
}

View File

@ -37,13 +37,14 @@
package org.jooq;
import java.util.List;
import java.util.Map;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.Factory;
/**
* Any query
*
*
* @author Lukas Eder
*/
public interface Query extends QueryPart {
@ -51,7 +52,7 @@ public interface Query extends QueryPart {
/**
* Execute the query, if it has been created with a properly configured
* factory
*
*
* @return A result value, depending on the concrete implementation of
* {@link Query}:
* <ul>
@ -84,8 +85,34 @@ public interface Query extends QueryPart {
String getSQL();
/**
* Retrieve the bind values that will be bound by this Query
* Retrieve the bind values that will be bound by this Query. This
* <code>List</code> cannot be modified. To modify bind values, use
* {@link #getParams()} instead.
*/
List<Object> getBindValues();
/**
* Get a <code>Map</code> of named parameters. The <code>Map</code> itself
* cannot be modified, but the {@link Param} elements allow for modifying
* bind values on an existing {@link Query}.
* <p>
* Bind values created with {@link Factory#val(Object)} will have their bind
* index as name.
*
* @see Param
* @see Factory#param(String, Object)
*/
Map<String, Param<?>> getParams();
/**
* Get a named parameter from the {@link Query}, provided its name.
* <p>
* Bind values created with {@link Factory#val(Object)} will have their bind
* index as name.
*
* @see Param
* @see Factory#param(String, Object)
*/
Param<?> getParam(String name);
}

View File

@ -37,6 +37,7 @@
package org.jooq;
import java.util.List;
import java.util.Map;
import org.jooq.exception.DataAccessException;
@ -60,14 +61,35 @@ public interface QueryPartInternal extends QueryPart {
void toSQL(RenderContext context);
/**
* Retrieve the bind values that will be bound by this QueryPart
* Retrieve the SQL that will be rendered by this {@link QueryPart}
* <p>
* This method is exposed publicly in {@link Query#getSQL()}
*/
String getSQL();
/**
* Retrieve the bind values that will be bound by this {@link QueryPart}
* <p>
* This method is exposed publicly in {@link Query#getBindValues()}
*/
List<Object> getBindValues();
/**
* Bind all parameters of this QueryPart to a PreparedStatement
* Retrieve the named parameters that will be bound by this {@link QueryPart}
* <p>
* This method is exposed publicly in {@link Query#getParams()}
*/
Map<String, Param<?>> getParams();
/**
* Retrieve a named parameter that will be bound by this {@link QueryPart}
* <p>
* This method is exposed publicly in {@link Query#getParam(String)}
*/
Param<?> getParam(String name);
/**
* Bind all parameters of this {@link QueryPart} to a PreparedStatement
* <p>
* This method is for JOOQ INTERNAL USE only. Do not reference directly
*
@ -79,7 +101,7 @@ public interface QueryPartInternal extends QueryPart {
void bind(BindContext context) throws DataAccessException;
/**
* Reproduce the SQL dialect this QueryPart was created with
* Reproduce the SQL dialect this {@link QueryPart} was created with
* <p>
* This method is for JOOQ INTERNAL USE only. Do not reference directly
*
@ -88,7 +110,7 @@ public interface QueryPartInternal extends QueryPart {
SQLDialect getDialect();
/**
* Check whether this QueryPart is able to declare fields in a
* Check whether this {@link QueryPart} is able to declare fields in a
* <code>SELECT</code> clause.
* <p>
* This method can be used by any {@link Context} to check how a certain SQL
@ -99,7 +121,7 @@ public interface QueryPartInternal extends QueryPart {
boolean declaresFields();
/**
* Check whether this QueryPart is able to declare tables in a
* Check whether this {@link QueryPart} is able to declare tables in a
* <code>FROM</code> clause or <code>JOIN</code> clause.
* <p>
* This method can be used by any {@link Context} to check how a certain SQL

View File

@ -106,4 +106,18 @@ public interface RenderContext extends Context<RenderContext> {
* Set the new context value for {@link #inline()}
*/
RenderContext inline(boolean inline);
/**
* Whether bind variables should be rendered as named parameters:<br/>
* <code>&#160; :1, :2, :custom_name</code>
* <p>
* or as JDBC bind variables <br/>
* <code>&#160; ?</code>
*/
boolean namedParams();
/**
* Set the new context value for {@link #namedParams()}
*/
RenderContext namedParams(boolean renderNamedParams);
}

View File

@ -0,0 +1,175 @@
/**
* 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.impl;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import org.jooq.BindContext;
import org.jooq.Configuration;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
/**
* A base class for {@link BindContext} implementations
*
* @author Lukas Eder
*/
abstract class AbstractBindContext extends AbstractContext<BindContext> implements BindContext {
/**
* Generated UID
*/
private static final long serialVersionUID = -319766597723101571L;
AbstractBindContext(Configuration configuration) {
super(configuration);
}
AbstractBindContext(BindContext context) {
this((Configuration) context);
declareFields(context.declareFields());
declareTables(context.declareTables());
}
// ------------------------------------------------------------------------
// BindContext API
// ------------------------------------------------------------------------
@Override
public final BindContext bind(Collection<? extends QueryPart> parts) {
for (QueryPart part : parts) {
bind(part);
}
return this;
}
@Override
public final BindContext bind(QueryPart[] parts) {
bind(Arrays.asList(parts));
return this;
}
@Override
public final BindContext bind(QueryPart part) {
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
// 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 not declaring, or "part" can declare
else {
bindInternal(internal);
}
return this;
}
@Override
public final BindContext bindValues(Object... values) {
// [#724] When values is null, this is probably due to API-misuse
// The user probably meant new Object[] { null }
if (values == null) {
bindValues(new Object[] { null });
}
else {
for (Object value : values) {
Class<?> type = (value == null) ? Object.class : value.getClass();
bindValue(value, type);
}
}
return this;
}
@Override
public final BindContext bindValue(Object value, Class<?> type) {
try {
return bindValue0(value, type);
}
catch (SQLException e) {
throw Util.translate("DefaultBindContext.bindValue", null, e);
}
}
// ------------------------------------------------------------------------
// AbstractBindContext template methods
// ------------------------------------------------------------------------
/**
* Subclasses may override this method to achieve different behaviour
*/
protected void bindInternal(QueryPartInternal internal) {
internal.bind(this);
}
/**
* Subclasses may override this method to achieve different behaviour
*/
@SuppressWarnings("unused")
protected BindContext bindValue0(Object value, Class<?> type) throws SQLException {
return this;
}
// ------------------------------------------------------------------------
// Object API
// ------------------------------------------------------------------------
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
toString(sb);
return sb.toString();
}
}

View File

@ -56,6 +56,7 @@ abstract class AbstractContext<C extends Context<C>> implements Context<C> {
boolean declareFields;
boolean declareTables;
boolean subquery;
int index;
AbstractContext(Configuration configuration) {
this.configuration = configuration;
@ -107,6 +108,16 @@ abstract class AbstractContext<C extends Context<C>> implements Context<C> {
return (C) this;
}
@Override
public final int nextIndex() {
return ++index;
}
@Override
public final int peekIndex() {
return index + 1;
}
// ------------------------------------------------------------------------
// Configuration API
// ------------------------------------------------------------------------
@ -127,7 +138,10 @@ abstract class AbstractContext<C extends Context<C>> implements Context<C> {
}
void toString(StringBuilder sb) {
sb.append("\ndeclaring [");
sb.append( "bind index [");
sb.append(index);
sb.append("]");
sb.append("\ndeclaring [");
if (declareFields) {
sb.append("fields");
@ -139,7 +153,7 @@ abstract class AbstractContext<C extends Context<C>> implements Context<C> {
sb.append("-");
}
sb.append("]\nsubquery [");
sb.append("]\nsubquery [");
sb.append(subquery);
sb.append("]");
}

View File

@ -42,10 +42,12 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jooq.Attachable;
import org.jooq.AttachableInternal;
import org.jooq.Configuration;
import org.jooq.Param;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
@ -107,16 +109,50 @@ abstract class AbstractQueryPart implements QueryPartInternal, AttachableInterna
/**
* This method is also declared as {@link Query#getSQL()}
* <p>
* {@inheritDoc}
*/
@Override
public final String getSQL() {
return create().render(this);
}
/**
* This method is also declared as {@link Query#getBindValues()}
* <p>
* {@inheritDoc}
*/
@Override
public final List<Object> getBindValues() {
BindValueCollector collector = new BindValueCollector();
create(getConfiguration()).bind(this, collector);
return collector.result;
List<Object> result = new ArrayList<Object>();
for (Param<?> param : getParams().values()) {
result.add(param.getValue());
}
return Collections.unmodifiableList(result);
}
/**
* This method is also declared as {@link Query#getParams()}
* <p>
* {@inheritDoc}
*/
@Override
public final Map<String, Param<?>> getParams() {
ParamCollector collector = new ParamCollector(getConfiguration());
collector.bind(this);
return Collections.unmodifiableMap(collector.result);
}
/**
* This method is also declared as {@link Query#getParam(String)}
* <p>
* {@inheritDoc}
*/
@Override
public final Param<?> getParam(String name) {
return getParams().get(name);
}
/**

View File

@ -1,529 +0,0 @@
/**
* 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.impl;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import org.jooq.Query;
/**
* A stub prepared statement that acts as a collector of bind values, in order
* to retrieve the bound values in correct order for
* {@link Query#getBindValues()}
*
* @author Lukas Eder
*/
class BindValueCollector implements PreparedStatement {
final List<Object> result = new ArrayList<Object>();
@Override
public <T> T unwrap(Class<T> iface) {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public ResultSet executeQuery(String sql) {
return null;
}
@Override
public int executeUpdate(String sql) {
return 0;
}
@Override
public void close() {}
@Override
public int getMaxFieldSize() {
return 0;
}
@Override
public void setMaxFieldSize(int max) {}
@Override
public int getMaxRows() {
return 0;
}
@Override
public void setMaxRows(int max) {}
@Override
public void setEscapeProcessing(boolean enable) {}
@Override
public int getQueryTimeout() {
return 0;
}
@Override
public void setQueryTimeout(int seconds) {}
@Override
public void cancel() {}
@Override
public SQLWarning getWarnings() {
return null;
}
@Override
public void clearWarnings() {}
@Override
public void setCursorName(String name) {}
@Override
public boolean execute(String sql) {
return false;
}
@Override
public ResultSet getResultSet() {
return null;
}
@Override
public int getUpdateCount() {
return 0;
}
@Override
public boolean getMoreResults() {
return false;
}
@Override
public void setFetchDirection(int direction) {}
@Override
public int getFetchDirection() {
return 0;
}
@Override
public void setFetchSize(int rows) {}
@Override
public int getFetchSize() {
return 0;
}
@Override
public int getResultSetConcurrency() {
return 0;
}
@Override
public int getResultSetType() {
return 0;
}
@Override
public void addBatch(String sql) {}
@Override
public void clearBatch() {}
@Override
public int[] executeBatch() {
return null;
}
@Override
public Connection getConnection() {
return null;
}
@Override
public boolean getMoreResults(int current) {
return false;
}
@Override
public ResultSet getGeneratedKeys() {
return null;
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) {
return 0;
}
@Override
public int executeUpdate(String sql, int[] columnIndexes) {
return 0;
}
@Override
public int executeUpdate(String sql, String[] columnNames) {
return 0;
}
@Override
public boolean execute(String sql, int autoGeneratedKeys) {
return false;
}
@Override
public boolean execute(String sql, int[] columnIndexes) {
return false;
}
@Override
public boolean execute(String sql, String[] columnNames) {
return false;
}
@Override
public int getResultSetHoldability() {
return 0;
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void setPoolable(boolean poolable) {}
@Override
public boolean isPoolable() {
return false;
}
@Override
public ResultSet executeQuery() {
return null;
}
@Override
public int executeUpdate() {
return 0;
}
@Override
public void setNull(int parameterIndex, int sqlType) {
result.add(null);
}
@Override
public void setBoolean(int parameterIndex, boolean x) {
result.add(x);
}
@Override
public void setByte(int parameterIndex, byte x) {
result.add(x);
}
@Override
public void setShort(int parameterIndex, short x) {
result.add(x);
}
@Override
public void setInt(int parameterIndex, int x) {
result.add(x);
}
@Override
public void setLong(int parameterIndex, long x) {
result.add(x);
}
@Override
public void setFloat(int parameterIndex, float x) {
result.add(x);
}
@Override
public void setDouble(int parameterIndex, double x) {
result.add(x);
}
@Override
public void setBigDecimal(int parameterIndex, BigDecimal x) {
result.add(x);
}
@Override
public void setString(int parameterIndex, String x) {
result.add(x);
}
@Override
public void setBytes(int parameterIndex, byte[] x) {
result.add(x);
}
@Override
public void setDate(int parameterIndex, Date x) {
result.add(x);
}
@Override
public void setTime(int parameterIndex, Time x) {
result.add(x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x) {
result.add(x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, int length) {
result.add(x);
}
@Override
public void setUnicodeStream(int parameterIndex, InputStream x, int length) {
result.add(x);
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length) {
result.add(x);
}
@Override
public void clearParameters() {}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) {
result.add(x);
}
@Override
public void setObject(int parameterIndex, Object x) {
result.add(x);
}
@Override
public boolean execute() {
return false;
}
@Override
public void addBatch() {}
@Override
public void setCharacterStream(int parameterIndex, Reader x, int length) {
result.add(x);
}
@Override
public void setRef(int parameterIndex, Ref x) {
result.add(x);
}
@Override
public void setBlob(int parameterIndex, Blob x) {
result.add(x);
}
@Override
public void setClob(int parameterIndex, Clob x) {
result.add(x);
}
@Override
public void setArray(int parameterIndex, Array x) {
result.add(x);
}
@Override
public ResultSetMetaData getMetaData() {
return null;
}
@Override
public void setDate(int parameterIndex, Date x, Calendar cal) {
result.add(x);
}
@Override
public void setTime(int parameterIndex, Time x, Calendar cal) {
result.add(x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) {
result.add(x);
}
@Override
public void setNull(int parameterIndex, int sqlType, String typeName) {
result.add(null);
}
@Override
public void setURL(int parameterIndex, URL x) {
result.add(x);
}
@Override
public ParameterMetaData getParameterMetaData() {
return null;
}
@Override
public void setRowId(int parameterIndex, RowId x) {
result.add(x);
}
@Override
public void setNString(int parameterIndex, String x) {
result.add(x);
}
@Override
public void setNCharacterStream(int parameterIndex, Reader x, long length) {
result.add(x);
}
@Override
public void setNClob(int parameterIndex, NClob x) {
result.add(x);
}
@Override
public void setClob(int parameterIndex, Reader x, long length) {
result.add(x);
}
@Override
public void setBlob(int parameterIndex, InputStream x, long length) {
result.add(x);
}
@Override
public void setNClob(int parameterIndex, Reader x, long length) {
result.add(x);
}
@Override
public void setSQLXML(int parameterIndex, SQLXML x) {
result.add(x);
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) {
result.add(x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, long length) {
result.add(x);
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, long length) {
result.add(x);
}
@Override
public void setCharacterStream(int parameterIndex, Reader x, long length) {
result.add(x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x) {
result.add(x);
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x) {
result.add(x);
}
@Override
public void setCharacterStream(int parameterIndex, Reader x) {
result.add(x);
}
@Override
public void setNCharacterStream(int parameterIndex, Reader x) {
result.add(x);
}
@Override
public void setClob(int parameterIndex, Reader x) {
result.add(x);
}
@Override
public void setBlob(int parameterIndex, InputStream x) {
result.add(x);
}
@Override
public void setNClob(int parameterIndex, Reader x) {
result.add(x);
}
}

View File

@ -1,151 +0,0 @@
/**
* 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.impl;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import org.jooq.Attachable;
import org.jooq.BindContext;
import org.jooq.DataType;
import org.jooq.EnumType;
import org.jooq.MasterDataType;
import org.jooq.RenderContext;
/**
* @author Lukas Eder
*/
class Constant<T> extends AbstractField<T> {
private static final long serialVersionUID = 6807729087019209084L;
private final T value;
Constant(T value, DataType<T> type) {
super("" + value, type);
this.value = value;
}
@Override
public final List<Attachable> getAttachables() {
return Collections.emptyList();
}
@Override
public final void toSQL(RenderContext context) {
// Casting is only done when parameters are NOT inlined
if (!context.inline()) {
// Generated enums should not be cast...
// The exception's exception
if (!(value instanceof EnumType) && !(value instanceof MasterDataType)) {
switch (context.getDialect()) {
// These dialects can hardly detect the type of a bound constant.
case DB2:
case DERBY:
// These dialects have some trouble, when they mostly get it right.
case H2:
case HSQLDB:
// [#722] TODO This is probably not entirely right.
case INGRES:
// [#632] Sybase needs explicit casting in very rare cases.
case SYBASE: {
toSQLCast(context);
return;
}
}
}
}
// Most RDBMS can handle constants as typeless literals
FieldTypeHelper.toSQL(context, value, this);
}
/**
* Render the bind variable including a cast, if necessary
*/
private void toSQLCast(RenderContext context) {
switch (context.getDialect()) {
// [#822] Some RDBMS need precision / scale information on BigDecimals
case DB2:
case DERBY:
case HSQLDB: {
// Add precision / scale on BigDecimals
if (getType() == BigDecimal.class) {
int scale = ((BigDecimal) value).scale();
int precision = scale + ((BigDecimal) value).precision();
context.sql("cast(? as ")
.sql(getDataType(context).getCastTypeName(context, precision, scale))
.sql(")");
break;
}
// No break, fall through
else {
}
}
// These dialects don't need precision / scale info on BigDecimals
case H2:
case INGRES:
case SYBASE: {
context.sql("cast(? as ")
.sql(getDataType(context).getCastTypeName(context))
.sql(")");
}
}
}
@Override
public final void bind(BindContext context) {
context.bindValue(value, getType());
}
@Override
public final boolean isNullLiteral() {
return value == null;
}
}

View File

@ -46,15 +46,12 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import org.jooq.ArrayRecord;
import org.jooq.BindContext;
import org.jooq.Configuration;
import org.jooq.EnumType;
import org.jooq.MasterDataType;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
import org.jooq.SQLDialect;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.tools.JooqLogger;
@ -63,7 +60,7 @@ import org.jooq.tools.unsigned.UNumber;
/**
* @author Lukas Eder
*/
class DefaultBindContext extends AbstractContext<BindContext> implements BindContext {
class DefaultBindContext extends AbstractBindContext {
/**
* Generated UID
@ -72,7 +69,6 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
private static final JooqLogger log = JooqLogger.getLogger(Util.class);
private final PreparedStatement stmt;
private int index;
DefaultBindContext(Configuration configuration, PreparedStatement stmt) {
super(configuration);
@ -93,89 +89,8 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
}
@Override
public final int nextIndex() {
return ++index;
}
@Override
public final int peekIndex() {
return index + 1;
}
@Override
public final BindContext bind(QueryPart part) {
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
// We're declaring fields, but "part" does not declare fields
if (declareFields() && !internal.declaresFields()) {
declareFields(false);
internal.bind(this);
declareFields(true);
}
// We're declaring tables, but "part" does not declare tables
else if (declareTables() && !internal.declaresTables()) {
declareTables(false);
internal.bind(this);
declareTables(true);
}
// We're not declaring, or "part" can declare
else {
internal.bind(this);
}
return this;
}
@Override
public final BindContext bind(Collection<? extends QueryPart> parts) {
for (QueryPart part : parts) {
bind(part);
}
return this;
}
@Override
public final BindContext bind(QueryPart[] parts) {
bind(Arrays.asList(parts));
return this;
}
@Override
public final BindContext bindValues(Object... values) {
// [#724] When values is null, this is probably due to API-misuse
// The user probably meant new Object[] { null }
if (values == null) {
bindValues(new Object[] { null });
}
else {
for (Object value : values) {
Class<?> type = (value == null) ? Object.class : value.getClass();
bindValue(value, type);
}
}
return this;
}
@Override
public final BindContext bindValue(Object value, Class<?> type) {
try {
return bindValue0(value, type);
}
catch (SQLException e) {
throw Util.translate("DefaultBindContext.bindValue", null, e);
}
}
@SuppressWarnings("unchecked")
private final BindContext bindValue0(Object value, Class<?> type) throws SQLException {
protected final BindContext bindValue0(Object value, Class<?> type) throws SQLException {
SQLDialect dialect = configuration.getDialect();
if (log.isTraceEnabled()) {
@ -335,20 +250,4 @@ class DefaultBindContext extends AbstractContext<BindContext> implements BindCon
return this;
}
// ------------------------------------------------------------------------
// Object API
// ------------------------------------------------------------------------
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("binding [index ");
sb.append(index);
sb.append("]");
toString(sb);
return sb.toString();
}
}

View File

@ -52,6 +52,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
private final StringBuilder sql;
private boolean inline;
private boolean renderNamedParams;
private int alias;
DefaultRenderContext(Configuration configuration) {
@ -64,6 +65,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
this((Configuration) context);
inline(context.inline());
namedParams(context.namedParams());
declareFields(context.declareFields());
declareTables(context.declareTables());
}
@ -187,6 +189,17 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
return this;
}
@Override
public final boolean namedParams() {
return renderNamedParams;
}
@Override
public final RenderContext namedParams(boolean r) {
this.renderNamedParams = r;
return this;
}
// ------------------------------------------------------------------------
// Object API
// ------------------------------------------------------------------------
@ -195,12 +208,15 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("rendering [");
sb.append( "rendering [");
sb.append(render());
sb.append("]");
sb.append("\ninlining [");
sb.append("\ninlining [");
sb.append(inline);
sb.append("]");
sb.append("\nnamed params [");
sb.append(renderNamedParams);
sb.append("]");
toString(sb);
return sb.toString();

View File

@ -79,6 +79,7 @@ import org.jooq.InsertSetStep;
import org.jooq.InsertValuesStep;
import org.jooq.LoaderOptionsStep;
import org.jooq.MergeUsingStep;
import org.jooq.Param;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.Record;
@ -218,6 +219,7 @@ public class Factory implements FactoryOperations {
* <li> <code>{@link RenderContext#declareFields()} == false</code></li>
* <li> <code>{@link RenderContext#declareTables()} == false</code></li>
* <li> <code>{@link RenderContext#inline()} == false</code></li>
* <li> <code>{@link RenderContext#namedParams()} == false</code></li>
* </ul>
* <p>
* RenderContext for JOOQ INTERNAL USE only. Avoid referencing it directly
@ -234,6 +236,14 @@ public class Factory implements FactoryOperations {
return renderContext().render(part);
}
/**
* {@inheritDoc}
*/
@Override
public final String renderNamedParams(QueryPart part) {
return renderContext().namedParams(true).render(part);
}
/**
* {@inheritDoc}
*/
@ -3531,18 +3541,87 @@ public class Factory implements FactoryOperations {
// Bind values
// -------------------------------------------------------------------------
/**
* Create a named parameter with a generic type ({@link Object} /
* {@link SQLDataType#OTHER}) and no initial value.
* <p>
* Try to avoid this method when using any of these databases, as these
* databases may have trouble inferring the type of the bind value. Use
* typed named parameters instead, using {@link #param(String, Class)} or
* {@link #param(String, DataType)}
* <ul>
* <li> {@link SQLDialect#DB2}</li>
* <li> {@link SQLDialect#DERBY}</li>
* <li> {@link SQLDialect#H2}</li>
* <li> {@link SQLDialect#HSQLDB}</li>
* <li> {@link SQLDialect#INGRES}</li>
* <li> {@link SQLDialect#SYBASE}</li>
* </ul>
*
* @see #param(String, Object)
*/
public static Param<Object> param(String name) {
return param(name, Object.class);
}
/**
* Create a named parameter with a defined type and no initial value.
*
* @see #param(String, Object)
*/
public static <T> Param<T> param(String name, Class<? extends T> type) {
return param(name, SQLDataType.getDataType(null, type));
}
/**
* Create a named parameter with a defined type and no initial value.
*
* @see #param(String, Object)
*/
public static <T> Param<T> param(String name, DataType<T> type) {
return new Val<T>(null, type, name);
}
/**
* Create a named parameter with an initial value.
* <p>
* Named parameters are useful for several use-cases:
* <ul>
* <li>They can be used with Spring's <code>JdbcTemplate</code>, which
* supports named parameters. Use
* {@link FactoryOperations#renderNamedParams(QueryPart)} to render
* parameter names in SQL</li>
* <li>Named parameters can be retrieved using a well-known name from
* {@link Query#getParam(String)} and {@link Query#getParams()}.</li>
* </ul>
*
* @see Query#getParam(String)
* @see Query#getParams()
* @see #renderNamedParams(QueryPart)
*/
public static <T> Param<T> param(String name, T value) {
return new Val<T>(value, val(value).getDataType(), name);
}
/**
* Get a value
* <p>
* jOOQ tries to derive the RDBMS {@link DataType} from the provided Java
* type <code>&lt;T&gt;</code>. This may not always be accurate, which can
* lead to problems in some strongly typed RDMBS (namely:
* {@link SQLDialect#DERBY}, {@link SQLDialect#DB2}, {@link SQLDialect#H2},
* {@link SQLDialect#HSQLDB}), especially when value is <code>null</code>.
* lead to problems in some strongly typed RDMBS, especially when value is
* <code>null</code>. These databases are namely:
* <ul>
* <li>{@link SQLDialect#DERBY}</li>
* <li>{@link SQLDialect#DB2}</li>
* <li>{@link SQLDialect#H2}</li>
* <li>{@link SQLDialect#HSQLDB}
* <li>{@link SQLDialect#INGRES}
* <li>{@link SQLDialect#SYBASE}
* </ul>
* <p>
* If you need more type-safety, please use
* {@link #val(Object, DataType)} instead, and provide the precise
* RDMBS-specific data type, that is needed.
* If you need more type-safety, please use {@link #val(Object, DataType)}
* instead, and provide the precise RDMBS-specific data type, that is
* needed.
*
* @param <T> The generic value type
* @param value The constant value
@ -3624,7 +3703,7 @@ public class Factory implements FactoryOperations {
// The default behaviour
else {
return new Constant<T>(type.convert(value), type);
return new Val<T>(type.convert(value), type);
}
}

View File

@ -53,7 +53,6 @@ import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -65,9 +64,7 @@ import org.jooq.EnumType;
import org.jooq.Field;
import org.jooq.FieldProvider;
import org.jooq.MasterDataType;
import org.jooq.NamedTypeProviderQueryPart;
import org.jooq.Record;
import org.jooq.RenderContext;
import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.UDTRecord;
@ -109,130 +106,6 @@ public final class FieldTypeHelper {
private static final JooqLogger log = JooqLogger.getLogger(FieldTypeHelper.class);
public static void toSQL(RenderContext context, Object value) {
if (value == null) {
toSQL(context, value, Object.class);
}
else {
toSQL(context, value, value.getClass());
}
}
public static void toSQL(RenderContext context, Object value, NamedTypeProviderQueryPart<?> field) {
toSQL(context, value, field.getType());
}
public static void toSQL(RenderContext context, Object value, Class<?> type) {
if (context.inline()) {
if (value == null) {
context.sql("null");
}
else if (type == Blob.class) {
// blob's are treated as byte[] by jOOQ
context.sql("[BLOB]");
}
else if (type == Boolean.class) {
context.sql(value.toString());
}
else if (type == BigInteger.class) {
context.sql(value.toString());
}
else if (type == BigDecimal.class) {
context.sql(value.toString());
}
else if (type == Byte.class) {
context.sql(value.toString());
}
else if (type == byte[].class) {
context.sql("'")
.sql(new String((byte[]) value).replace("'", "''"))
.sql("'");
}
else if (type == Clob.class) {
// clob's are treated as String by jOOQ
context.sql("[CLOB]");
}
else if (type == Date.class) {
context.sql("'").sql(value.toString()).sql("'");
}
else if (type == Double.class) {
context.sql(value.toString());
}
else if (type == Float.class) {
context.sql(value.toString());
}
else if (type == Integer.class) {
context.sql(value.toString());
}
else if (type == Long.class) {
context.sql(value.toString());
}
else if (type == Short.class) {
context.sql(value.toString());
}
else if (type == String.class) {
context.sql("'")
.sql(value.toString().replace("'", "''"))
.sql("'");
}
else if (type == Time.class) {
context.sql("'").sql(value.toString()).sql("'");
}
else if (type == Timestamp.class) {
context.sql("'").sql(value.toString()).sql("'");
}
else if (type.isArray()) {
context.sql("ARRAY")
.sql(Arrays.asList((Object[]) value).toString());
}
else if (UNumber.class.isAssignableFrom(type)) {
context.sql(value.toString());
}
else if (ArrayRecord.class.isAssignableFrom(type)) {
context.sql(value.toString());
}
else if (EnumType.class.isAssignableFrom(type)) {
toSQL(context, ((EnumType) value).getLiteral());
}
else if (MasterDataType.class.isAssignableFrom(type)) {
toSQL(context, ((MasterDataType<?>) value).getPrimaryKey());
}
else if (UDTRecord.class.isAssignableFrom(type)) {
context.sql("[UDT]");
}
else {
throw new UnsupportedOperationException("Class " + type + " is not supported");
}
}
// In Postgres, some additional casting must be done in some cases...
// TODO: Improve this implementation with [#215] (cast support)
else if (context.getDialect() == SQLDialect.POSTGRES) {
// Postgres needs explicit casting for array types
if (type.isArray() && byte[].class != type) {
context.sql("?::");
context.sql(getDataType(context.getDialect(), type).getCastTypeName(context));
}
// ... and also for enum types
else if (EnumType.class.isAssignableFrom(type)) {
context.sql("?::");
context.literal(((EnumType) value).getName());
}
else {
context.sql("?");
}
}
else {
context.sql("?");
}
}
@SuppressWarnings("unchecked")
public static <T> T getFromSQLInput(Configuration configuration, SQLInput stream, Field<T> field) throws SQLException {
Class<? extends T> type = field.getType();

View File

@ -0,0 +1,96 @@
/**
* 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.impl;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.jooq.BindContext;
import org.jooq.Configuration;
import org.jooq.Param;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
import org.jooq.tools.StringUtils;
/**
* A stub {@link BindContext} that acts as a collector of {@link Param}
* {@link QueryPart}'s
*
* @author Lukas Eder
*/
class ParamCollector extends AbstractBindContext {
/**
* Generated UID
*/
private static final long serialVersionUID = -3741599479523459297L;
final Map<String, Param<?>> result = new LinkedHashMap<String, Param<?>>();
ParamCollector(Configuration configuration) {
super(configuration);
}
@Override
public final PreparedStatement statement() {
throw new UnsupportedOperationException();
}
@Override
protected final void bindInternal(QueryPartInternal internal) {
if (internal instanceof Param) {
Param<?> param = (Param<?>) internal;
String i = String.valueOf(nextIndex());
if (StringUtils.isBlank(param.getParamName())) {
result.put(i, param);
}
else {
result.put(param.getParamName(), param);
}
}
else {
super.bindInternal(internal);
}
}
@Override
protected final BindContext bindValue0(Object value, Class<?> type) throws SQLException {
throw new UnsupportedOperationException();
}
}

View File

@ -35,6 +35,8 @@
*/
package org.jooq.impl;
import static org.jooq.impl.Factory.val;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@ -173,7 +175,7 @@ final class Util {
context.sql(split[i]);
if (i < bindings.length) {
FieldTypeHelper.toSQL(context, bindings[i]);
context.sql(val(bindings[i]));
}
}
}

View File

@ -0,0 +1,307 @@
/**
* 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.impl;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jooq.ArrayRecord;
import org.jooq.Attachable;
import org.jooq.BindContext;
import org.jooq.DataType;
import org.jooq.EnumType;
import org.jooq.MasterDataType;
import org.jooq.Param;
import org.jooq.RenderContext;
import org.jooq.SQLDialect;
import org.jooq.UDTRecord;
import org.jooq.tools.StringUtils;
/**
* @author Lukas Eder
*/
class Val<T> extends AbstractField<T> implements Param<T> {
private static final long serialVersionUID = 6807729087019209084L;
private final String paramName;
private T value;
Val(T value, DataType<T> type) {
this(value, type, null);
}
Val(T value, DataType<T> type, String paramName) {
super(name(value, paramName), type);
this.paramName = paramName;
this.value = value;
}
private static String name(Object value, String paramName) {
return paramName == null ? String.valueOf(value) : paramName;
}
// ------------------------------------------------------------------------
// Field API
// ------------------------------------------------------------------------
@Override
public final List<Attachable> getAttachables() {
return Collections.emptyList();
}
@Override
public final void toSQL(RenderContext context) {
// Casting is only done when parameters are NOT inlined
if (!context.inline()) {
// Generated enums should not be cast...
// The exception's exception
if (!(getValue() instanceof EnumType) && !(getValue() instanceof MasterDataType)) {
switch (context.getDialect()) {
// These dialects can hardly detect the type of a bound constant.
case DB2:
case DERBY:
// These dialects have some trouble, when they mostly get it right.
case H2:
case HSQLDB:
// [#722] TODO This is probably not entirely right.
case INGRES:
// [#632] Sybase needs explicit casting in very rare cases.
case SYBASE: {
toSQLCast(context);
return;
}
}
}
}
// Most RDBMS can handle constants as typeless literals
toSQL(context, getValue(), this.getType());
}
/**
* Render the bind variable including a cast, if necessary
*/
private void toSQLCast(RenderContext context) {
switch (context.getDialect()) {
// [#822] Some RDBMS need precision / scale information on BigDecimals
case DB2:
case DERBY:
case HSQLDB: {
// Add precision / scale on BigDecimals
if (getType() == BigDecimal.class) {
int scale = ((BigDecimal) getValue()).scale();
int precision = scale + ((BigDecimal) getValue()).precision();
context.sql("cast(")
.sql(getBindVariable(context))
.sql(" as ")
.sql(getDataType(context).getCastTypeName(context, precision, scale))
.sql(")");
break;
}
// No break, fall through
else {
}
}
// These dialects don't need precision / scale info on BigDecimals
case H2:
case INGRES:
case SYBASE: {
context.sql("cast(")
.sql(getBindVariable(context))
.sql(" as ")
.sql(getDataType(context).getCastTypeName(context))
.sql(")");
}
}
}
/**
* Get a bind variable, depending on value of
* {@link RenderContext#namedParams()}
*/
private final String getBindVariable(RenderContext context) {
if (context.namedParams()) {
int index = context.nextIndex();
if (StringUtils.isBlank(getParamName())) {
return ":" + index;
}
else {
return ":" + getName();
}
}
else {
return "?";
}
}
/**
* Inlining abstraction
*/
private void toSQL(RenderContext context, Object val) {
if (val == null) {
toSQL(context, val, Object.class);
}
else {
toSQL(context, val, val.getClass());
}
}
/**
* Inlining abstraction
*/
private void toSQL(RenderContext context, Object val, Class<?> type) {
if (context.inline()) {
if (val == null) {
context.sql("null");
}
else if (type == Boolean.class) {
context.sql(val.toString());
}
else if (type == byte[].class) {
// TODO [#990] This can cause issues
context.sql("'")
.sql(new String((byte[]) val).replace("'", "''"))
.sql("'");
}
else if (Number.class.isAssignableFrom(type)) {
context.sql(val.toString());
}
else if (type.isArray()) {
context.sql("ARRAY")
.sql(Arrays.asList((Object[]) val).toString());
}
else if (ArrayRecord.class.isAssignableFrom(type)) {
context.sql(val.toString());
}
else if (EnumType.class.isAssignableFrom(type)) {
toSQL(context, ((EnumType) val).getLiteral());
}
else if (MasterDataType.class.isAssignableFrom(type)) {
toSQL(context, ((MasterDataType<?>) val).getPrimaryKey());
}
else if (UDTRecord.class.isAssignableFrom(type)) {
context.sql("[UDT]");
}
// Known fall-through types:
// - Blob, Clob (both not supported by jOOQ)
// - String
// - java.util.Date subtypes
else {
context.sql("'")
.sql(val.toString().replace("'", "''"))
.sql("'");
}
}
// In Postgres, some additional casting must be done in some cases...
// TODO: Improve this implementation with [#215] (cast support)
else if (context.getDialect() == SQLDialect.POSTGRES) {
// Postgres needs explicit casting for array types
if (type.isArray() && byte[].class != type) {
context.sql(getBindVariable(context));
context.sql("::");
context.sql(FieldTypeHelper.getDataType(context.getDialect(), type).getCastTypeName(context));
}
// ... and also for enum types
else if (EnumType.class.isAssignableFrom(type)) {
context.sql(getBindVariable(context));
context.sql("::");
context.literal(((EnumType) val).getName());
}
else {
context.sql(getBindVariable(context));
}
}
else {
context.sql(getBindVariable(context));
}
}
@Override
public final void bind(BindContext context) {
context.bindValue(getValue(), getType());
}
@Override
public final boolean isNullLiteral() {
return getValue() == null;
}
// ------------------------------------------------------------------------
// Param API
// ------------------------------------------------------------------------
@Override
public final void setValue(T value) {
setConverted(value);
}
@Override
public final void setConverted(Object value) {
this.value = getDataType().convert(value);
}
@Override
public final T getValue() {
return value;
}
@Override
public final String getParamName() {
return paramName;
}
}

View File

@ -48,6 +48,7 @@ import static org.jooq.impl.Factory.falseCondition;
import static org.jooq.impl.Factory.field;
import static org.jooq.impl.Factory.max;
import static org.jooq.impl.Factory.min;
import static org.jooq.impl.Factory.param;
import static org.jooq.impl.Factory.replace;
import static org.jooq.impl.Factory.round;
import static org.jooq.impl.Factory.sum;
@ -67,6 +68,8 @@ import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -83,6 +86,8 @@ import org.jooq.Insert;
import org.jooq.InsertQuery;
import org.jooq.Merge;
import org.jooq.Operator;
import org.jooq.Param;
import org.jooq.Query;
import org.jooq.RenderContext;
import org.jooq.Select;
import org.jooq.SelectFinalStep;
@ -184,8 +189,12 @@ public class jOOQTest {
return r_decT().inline(true);
}
protected final RenderContext r_refP() {
return r_ref().namedParams(true);
}
@Test
public final void testNullPointerExceptionSafety() throws Exception {
public void testNullPointerExceptionSafety() throws Exception {
// Functions created from a field
// ------------------------------
assertEquals(
@ -524,7 +533,7 @@ public class jOOQTest {
}
@Test
public final void testTruncate() throws Exception {
public void testTruncate() throws Exception {
Truncate<Table1Record> t = create.truncate(TABLE1);
assertEquals("truncate table \"TABLE1\"", r_dec().render(t));
@ -532,7 +541,7 @@ public class jOOQTest {
}
@Test
public final void testAliasing() throws Exception {
public void testAliasing() throws Exception {
assertEquals("\"TABLE1\"", r_decT().render(TABLE1));
assertEquals("\"TABLE1\"", r_decF().render(TABLE1));
assertEquals("\"TABLE1\"", r_ref().render(TABLE1));
@ -581,7 +590,7 @@ public class jOOQTest {
}
@Test
public final void testMultipleCombinedCondition() throws Exception {
public void testMultipleCombinedCondition() throws Exception {
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID2.equal(20);
Condition c3 = FIELD_ID1.equal(30);
@ -617,7 +626,7 @@ public class jOOQTest {
}
@Test
public final void testBetweenCondition() throws Exception {
public void testBetweenCondition() throws Exception {
Condition c = FIELD_ID1.between(1, 10);
assertEquals("\"TABLE1\".\"ID1\" between 1 and 10", r_refI().render(c));
assertEquals("\"TABLE1\".\"ID1\" between ? and ?", r_ref().render(c));
@ -634,7 +643,7 @@ public class jOOQTest {
}
@Test
public final void testInCondition() throws Exception {
public void testInCondition() throws Exception {
Condition c = FIELD_ID1.in(new Integer[0]);
assertEquals(falseCondition(), c);
@ -657,7 +666,7 @@ public class jOOQTest {
}
@Test
public final void testInSelectCondition() throws Exception {
public void testInSelectCondition() throws Exception {
Condition c = FIELD_ID1.in(create.selectFrom(TABLE1).where(FIELD_NAME1.equal("x")));
assertEquals("\"TABLE1\".\"ID1\" in (select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where \"TABLE1\".\"NAME1\" = 'x')", r_refI().render(c));
assertEquals("\"TABLE1\".\"ID1\" in (select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where \"TABLE1\".\"NAME1\" = ?)", r_ref().render(c));
@ -677,7 +686,7 @@ public class jOOQTest {
}
@Test
public final void testCompareCondition() throws Exception {
public void testCompareCondition() throws Exception {
Condition c = FIELD_ID1.equal(10);
assertEquals("\"TABLE1\".\"ID1\" = 10", r_refI().render(c));
assertEquals("\"TABLE1\".\"ID1\" = ?", r_ref().render(c));
@ -693,7 +702,7 @@ public class jOOQTest {
}
@Test
public final void testNotCondition() throws Exception {
public void testNotCondition() throws Exception {
Condition c = FIELD_ID1.equal(10).not();
assertEquals("not(\"TABLE1\".\"ID1\" = 10)", r_refI().render(c));
assertEquals("not(\"TABLE1\".\"ID1\" = ?)", r_ref().render(c));
@ -712,7 +721,7 @@ public class jOOQTest {
}
@Test
public final void testPlainSQLCondition() throws Exception {
public void testPlainSQLCondition() throws Exception {
Condition c1 = condition("TABLE1.ID = 10");
Condition c2 = condition("TABLE1.ID = ? and TABLE2.ID = ?", 10, "20");
@ -734,7 +743,7 @@ public class jOOQTest {
}
@Test
public final void testCustomCondition() throws Exception {
public void testCustomCondition() throws Exception {
Condition c = new CustomCondition() {
private static final long serialVersionUID = 6302350477408137757L;
@ -772,7 +781,7 @@ public class jOOQTest {
}
@Test
public final void testPlainSQLField() throws Exception {
public void testPlainSQLField() throws Exception {
Field<?> f1 = field("DECODE(TABLE1.ID, 1, 'a', 'b')");
Field<?> f2 = field("DECODE(TABLE1.ID, 1, ?, ?)", "a", "b");
@ -794,7 +803,7 @@ public class jOOQTest {
}
@Test
public final void testCustomField() throws Exception {
public void testCustomField() throws Exception {
Field<?> f = new CustomField<Integer>("test", TestDataType.INTEGER_TYPE) {
private static final long serialVersionUID = 1L;
@ -827,7 +836,7 @@ public class jOOQTest {
}
@Test
public final void testIsNullCondition() throws Exception {
public void testIsNullCondition() throws Exception {
Condition c1 = FIELD_ID1.isNull();
assertEquals("\"TABLE1\".\"ID1\" is null", r_refI().render(c1));
assertEquals("\"TABLE1\".\"ID1\" is null", r_ref().render(c1));
@ -844,7 +853,7 @@ public class jOOQTest {
}
@Test
public final void testCaseValueFunction() throws Exception {
public void testCaseValueFunction() throws Exception {
Case decode = decode();
CaseValueStep<Integer> value = decode.value(FIELD_ID1);
CaseWhenStep<Integer, String> c = value.when(1, "one");
@ -883,7 +892,7 @@ public class jOOQTest {
}
@Test
public final void testCaseConditionFunction() throws Exception {
public void testCaseConditionFunction() throws Exception {
Case decode = decode();
CaseConditionStep<String> c = decode.when(FIELD_ID1.equal(1), "one");
@ -921,7 +930,7 @@ public class jOOQTest {
}
@Test
public final void testNullFunction() throws Exception {
public void testNullFunction() throws Exception {
Field<?> f = val((Object) null);
assertEquals("null", r_refI().render(f));
assertEquals("null", r_ref().render(f));
@ -931,7 +940,7 @@ public class jOOQTest {
}
@Test
public final void testConstantFunction() throws Exception {
public void testConstantFunction() throws Exception {
Field<Integer> f1 = val(Integer.valueOf(1));
assertEquals(Integer.class, f1.getType());
assertEquals("1", r_refI().render(f1));
@ -971,7 +980,7 @@ public class jOOQTest {
}
@Test
public final void testArithmeticSumExpressions() throws Exception {
public void testArithmeticSumExpressions() throws Exception {
Field<Integer> sum1 = FIELD_ID1.add(FIELD_ID1).add(1).add(2);
assertEquals(Integer.class, sum1.getType());
assertEquals("(\"TABLE1\".\"ID1\" + \"TABLE1\".\"ID1\" + 1 + 2)", r_refI().render(sum1));
@ -999,7 +1008,7 @@ public class jOOQTest {
}
@Test
public final void testArithmeticDifferenceExpressions() throws Exception {
public void testArithmeticDifferenceExpressions() throws Exception {
Field<Integer> difference1 = FIELD_ID1.sub(FIELD_ID1).sub(1).sub(2);
assertEquals(Integer.class, difference1.getType());
assertEquals("(((\"TABLE1\".\"ID1\" - \"TABLE1\".\"ID1\") - 1) - 2)", r_refI().render(difference1));
@ -1027,7 +1036,7 @@ public class jOOQTest {
}
@Test
public final void testArithmeticProductExpressions() throws Exception {
public void testArithmeticProductExpressions() throws Exception {
Field<Integer> product1 = FIELD_ID1.mul(FIELD_ID1).mul(1).mul(2);
assertEquals(Integer.class, product1.getType());
assertEquals("(\"TABLE1\".\"ID1\" * \"TABLE1\".\"ID1\" * 1 * 2)", r_refI().render(product1));
@ -1055,7 +1064,7 @@ public class jOOQTest {
}
@Test
public final void testArithmeticDivisionExpressions() throws Exception {
public void testArithmeticDivisionExpressions() throws Exception {
Field<Integer> division1 = FIELD_ID1.div(FIELD_ID1).div(1).div(2);
assertEquals(Integer.class, division1.getType());
assertEquals("(((\"TABLE1\".\"ID1\" / \"TABLE1\".\"ID1\") / 1) / 2)", r_refI().render(division1));
@ -1083,14 +1092,14 @@ public class jOOQTest {
}
@Test
public final void testFunctions() {
public void testFunctions() {
Field<String> f = replace(FIELD_NAME1, "a", "b");
assertEquals("replace(\"TABLE1\".\"NAME1\", 'a', 'b')", r_refI().render(f));
assertEquals("replace(\"TABLE1\".\"NAME1\", ?, ?)", r_ref().render(f));
}
@Test
public final void testArithmeticExpressions() {
public void testArithmeticExpressions() {
Field<? extends Number> f;
f = FIELD_ID1.add(1).sub(2).add(3);
@ -1119,7 +1128,7 @@ public class jOOQTest {
}
@Test
public final void testArithmeticFunctions() throws Exception {
public void testArithmeticFunctions() throws Exception {
Field<BigDecimal> sum1 = sum(FIELD_ID1);
assertEquals(BigDecimal.class, sum1.getType());
assertEquals("sum(\"TABLE1\".\"ID1\")", r_refI().render(sum1));
@ -1234,7 +1243,7 @@ public class jOOQTest {
}
@Test
public final void testInsertQuery1() throws Exception {
public void testInsertQuery1() throws Exception {
InsertQuery<Table1Record> q = create.insertQuery(TABLE1);
q.addValue(FIELD_ID1, 10);
@ -1254,7 +1263,7 @@ public class jOOQTest {
}
@Test
public final void testInsertQuery2() throws Exception {
public void testInsertQuery2() throws Exception {
InsertQuery<Table1Record> q = create.insertQuery(TABLE1);
q.addValue(FIELD_ID1, 10);
@ -1278,7 +1287,7 @@ public class jOOQTest {
}
@Test
public final void testInsertSelect1() throws Exception {
public void testInsertSelect1() throws Exception {
InsertQuery<Table1Record> q = create.insertQuery(TABLE1);
q.addValue(FIELD_ID1, round(val(10)));
@ -1298,7 +1307,7 @@ public class jOOQTest {
}
@Test
public final void testInsertSelect2() throws Exception {
public void testInsertSelect2() throws Exception {
Insert<Table1Record> q = create.insertInto(TABLE1, create.selectQuery());
assertEquals("insert into \"TABLE1\" (\"ID1\", \"NAME1\", \"DATE1\") select 1 from dual", r_refI().render(q));
@ -1321,7 +1330,7 @@ public class jOOQTest {
}
@Test
public final void testUpdateQuery1() throws Exception {
public void testUpdateQuery1() throws Exception {
UpdateQuery<Table1Record> q = create.updateQuery(TABLE1);
q.addValue(FIELD_ID1, 10);
@ -1340,7 +1349,7 @@ public class jOOQTest {
}
@Test
public final void testUpdateQuery2() throws Exception {
public void testUpdateQuery2() throws Exception {
UpdateQuery<Table1Record> q = create.updateQuery(TABLE1);
q.addValue(FIELD_ID1, 10);
@ -1361,7 +1370,7 @@ public class jOOQTest {
}
@Test
public final void testUpdateQuery3() throws Exception {
public void testUpdateQuery3() throws Exception {
UpdateQuery<Table1Record> q = create.updateQuery(TABLE1);
Condition c = FIELD_ID1.equal(10);
@ -1385,7 +1394,7 @@ public class jOOQTest {
}
@Test
public final void testUpdateQuery4() throws Exception {
public void testUpdateQuery4() throws Exception {
UpdateQuery<Table1Record> q = create.updateQuery(TABLE1);
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID1.equal(20);
@ -1412,7 +1421,7 @@ public class jOOQTest {
}
@Test
public final void testUpdateQuery5() throws Exception {
public void testUpdateQuery5() throws Exception {
UpdateQuery<Table1Record> q = create.updateQuery(TABLE1);
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID1.equal(20);
@ -1442,7 +1451,7 @@ public class jOOQTest {
}
@Test
public final void testMergeQuery() throws Exception {
public void testMergeQuery() throws Exception {
Merge<Table1Record> q =
create.mergeInto(TABLE1)
.using(create.select(FIELD_ID2).from(TABLE2))
@ -1475,7 +1484,7 @@ public class jOOQTest {
}
@Test
public final void testDeleteQuery1() throws Exception {
public void testDeleteQuery1() throws Exception {
DeleteQuery<Table1Record> q = create.deleteQuery(TABLE1);
assertEquals("delete from \"TABLE1\"", r_refI().render(q));
@ -1484,7 +1493,7 @@ public class jOOQTest {
}
@Test
public final void testDeleteQuery2() throws Exception {
public void testDeleteQuery2() throws Exception {
DeleteQuery<Table1Record> q = create.deleteQuery(TABLE1);
q.addConditions(falseCondition());
@ -1494,7 +1503,7 @@ public class jOOQTest {
}
@Test
public final void testDeleteQuery3() throws Exception {
public void testDeleteQuery3() throws Exception {
DeleteQuery<Table1Record> q = create.deleteQuery(TABLE1);
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID1.equal(20);
@ -1517,7 +1526,7 @@ public class jOOQTest {
}
@Test
public final void testDeleteQuery4() throws Exception {
public void testDeleteQuery4() throws Exception {
DeleteQuery<Table1Record> q = create.deleteQuery(TABLE1);
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID1.equal(20);
@ -1543,7 +1552,7 @@ public class jOOQTest {
}
@Test
public final void testConditionalSelectQuery1() throws Exception {
public void testConditionalSelectQuery1() throws Exception {
Select<?> q = create.selectQuery();
Select<?> s = create.select();
@ -1553,7 +1562,7 @@ public class jOOQTest {
}
@Test
public final void testConditionalSelectQuery2() throws Exception {
public void testConditionalSelectQuery2() throws Exception {
SelectQuery q = create.selectQuery();
q.addConditions(falseCondition());
@ -1563,7 +1572,7 @@ public class jOOQTest {
}
@Test
public final void testConditionalSelectQuery3() throws Exception {
public void testConditionalSelectQuery3() throws Exception {
SelectQuery q = create.selectQuery();
q.addConditions(falseCondition());
@ -1574,7 +1583,7 @@ public class jOOQTest {
}
@Test
public final void testConditionalSelectQuery4() throws Exception {
public void testConditionalSelectQuery4() throws Exception {
SelectQuery q = create.selectQuery();
Condition c1 = FIELD_ID1.equal(10);
Condition c2 = FIELD_ID1.equal(20);
@ -1600,7 +1609,7 @@ public class jOOQTest {
}
@Test
public final void testConditionalSelectQuery5() throws Exception {
public void testConditionalSelectQuery5() throws Exception {
SelectQuery q = create.selectQuery();
Condition c1 = condition("\"TABLE1\".\"ID1\" = ?", "10");
Condition c2 = condition("\"TABLE2\".\"ID2\" = 20 or \"TABLE2\".\"ID2\" = ?", 30);
@ -1623,7 +1632,7 @@ public class jOOQTest {
}
@Test
public final void testDistinctSelectQuery() throws Exception {
public void testDistinctSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addSelect(FIELD_ID1, FIELD_ID2);
q.setDistinct(true);
@ -1637,7 +1646,7 @@ public class jOOQTest {
}
@Test
public final void testProductSelectQuery() throws Exception {
public void testProductSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
@ -1652,7 +1661,7 @@ public class jOOQTest {
}
@Test
public final void testJoinSelectQuery() throws Exception {
public void testJoinSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
@ -1666,7 +1675,7 @@ public class jOOQTest {
}
@Test
public final void testJoinOnConditionSelectQuery() throws Exception {
public void testJoinOnConditionSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
q.addJoin(TABLE2, FIELD_ID1.equal(FIELD_ID2));
@ -1687,7 +1696,7 @@ public class jOOQTest {
}
@Test
public final void testJoinComplexSelectQuery() throws Exception {
public void testJoinComplexSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
@ -1741,7 +1750,7 @@ public class jOOQTest {
}
@Test
public final void testJoinSelf() throws Exception {
public void testJoinSelf() throws Exception {
Table<Table1Record> t1 = TABLE1.as("t1");
Table<Table1Record> t2 = TABLE1.as("t2");
@ -1760,7 +1769,7 @@ public class jOOQTest {
}
@Test
public final void testJoinTypeSelectQuery() throws Exception {
public void testJoinTypeSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
q.addJoin(TABLE2, LEFT_OUTER_JOIN, FIELD_ID1.equal(FIELD_ID2));
@ -1773,7 +1782,7 @@ public class jOOQTest {
}
@Test
public final void testGroupSelectQuery() throws Exception {
public void testGroupSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
@ -1837,7 +1846,7 @@ public class jOOQTest {
}
@Test
public final void testOrderSelectQuery() throws Exception {
public void testOrderSelectQuery() throws Exception {
SimpleSelectQuery<Table1Record> q = create.selectQuery(TABLE1);
q.addOrderBy(FIELD_ID1);
@ -1857,7 +1866,7 @@ public class jOOQTest {
}
@Test
public final void testCompleteSelectQuery() throws Exception {
public void testCompleteSelectQuery() throws Exception {
SelectQuery q = create.selectQuery();
q.addFrom(TABLE1);
q.addJoin(TABLE2, FIELD_ID1.equal(FIELD_ID2));
@ -1889,7 +1898,7 @@ public class jOOQTest {
}
@Test
public final void testCombinedSelectQuery() throws Exception {
public void testCombinedSelectQuery() throws Exception {
Select<?> combine = createCombinedSelectQuery();
assertEquals("(select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where \"TABLE1\".\"ID1\" = 1) union (select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where \"TABLE1\".\"ID1\" = 2)", r_refI().render(combine));
@ -1949,7 +1958,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect1() throws Exception {
public void testInnerSelect1() throws Exception {
SimpleSelectQuery<Table1Record> q1 = create.selectQuery(TABLE1);
SimpleSelectQuery<Table1Record> q2 = create.selectQuery(q1.asTable().as("inner_temp_table"));
SimpleSelectQuery<Table1Record> q3 = create.selectQuery(q2.asTable().as("outer_temp_table"));
@ -1962,7 +1971,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect2() throws Exception {
public void testInnerSelect2() throws Exception {
SelectQuery q1 = create.selectQuery();
SelectQuery q2 = create.selectQuery();
@ -1978,7 +1987,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect3() throws Exception {
public void testInnerSelect3() throws Exception {
SelectQuery q1 = create.selectQuery();
SelectQuery q2 = create.selectQuery();
@ -1993,7 +2002,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect4() throws Exception {
public void testInnerSelect4() throws Exception {
SelectQuery q1 = create.selectQuery();
SelectQuery q2 = create.selectQuery();
@ -2008,7 +2017,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect5() throws Exception {
public void testInnerSelect5() throws Exception {
SelectQuery q1 = create.selectQuery();
SelectQuery q2 = create.selectQuery();
@ -2023,7 +2032,7 @@ public class jOOQTest {
}
@Test
public final void testInnerSelect6() throws Exception {
public void testInnerSelect6() throws Exception {
SelectQuery q1 = create.selectQuery();
SelectQuery q2 = create.selectQuery();
@ -2036,4 +2045,98 @@ public class jOOQTest {
assertEquals("select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where exists (select \"TABLE2\".\"ID2\" from \"TABLE2\")", r_refI().render(q1));
assertEquals("select \"TABLE1\".\"ID1\", \"TABLE1\".\"NAME1\", \"TABLE1\".\"DATE1\" from \"TABLE1\" where exists (select \"TABLE2\".\"ID2\" from \"TABLE2\")", r_ref().render(q1));
}
@Test
public void testNamedParams() throws Exception {
Query q1 = create.select(val(1)).from(TABLE1).where(FIELD_ID1.equal(val(2)));
Query q2 = create.select(param("p1", 1)).from(TABLE1).where(FIELD_ID1.equal(param("p2", 2)));
Query q3 = create.select(param("p1", 1)).from(TABLE1).where(FIELD_ID1.equal(2));
Query q4 = create.select(val(1)).from(TABLE1).where(FIELD_ID1.equal(param("p2", 2)));
assertEquals("select 1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 2", r_refI().render(q1));
assertEquals("select :1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :2", r_refP().render(q1));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q1));
assertEquals("select 1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 2", r_refI().render(q2));
assertEquals("select :p1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :p2", r_refP().render(q2));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q2));
assertEquals("select 1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 2", r_refI().render(q3));
assertEquals("select :p1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :2", r_refP().render(q3));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q3));
assertEquals("select 1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 2", r_refI().render(q4));
assertEquals("select :1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :p2", r_refP().render(q4));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q4));
// Param / Val queries should be equal as toString() doesn't consider params
assertEquals(q1, q2);
assertEquals(q1, q3);
assertEquals(q1, q4);
// Params
Param<?> p11 = q1.getParam("1");
Param<?> p21 = q2.getParam("p1");
Param<?> p31 = q3.getParam("p1");
Param<?> p41 = q4.getParam("1");
Param<?> p12 = q1.getParam("2");
Param<?> p22 = q2.getParam("p2");
Param<?> p32 = q3.getParam("2");
Param<?> p42 = q4.getParam("p2");
assertEquals(Arrays.asList("1", "2"), new ArrayList<String>(q1.getParams().keySet()));
assertEquals(Arrays.asList("p1", "p2"), new ArrayList<String>(q2.getParams().keySet()));
assertEquals(Arrays.asList("p1", "2"), new ArrayList<String>(q3.getParams().keySet()));
assertEquals(Arrays.asList("1", "p2"), new ArrayList<String>(q4.getParams().keySet()));
// Types
assertEquals(Integer.class, p11.getType());
assertEquals(Integer.class, p21.getType());
assertEquals(Integer.class, p31.getType());
assertEquals(Integer.class, p41.getType());
assertEquals(Integer.class, p12.getType());
assertEquals(Integer.class, p22.getType());
assertEquals(Integer.class, p32.getType());
assertEquals(Integer.class, p42.getType());
// Values
assertEquals(Integer.valueOf(1), p11.getValue());
assertEquals(Integer.valueOf(1), p21.getValue());
assertEquals(Integer.valueOf(1), p31.getValue());
assertEquals(Integer.valueOf(1), p41.getValue());
assertEquals(Integer.valueOf(2), p12.getValue());
assertEquals(Integer.valueOf(2), p22.getValue());
assertEquals(Integer.valueOf(2), p32.getValue());
assertEquals(Integer.valueOf(2), p42.getValue());
// Param replacement
p11.setConverted(3);
p21.setConverted(3);
p31.setConverted(3);
p41.setConverted(3);
p12.setConverted(4);
p22.setConverted(4);
p32.setConverted(4);
p42.setConverted(4);
assertEquals("select 3 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 4", r_refI().render(q1));
assertEquals("select :1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :2", r_refP().render(q1));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q1));
assertEquals("select 3 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 4", r_refI().render(q2));
assertEquals("select :p1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :p2", r_refP().render(q2));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q2));
assertEquals("select 3 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 4", r_refI().render(q3));
assertEquals("select :p1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :2", r_refP().render(q3));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q3));
assertEquals("select 3 from \"TABLE1\" where \"TABLE1\".\"ID1\" = 4", r_refI().render(q4));
assertEquals("select :1 from \"TABLE1\" where \"TABLE1\".\"ID1\" = :p2", r_refP().render(q4));
assertEquals("select ? from \"TABLE1\" where \"TABLE1\".\"ID1\" = ?", r_ref().render(q4));
}
}