From 153d161e703d15c9f2d6e82a4fdd24ba33f75337 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Fri, 22 Jan 2016 18:59:49 +0100 Subject: [PATCH] [#4920] Generated functions without parameter names produce malformed SQL --- .../java/org/jooq/util/JavaGenerator.java | 10 +- .../jooq/util/DefaultParameterDefinition.java | 13 ++- .../org/jooq/util/ParameterDefinition.java | 7 ++ .../postgres/PostgresRoutineDefinition.java | 4 +- jOOQ/src/main/java/org/jooq/Parameter.java | 9 ++ .../java/org/jooq/impl/AbstractRoutine.java | 106 ++++++++++++++++-- .../java/org/jooq/impl/ParameterImpl.java | 19 +++- 7 files changed, 149 insertions(+), 19 deletions(-) diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java index b80cd1f3e3..f31ecab573 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java @@ -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 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 %s.%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 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 %s.%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); } } diff --git a/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java index a27eb78012..4ed680b579 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java @@ -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; + } } diff --git a/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java index e4ade5d6b2..7543219a7e 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java @@ -55,4 +55,11 @@ public interface ParameterDefinition extends TypedElementDefinition + * @see Parameter#isUnnamed() + */ + boolean isUnnamed(); } diff --git a/jOOQ-meta/src/main/java/org/jooq/util/postgres/PostgresRoutineDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/postgres/PostgresRoutineDefinition.java index 7daa96a702..488e69d2a7 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/postgres/PostgresRoutineDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/postgres/PostgresRoutineDefinition.java @@ -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); diff --git a/jOOQ/src/main/java/org/jooq/Parameter.java b/jOOQ/src/main/java/org/jooq/Parameter.java index c6f34dbdde..b1386a259c 100644 --- a/jOOQ/src/main/java/org/jooq/Parameter.java +++ b/jOOQ/src/main/java/org/jooq/Parameter.java @@ -114,4 +114,13 @@ public interface Parameter extends QueryPart { * Currently, this is only supported for Oracle 11g */ boolean isDefaulted(); + + /** + * Whether this parameter has a name or not. + *

+ * 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(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 147e80bf72..e5e29f2f90 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -122,6 +122,7 @@ public abstract class AbstractRoutine 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 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 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 Parameter createParameter(String name, DataType type) { return createParameter(name, type, false, null, null); } @@ -824,7 +833,10 @@ public abstract class AbstractRoutine 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 Parameter createParameter(String name, DataType type, boolean isDefaulted) { return createParameter(name, type, isDefaulted, null, null); } @@ -837,7 +849,10 @@ public abstract class AbstractRoutine 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, Converter converter) { return createParameter(name, type, isDefaulted, converter, null); } @@ -850,7 +865,10 @@ public abstract class AbstractRoutine 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, Binding binding) { return createParameter(name, type, isDefaulted, null, binding); } @@ -863,15 +881,78 @@ public abstract class AbstractRoutine 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, Converter converter, Binding 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 Parameter createParameter(String name, DataType 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, boolean isUnnamed, Converter 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, boolean isUnnamed, Binding 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 Parameter createParameter(String name, DataType type, boolean isDefaulted, Converter converter, Binding binding) { + protected static final Parameter createParameter(String name, DataType type, boolean isDefaulted, boolean isUnnamed, Converter converter, Binding binding) { final Binding actualBinding = DefaultBinding.newBinding(converter, type, binding); final DataType actualType = converter == null && binding == null ? (DataType) type : type.asConvertedDataType(actualBinding); - return new ParameterImpl(name, actualType, isDefaulted, actualBinding); + return new ParameterImpl(name, actualType, actualBinding, isDefaulted, isUnnamed); } /** @@ -909,11 +990,22 @@ public abstract class AbstractRoutine 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)); } diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index 8705105b49..871c048ab5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -57,17 +57,19 @@ import org.jooq.tools.StringUtils; */ class ParameterImpl extends AbstractQueryPart implements Parameter { - private static final long serialVersionUID = -5277225593751085577L; + private static final long serialVersionUID = -5277225593751085577L; - private final String name; - private final DataType type; - private final boolean isDefaulted; + private final String name; + private final DataType type; + private final boolean isDefaulted; + private final boolean isUnnamed; @SuppressWarnings("unchecked") - ParameterImpl(String name, DataType type, boolean isDefaulted, Binding binding) { + ParameterImpl(String name, DataType type, Binding binding, boolean isDefaulted, boolean isUnnamed) { this.name = name; - this.isDefaulted = isDefaulted; this.type = type.asConvertedDataType((Binding) binding); + this.isDefaulted = isDefaulted; + this.isUnnamed = isUnnamed; } @Override @@ -115,6 +117,11 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { return isDefaulted; } + @Override + public final boolean isUnnamed() { + return isUnnamed; + } + // ------------------------------------------------------------------------ // XXX: Object API // ------------------------------------------------------------------------