[#2001] Named Params are treated as null literals on right sides of

comparisons
This commit is contained in:
Lukas Eder 2012-12-17 20:49:42 +01:00
parent 56d1dd1250
commit 0977fccd6d
25 changed files with 81 additions and 150 deletions

View File

@ -364,9 +364,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T725,
for (Field<Integer> n : asList(n1, n2)) {
assertEquals(null, create().select(n).fetchOne(n));
assertEquals(Integer.valueOf(1), create().select(c).from(TAuthor()).where(TAuthor_ID().equal(1)).and(n.isNull()).fetchOne(c));
assertEquals(Integer.valueOf(1), create().select(c).from(TAuthor()).where(TAuthor_ID().equal(1)).and(n.equal(n)).fetchOne(c));
assertEquals(null, create().selectOne().from(TAuthor()).where(n.isNotNull()).fetchAny());
assertEquals(null, create().selectOne().from(TAuthor()).where(n.notEqual(n)).fetchAny());
}
UpdateQuery<A> u = create().updateQuery(TAuthor());

View File

@ -9164,6 +9164,7 @@ for (Record record : create().select(
<h3>GroupField</h3>
<h3>? extends T has been relaxed, e.g. for casting, Field.getType(), etc.</h3>
<h3>Ant task removed</h3>
<h3>#2001 eq/equal(null) and ne/notEqual(null) now work as in SQL</h3>
<h3>Object renames</h3>
<ul>

View File

@ -124,17 +124,6 @@ public interface Field<T> extends GroupField {
@Override
boolean equals(Object other);
/**
* Whether this field represents a <code>null</code> literal.
* <p>
* This method is for JOOQ INTERNAL USE only!
* <p>
* This method was added to be able to recognise <code>null</code> literals
* within jOOQ and handle them specially, as some SQL dialects have a rather
* un-intuitive way of handling <code>null</code> values.
*/
boolean isNullLiteral();
// ------------------------------------------------------------------------
// Type casts
// ------------------------------------------------------------------------
@ -1284,10 +1273,6 @@ public interface Field<T> extends GroupField {
/**
* <code>this = value</code>
* <p>
* If <code>value == null</code>, then this will return a condition
* equivalent to {@link #isNull()} for convenience. SQL's ternary
* <code>NULL</code> logic is rarely of use for Java programmers.
*/
@Support
Condition equal(T value);
@ -1319,10 +1304,6 @@ public interface Field<T> extends GroupField {
/**
* <code>this = value</code>
* <p>
* If <code>value == null</code>, then this will return a condition
* equivalent to {@link #isNull()} for convenience. SQL's ternary
* <code>NULL</code> logic is rarely of use for Java programmers.
*
* @see #equal(Object)
*/
@ -1360,10 +1341,6 @@ public interface Field<T> extends GroupField {
/**
* <code>this != value</code>
* <p>
* If <code>value == null</code>, then this will return a condition
* equivalent to {@link #isNotNull()} for convenience. SQL's ternary
* <code>NULL</code> logic is rarely of use for Java programmers.
*/
@Support
Condition notEqual(T value);
@ -1395,10 +1372,6 @@ public interface Field<T> extends GroupField {
/**
* <code>this != value</code>
* <p>
* If <code>value == null</code>, then this will return a condition
* equivalent to {@link #isNotNull()} for convenience. SQL's ternary
* <code>NULL</code> logic is rarely of use for Java programmers.
*
* @see #notEqual(Object)
*/

View File

@ -106,9 +106,6 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
@Override
public abstract void bind(BindContext context);
@Override
public abstract boolean isNullLiteral();
// ------------------------------------------------------------------------
// XXX: API
// ------------------------------------------------------------------------
@ -313,12 +310,12 @@ abstract class AbstractField<T> extends AbstractQueryPart implements Field<T> {
@Override
public final Condition isNull() {
return equal((T) null);
return new IsNull(this, true);
}
@Override
public final Condition isNotNull() {
return notEqual((T) null);
return new IsNull(this, false);
}
@Override

View File

@ -72,11 +72,6 @@ abstract class AbstractFunction<T> extends AbstractField<T> {
context.bind(getFunction(context));
}
@Override
public final boolean isNullLiteral() {
return false;
}
final QueryPart getFunction(Configuration configuration) {
return getFunction0(configuration);
}

View File

@ -80,9 +80,4 @@ class ArrayConstant<T> extends AbstractField<T> {
public final void bind(BindContext context) {
context.bindValues(array);
}
@Override
public final boolean isNullLiteral() {
return array == null;
}
}

View File

@ -139,9 +139,4 @@ class CaseConditionStepImpl<T> extends AbstractField<T> implements CaseCondition
context.keyword("end")
.formatIndentLockEnd();
}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -198,9 +198,4 @@ class CaseWhenStepImpl<V, T> extends AbstractField<T> implements CaseWhenStep<V,
context.keyword("end")
.formatIndentLockEnd();
}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -187,9 +187,4 @@ class Cast<T> extends AbstractField<T> {
context.bind(field);
}
@Override
public final boolean isNullLiteral() {
return field.isNullLiteral();
}
}

View File

@ -37,10 +37,8 @@
package org.jooq.impl;
import static java.util.Arrays.asList;
import static org.jooq.Comparator.EQUALS;
import static org.jooq.Comparator.LIKE;
import static org.jooq.Comparator.LIKE_IGNORE_CASE;
import static org.jooq.Comparator.NOT_EQUALS;
import static org.jooq.Comparator.NOT_LIKE;
import static org.jooq.Comparator.NOT_LIKE_IGNORE_CASE;
import static org.jooq.SQLDialect.ASE;
@ -79,12 +77,7 @@ class CompareCondition extends AbstractCondition {
@Override
public final void bind(BindContext context) {
context.bind(field1);
// [#1084] Bind field2 only if it is actually rendered
if (!field2.isNullLiteral() || !asList(EQUALS, NOT_EQUALS).contains(comparator)) {
context.bind(field2);
}
context.bind(field1).bind(field2);
}
@Override
@ -117,18 +110,6 @@ class CompareCondition extends AbstractCondition {
context.sql(lhs)
.sql(" ");
if (rhs.isNullLiteral()) {
switch (op) {
case EQUALS:
context.keyword("is null");
return;
case NOT_EQUALS:
context.keyword("is not null");
return;
}
}
// [#1131] Some weird DB2 issue stops "LIKE" from working with a
// concatenated search expression, if the expression is more than 4000
// characters long

View File

@ -91,16 +91,6 @@ public abstract class CustomField<T> extends AbstractField<T> {
// Further overrides allowed
// -------------------------------------------------------------------------
/**
* Subclasses may further override this method
* <hr/>
* {@inheritDoc}
*/
@Override
public boolean isNullLiteral() {
return false;
}
// -------------------------------------------------------------------------
// No further overrides allowed
// -------------------------------------------------------------------------

View File

@ -79,13 +79,13 @@ class Decode<T, Z> extends AbstractFunction<Z> {
default: {
CaseConditionStep<Z> when = Factory
.decode()
.when(field.equal(search), result);
.when(field.isNotDistinctFrom(search), result);
for (int i = 0; i < more.length; i += 2) {
// search/result pair
if (i + 1 < more.length) {
when = when.when(field.equal((Field<T>) more[i]), (Field<Z>) more[i + 1]);
when = when.when(field.isNotDistinctFrom((Field<T>) more[i]), (Field<Z>) more[i + 1]);
}
// trailing default value

View File

@ -536,10 +536,5 @@ class Expression<T> extends AbstractFunction<T> {
public final void bind(BindContext context) {
context.bind(lhs).bind((QueryPart) rhs);
}
@Override
public final boolean isNullLiteral() {
return false;
}
}
}

View File

@ -3113,15 +3113,19 @@ public class Factory {
* Returns the dialect's equivalent to DECODE:
* <ul>
* <li>Oracle <a
* href="http://www.techonthenet.com/oracle/functions/decode.php">DECODE</a></li>
* href="http://www.techonthenet.com/oracle/functions/decode.php">DECODE</a>
* </li>
* </ul>
* <p>
* Other dialects: <code><pre>
* CASE WHEN [this = search] THEN [result],
* [WHEN more... THEN more...]
* CASE WHEN [this IS NOT DISTINCT FROM search] THEN [result],
* [WHEN more... THEN more...]
* [ELSE more...]
* END
* </pre></code>
* <p>
* Note the use of the <code>DISTINCT</code> predicate to produce the same,
* conveniently <code>NULL</code>-agnostic behaviour as Oracle.
*
* @param value The value to decode
* @param search the mandatory first search parameter
@ -3129,7 +3133,7 @@ public class Factory {
* @param more the optional parameters. If <code>more.length</code> is even,
* then it is assumed that it contains more search/result pairs.
* If <code>more.length</code> is odd, then it is assumed that it
* contains more search/result pairs plus a default at the end. *
* contains more search/result pairs plus a default at the end.
*/
@Support
public static <Z, T> Field<Z> decode(Field<T> value, Field<T> search, Field<Z> result, Field<?>... more) {

View File

@ -70,11 +70,6 @@ class FieldAlias<T> extends AbstractField<T> {
return alias.wrapped().as(as);
}
@Override
public final boolean isNullLiteral() {
return alias.wrapped().isNullLiteral();
}
@Override
public final boolean declaresFields() {
return true;

View File

@ -438,11 +438,6 @@ class Function<T> extends AbstractField<T> implements
}
}
@Override
public final boolean isNullLiteral() {
return false;
}
// -------------------------------------------------------------------------
// XXX aggregate and window function fluent API methods
// -------------------------------------------------------------------------

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.impl;
import org.jooq.BindContext;
import org.jooq.Field;
import org.jooq.RenderContext;
/**
* @author Lukas Eder
*/
class IsNull extends AbstractCondition {
private static final long serialVersionUID = -747240442279619486L;
private final Field<?> field;
private final boolean isNull;
IsNull(Field<?> field, boolean isNull) {
this.field = field;
this.isNull = isNull;
}
@Override
public final void bind(BindContext context) {
context.bind(field);
}
@Override
public final void toSQL(RenderContext context) {
context.sql(field).keyword(isNull ? " is null" : " is not null");
}
}

View File

@ -100,9 +100,4 @@ class Neg<T> extends AbstractField<T> {
public final void bind(BindContext context) {
context.bind(field);
}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -78,9 +78,4 @@ class QualifiedField<T> extends AbstractField<T> {
@Override
public final void bind(BindContext context) {}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -72,9 +72,4 @@ class SQLField<T> extends AbstractField<T> {
public final void bind(BindContext context) {
Utils.renderAndBind(null, context, sql, substitutes);
}
@Override
public final boolean isNullLiteral() {
return "null".equalsIgnoreCase(("" + sql).trim());
}
}

View File

@ -101,9 +101,4 @@ class SelectQueryAsField<T> extends AbstractField<T> {
.sql(")");
}
}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -82,9 +82,4 @@ class TableFieldImpl<R extends Record, T> extends AbstractField<T> implements Ta
@Override
public final void bind(BindContext context) {}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -175,9 +175,4 @@ class UDTConstant<R extends UDTRecord<R>> extends AbstractField<R> {
throw new SQLDialectNotSupportedException("UDTs not supported in dialect " + context.getDialect());
}
}
@Override
public final boolean isNullLiteral() {
return record == null;
}
}

View File

@ -77,9 +77,4 @@ class UDTFieldImpl<R extends UDTRecord<R>, T> extends AbstractField<T> implement
@Override
public final void bind(BindContext context) {}
@Override
public final boolean isNullLiteral() {
return false;
}
}

View File

@ -541,11 +541,6 @@ class Val<T> extends AbstractField<T> implements Param<T> {
}
}
@Override
public final boolean isNullLiteral() {
return getValue() == null;
}
// ------------------------------------------------------------------------
// XXX: Param API
// ------------------------------------------------------------------------