[#4920] Generated functions without parameter names produce malformed SQL

This commit is contained in:
lukaseder 2016-01-22 18:59:49 +01:00
parent 65db390ee0
commit 153d161e70
7 changed files with 149 additions and 19 deletions

View File

@ -3886,6 +3886,7 @@ public class JavaGenerator extends AbstractGenerator {
final String paramName = parameter.getName();
final String paramComment = StringUtils.defaultString(parameter.getComment());
final String isDefaulted = parameter.isDefaulted() ? "true" : "false";
final String isUnnamed = parameter.isUnnamed() ? "true" : "false";
final List<String> converters = out.ref(list(
parameter.getType().getConverter(),
parameter.getType().getBinding()
@ -3893,8 +3894,8 @@ public class JavaGenerator extends AbstractGenerator {
out.tab(1).javadoc("The parameter <code>%s</code>.%s", parameter.getQualifiedOutputName(), defaultIfBlank(" " + paramComment, ""));
out.tab(1).println("val %s : %s[%s] = %s.createParameter(\"%s\", %s, %s[[before=, ][new %s]])",
paramId, Parameter.class, paramType, AbstractRoutine.class, paramName, paramTypeRef, isDefaulted, converters);
out.tab(1).println("val %s : %s[%s] = %s.createParameter(\"%s\", %s, %s, %s[[before=, ][new %s]])",
paramId, Parameter.class, paramType, AbstractRoutine.class, paramName, paramTypeRef, isDefaulted, isUnnamed, converters);
}
out.println("}");
@ -3920,6 +3921,7 @@ public class JavaGenerator extends AbstractGenerator {
final String paramName = parameter.getName();
final String paramComment = StringUtils.defaultString(parameter.getComment());
final String isDefaulted = parameter.isDefaulted() ? "true" : "false";
final String isUnnamed = parameter.isUnnamed() ? "true" : "false";
final List<String> converters = out.ref(list(
parameter.getType().getConverter(),
parameter.getType().getBinding()
@ -3927,8 +3929,8 @@ public class JavaGenerator extends AbstractGenerator {
out.tab(1).javadoc("The parameter <code>%s</code>.%s", parameter.getQualifiedOutputName(), defaultIfBlank(" " + paramComment, ""));
out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s[[before=, ][new %s()]]);",
Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted, converters);
out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s, %s[[before=, ][new %s()]]);",
Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted, isUnnamed, converters);
}
}

View File

@ -52,19 +52,30 @@ public class DefaultParameterDefinition
implements ParameterDefinition {
private final boolean isDefaulted;
private final boolean isUnnamed;
public DefaultParameterDefinition(RoutineDefinition routine, String name, int position, DataTypeDefinition type) {
this(routine, name, position, type, false);
this(routine, name, position, type, false, false);
}
public DefaultParameterDefinition(RoutineDefinition routine, String name, int position, DataTypeDefinition type, boolean isDefaulted) {
this(routine, name, position, type, isDefaulted, false);
}
public DefaultParameterDefinition(RoutineDefinition routine, String name, int position, DataTypeDefinition type, boolean isDefaulted, boolean isUnnamed) {
super(routine, name, position, type, null);
this.isDefaulted = isDefaulted;
this.isUnnamed = isUnnamed;
}
@Override
public boolean isDefaulted() {
return isDefaulted;
}
@Override
public boolean isUnnamed() {
return isUnnamed;
}
}

View File

@ -55,4 +55,11 @@ public interface ParameterDefinition extends TypedElementDefinition<RoutineDefin
* @see Parameter#isDefaulted()
*/
boolean isDefaulted();
/**
* Whether the parameter has a name.
* <p>
* @see Parameter#isUnnamed()
*/
boolean isUnnamed();
}

View File

@ -52,6 +52,7 @@ import java.util.Arrays;
import org.jooq.Record;
import org.jooq.exception.DataAccessException;
import org.jooq.tools.StringUtils;
import org.jooq.util.AbstractRoutineDefinition;
import org.jooq.util.DataTypeDefinition;
import org.jooq.util.Database;
@ -152,7 +153,8 @@ public class PostgresRoutineDefinition extends AbstractRoutineDefinition {
record.getValue(PARAMETERS.PARAMETER_NAME),
record.getValue(PARAMETERS.ORDINAL_POSITION),
type,
record.getValue(PARAMETERS.PARAMETER_DEFAULT) != null
record.getValue(PARAMETERS.PARAMETER_DEFAULT) != null,
StringUtils.isBlank(record.getValue(PARAMETERS.PARAMETER_NAME))
);
addParameter(InOutDefinition.getFromString(inOut), parameter);

View File

@ -114,4 +114,13 @@ public interface Parameter<T> extends QueryPart {
* Currently, this is only supported for Oracle 11g
*/
boolean isDefaulted();
/**
* Whether this parameter has a name or not.
* <p>
* Some databases (e.g. {@link SQLDialect#POSTGRES}) allow for using unnamed
* parameters. In this case, {@link #getName()} will return a synthetic name
* created from the parameter index.
*/
boolean isUnnamed();
}

View File

@ -122,6 +122,7 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
private ResultsImpl results;
private boolean overloaded;
private boolean hasDefaultedParameters;
private boolean hasUnnamedParameters;
// ------------------------------------------------------------------------
// Call-data attributes (call-specific)
@ -736,9 +737,14 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
return hasDefaultedParameters && !inValuesDefaulted.isEmpty();
}
private final boolean hasUnnamedParameters() {
return hasUnnamedParameters;
}
private final void addParameter(Parameter<?> parameter) {
allParameters.add(parameter);
hasDefaultedParameters |= parameter.isDefaulted();
hasUnnamedParameters |= parameter.isUnnamed();
}
protected final void addInParameter(Parameter<?> parameter) {
@ -811,7 +817,10 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
*
* @param name The name of the field (case-sensitive!)
* @param type The data type of the field
*
* @deprecated - Please, re-generate your routine code.
*/
@Deprecated
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type) {
return createParameter(name, type, false, null, null);
}
@ -824,7 +833,10 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
*
* @deprecated - Please, re-generate your routine code.
*/
@Deprecated
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type, boolean isDefaulted) {
return createParameter(name, type, isDefaulted, null, null);
}
@ -837,7 +849,10 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
*
* @deprecated - Please, re-generate your routine code.
*/
@Deprecated
protected static final <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Converter<T, U> converter) {
return createParameter(name, type, isDefaulted, converter, null);
}
@ -850,7 +865,10 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
*
* @deprecated - Please, re-generate your routine code.
*/
@Deprecated
protected static final <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Binding<T, U> binding) {
return createParameter(name, type, isDefaulted, null, binding);
}
@ -863,15 +881,78 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
*
* @deprecated - Please, re-generate your routine code.
*/
@Deprecated
protected static final <T, X, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Converter<X, U> converter, Binding<T, X> binding) {
return createParameter(name, type, isDefaulted, false, converter, binding);
}
/**
* Subclasses may call this method to create {@link UDTField} objects that
* are linked to this table.
*
* @param name The name of the field (case-sensitive!)
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
* @param isUnnamed Whether the parameter is unnamed (see
* {@link Parameter#isUnnamed()}.
*/
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type, boolean isDefaulted, boolean isUnnamed) {
return createParameter(name, type, isDefaulted, isUnnamed, null, null);
}
/**
* Subclasses may call this method to create {@link UDTField} objects that
* are linked to this table.
*
* @param name The name of the field (case-sensitive!)
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
* @param isUnnamed Whether the parameter is unnamed (see
* {@link Parameter#isUnnamed()}.
*/
protected static final <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, boolean isUnnamed, Converter<T, U> converter) {
return createParameter(name, type, isDefaulted, isUnnamed, converter, null);
}
/**
* Subclasses may call this method to create {@link UDTField} objects that
* are linked to this table.
*
* @param name The name of the field (case-sensitive!)
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
* @param isUnnamed Whether the parameter is unnamed (see
* {@link Parameter#isUnnamed()}.
*/
protected static final <T, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, boolean isUnnamed, Binding<T, U> binding) {
return createParameter(name, type, isDefaulted, isUnnamed, null, binding);
}
/**
* Subclasses may call this method to create {@link UDTField} objects that
* are linked to this table.
*
* @param name The name of the field (case-sensitive!)
* @param type The data type of the field
* @param isDefaulted Whether the parameter is defaulted (see
* {@link Parameter#isDefaulted()}
* @param isUnnamed Whether the parameter is unnamed (see
* {@link Parameter#isUnnamed()}.
*/
@SuppressWarnings("unchecked")
protected static final <T, X, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, Converter<X, U> converter, Binding<T, X> binding) {
protected static final <T, X, U> Parameter<U> createParameter(String name, DataType<T> type, boolean isDefaulted, boolean isUnnamed, Converter<X, U> converter, Binding<T, X> binding) {
final Binding<T, U> actualBinding = DefaultBinding.newBinding(converter, type, binding);
final DataType<U> actualType = converter == null && binding == null
? (DataType<U>) type
: type.asConvertedDataType(actualBinding);
return new ParameterImpl<U>(name, actualType, isDefaulted, actualBinding);
return new ParameterImpl<U>(name, actualType, actualBinding, isDefaulted, isUnnamed);
}
/**
@ -909,11 +990,22 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
continue;
// Disambiguate overloaded function signatures
if (ctx.family() == POSTGRES)
if (isOverloaded())
fields.add(field("{0} := {1}", name(parameter.getName()), getInValues().get(parameter).cast(parameter.getType())));
else
fields.add(field("{0} := {1}", name(parameter.getName()), getInValues().get(parameter)));
if (ctx.family() == POSTGRES) {
// [#4920] In case there are any unnamed parameters, we mustn't
if (hasUnnamedParameters()) {
if (isOverloaded())
fields.add(getInValues().get(parameter).cast(parameter.getType()));
else
fields.add(getInValues().get(parameter));
}
else {
if (isOverloaded())
fields.add(field("{0} := {1}", name(parameter.getName()), getInValues().get(parameter).cast(parameter.getType())));
else
fields.add(field("{0} := {1}", name(parameter.getName()), getInValues().get(parameter)));
}
}
else
fields.add(getInValues().get(parameter));
}

View File

@ -57,17 +57,19 @@ import org.jooq.tools.StringUtils;
*/
class ParameterImpl<T> extends AbstractQueryPart implements Parameter<T> {
private static final long serialVersionUID = -5277225593751085577L;
private static final long serialVersionUID = -5277225593751085577L;
private final String name;
private final DataType<T> type;
private final boolean isDefaulted;
private final String name;
private final DataType<T> type;
private final boolean isDefaulted;
private final boolean isUnnamed;
@SuppressWarnings("unchecked")
ParameterImpl(String name, DataType<T> type, boolean isDefaulted, Binding<?, T> binding) {
ParameterImpl(String name, DataType<T> type, Binding<?, T> binding, boolean isDefaulted, boolean isUnnamed) {
this.name = name;
this.isDefaulted = isDefaulted;
this.type = type.asConvertedDataType((Binding<T, T>) binding);
this.isDefaulted = isDefaulted;
this.isUnnamed = isUnnamed;
}
@Override
@ -115,6 +117,11 @@ class ParameterImpl<T> extends AbstractQueryPart implements Parameter<T> {
return isDefaulted;
}
@Override
public final boolean isUnnamed() {
return isUnnamed;
}
// ------------------------------------------------------------------------
// XXX: Object API
// ------------------------------------------------------------------------