[#1183] Add support for DEFAULT values in Oracle stored procedure parameters

[#1252] Avoid JDBC escape syntax for stored procedure calls, where this is possible
This commit is contained in:
Lukas Eder 2012-03-23 11:53:55 +00:00
parent e823bb6a7f
commit 5c6a588eec
19 changed files with 670 additions and 21 deletions

View File

@ -2507,6 +2507,10 @@ public class DefaultGenerator implements Generator {
type instanceof TableDefinition ||
type instanceof UDTDefinition;
boolean isDefaulted =
column instanceof ParameterDefinition &&
((ParameterDefinition) column).isDefaulted();
if (type instanceof TableDefinition) {
if (generateInstanceFields()) {
out.print("\tpublic final ");
@ -2559,6 +2563,10 @@ public class DefaultGenerator implements Generator {
}
}
if (isDefaulted) {
out.print(", true");
}
out.println(");");
}

View File

@ -46,7 +46,20 @@ public class DefaultParameterDefinition
extends AbstractTypedElementDefinition<RoutineDefinition>
implements ParameterDefinition {
private final boolean isDefaulted;
public DefaultParameterDefinition(RoutineDefinition routine, String name, int position, DataTypeDefinition type) {
this(routine, name, position, type, false);
}
public DefaultParameterDefinition(RoutineDefinition routine, String name, int position, DataTypeDefinition type, boolean isDefaulted) {
super(routine, name, position, type, null);
this.isDefaulted = isDefaulted;
}
@Override
public boolean isDefaulted() {
return isDefaulted;
}
}

View File

@ -35,6 +35,8 @@
*/
package org.jooq.util;
import org.jooq.Parameter;
/**
* An interface defining a parameter of a stored procedure or stored function.
*
@ -42,4 +44,10 @@ package org.jooq.util;
*/
public interface ParameterDefinition extends TypedElementDefinition<RoutineDefinition> {
/**
* Whether the parameter has a default value.
* <p>
* @see Parameter#isDefaulted()
*/
boolean isDefaulted();
}

View File

@ -76,7 +76,8 @@ public class OracleRoutineDefinition extends AbstractRoutineDefinition {
ALL_ARGUMENTS.DATA_PRECISION,
ALL_ARGUMENTS.DATA_SCALE,
ALL_ARGUMENTS.TYPE_NAME,
ALL_ARGUMENTS.POSITION)
ALL_ARGUMENTS.POSITION,
ALL_ARGUMENTS.DEFAULTED)
.from(ALL_ARGUMENTS)
.where(ALL_ARGUMENTS.OWNER.equal(getSchema().getName()))
.and(ALL_ARGUMENTS.OBJECT_NAME.equal(getName()))
@ -116,7 +117,8 @@ public class OracleRoutineDefinition extends AbstractRoutineDefinition {
this,
name,
position,
type);
type,
record.getValue(ALL_ARGUMENTS.DEFAULTED, boolean.class));
addParameter(inOut, parameter);
}

View File

@ -62,6 +62,8 @@ import org.jooq.UpdatableRecord;
import org.jooq.UpdateQuery;
import org.jooq.test.BaseTest;
import org.jooq.test.jOOQAbstractTest;
import org.jooq.tools.reflect.Reflect;
import org.jooq.tools.reflect.ReflectException;
import org.junit.Test;
@ -220,6 +222,43 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
}
}
@Test
public void testStoredProcedureWithDefaultParameters() {
if (cRoutines() == null) {
log.info("SKIPPING", "procedure tests with default parameters");
return;
}
Reflect pdefault;
try {
pdefault = Reflect.on(cRoutines().getPackage().getName() + ".routines.PDefault");
if (!pdefault.field("P_IN_NUMBER").call("isDefaulted").<Boolean>get()) {
log.info("SKIPPING", "procedure tests with default parameters");
return;
}
}
catch (ReflectException e) {
log.info("SKIPPING", "procedure tests with default parameters");
return;
}
Reflect executedWithDefaults = pdefault.create();
executedWithDefaults.call("execute", create());
assertEquals(0, executedWithDefaults.call("getPOutNumber").<Number>get().intValue());
assertEquals("0", executedWithDefaults.call("getPOutVarchar").get());
assertEquals(Date.valueOf("1981-07-10"), executedWithDefaults.call("getPOutDate").get());
Reflect executedWithoutDefault = pdefault.create();
executedWithoutDefault.call("setPInNumber", 123);
executedWithoutDefault.call("setPInVarchar", "abc");
executedWithoutDefault.call("setPInDate", Date.valueOf("2012-01-01"));
executedWithoutDefault.call("execute", create());
assertEquals(123, executedWithoutDefault.call("getPOutNumber").<Number>get().intValue());
assertEquals("abc", executedWithoutDefault.call("getPOutVarchar").get());
assertEquals(Date.valueOf("2012-01-01"), executedWithoutDefault.call("getPOutDate").get());
}
@Test
public void testStoredFunctions() throws Exception {
if (cRoutines() == null) {

View File

@ -3,6 +3,7 @@ DROP PROCEDURE p_author_exists/
DROP PROCEDURE p_create_author/
DROP PROCEDURE p_create_author_by_name/
DROP PROCEDURE p391/
DROP PROCEDURE p_default/
DROP FUNCTION f_author_exists/
DROP FUNCTION f_one/
@ -402,6 +403,21 @@ BEGIN
END
/
CREATE PROCEDURE p_default (
IN p_in_number INTEGER DEFAULT(0),
OUT p_out_number INTEGER,
IN p_in_varchar VARCHAR(10) DEFAULT('0'),
OUT p_out_varchar VARCHAR(10),
IN p_in_date DATE DEFAULT('1981-07-10'),
OUT p_out_date DATE
) LANGUAGE SQL
BEGIN
SET p_out_number = p_in_number;
SET p_out_varchar = p_in_varchar;
SET p_out_date = p_in_date;
END
/
CREATE FUNCTION f_author_exists (author_name varchar(50))
RETURNS INTEGER
LANGUAGE SQL

View File

@ -227,6 +227,27 @@ public final class Routines {
p.execute(configuration);
}
/**
* Call LUKAS.P_DEFAULT
*
* @param pInNumber IN parameter
* @param pOutNumber OUT parameter
* @param pInVarchar IN parameter
* @param pOutVarchar OUT parameter
* @param pInDate IN parameter
* @param pOutDate OUT parameter
* @throws org.jooq.exception.DataAccessException if something went wrong executing the query
*/
public static org.jooq.test.db2.generatedclasses.routines.PDefault pDefault(org.jooq.Configuration configuration, java.lang.Integer pInNumber, java.lang.String pInVarchar, java.sql.Date pInDate) {
org.jooq.test.db2.generatedclasses.routines.PDefault p = new org.jooq.test.db2.generatedclasses.routines.PDefault();
p.setPInNumber(pInNumber);
p.setPInVarchar(pInVarchar);
p.setPInDate(pInDate);
p.execute(configuration);
return p;
}
/**
* Call LUKAS.P_UNUSED
*

View File

@ -0,0 +1,90 @@
/**
* This class is generated by jOOQ
*/
package org.jooq.test.db2.generatedclasses.routines;
/**
* This class is generated by jOOQ.
*/
public class PDefault extends org.jooq.impl.AbstractRoutine<java.lang.Void> {
private static final long serialVersionUID = -140946464;
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.Integer> P_IN_NUMBER = createParameter("P_IN_NUMBER", org.jooq.impl.SQLDataType.INTEGER);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.Integer> P_OUT_NUMBER = createParameter("P_OUT_NUMBER", org.jooq.impl.SQLDataType.INTEGER);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_IN_VARCHAR = createParameter("P_IN_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_OUT_VARCHAR = createParameter("P_OUT_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_IN_DATE = createParameter("P_IN_DATE", org.jooq.impl.SQLDataType.DATE);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_OUT_DATE = createParameter("P_OUT_DATE", org.jooq.impl.SQLDataType.DATE);
/**
* Create a new routine call instance
*/
public PDefault() {
super("P_DEFAULT", org.jooq.test.db2.generatedclasses.Lukas.LUKAS);
addInParameter(P_IN_NUMBER);
addOutParameter(P_OUT_NUMBER);
addInParameter(P_IN_VARCHAR);
addOutParameter(P_OUT_VARCHAR);
addInParameter(P_IN_DATE);
addOutParameter(P_OUT_DATE);
}
/**
* Set the <code>P_IN_NUMBER</code> parameter to the routine
*/
public void setPInNumber(java.lang.Integer value) {
setValue(P_IN_NUMBER, value);
}
/**
* Set the <code>P_IN_VARCHAR</code> parameter to the routine
*/
public void setPInVarchar(java.lang.String value) {
setValue(P_IN_VARCHAR, value);
}
/**
* Set the <code>P_IN_DATE</code> parameter to the routine
*/
public void setPInDate(java.sql.Date value) {
setValue(P_IN_DATE, value);
}
public java.lang.Integer getPOutNumber() {
return getValue(P_OUT_NUMBER);
}
public java.lang.String getPOutVarchar() {
return getValue(P_OUT_VARCHAR);
}
public java.sql.Date getPOutDate() {
return getValue(P_OUT_DATE);
}
}

View File

@ -1346,6 +1346,11 @@ public abstract class jOOQAbstractTest<
new RoutineAndUDTTests(this).testStoredProcedure();
}
@Test
public void testStoredProcedureWithDefaultParameters() throws Exception {
new RoutineAndUDTTests(this).testStoredProcedureWithDefaultParameters();
}
@Test
public void testArrayTables() throws Exception {
new RoutineAndUDTTests(this).testArrayTables();

View File

@ -53,6 +53,7 @@ DROP PROCEDURE p_tables2/
DROP PROCEDURE p_tables3/
DROP PROCEDURE p_tables4/
DROP PROCEDURE p_many_parameters/
DROP PROCEDURE p_default/
DROP FUNCTION f_arrays1/
DROP FUNCTION f_arrays2/
DROP FUNCTION f_arrays3/
@ -754,6 +755,22 @@ END p_tables4;
/
CREATE OR REPLACE PROCEDURE p_default (
p_in_number IN number := 0,
p_out_number OUT number,
p_in_varchar IN varchar2 := '0',
p_out_varchar OUT varchar2,
p_in_date IN date := date '1981-07-10',
p_out_date OUT date
)
IS
BEGIN
p_out_number := p_in_number;
p_out_varchar := p_in_varchar;
p_out_date := p_in_date;
END p_default;
/
CREATE OR REPLACE PROCEDURE p_many_parameters (
f000 number, f001 number, f002 number, f003 number, f004 number,
f005 number, f006 number, f007 number, f008 number, f009 number,

View File

@ -784,6 +784,27 @@ public final class Routines {
p.execute(configuration);
}
/**
* Call TEST.P_DEFAULT
*
* @param pInNumber IN parameter
* @param pOutNumber OUT parameter
* @param pInVarchar IN parameter
* @param pOutVarchar OUT parameter
* @param pInDate IN parameter
* @param pOutDate OUT parameter
* @throws org.jooq.exception.DataAccessException if something went wrong executing the query
*/
public static org.jooq.test.oracle.generatedclasses.test.routines.PDefault pDefault(org.jooq.Configuration configuration, java.lang.Number pInNumber, java.lang.String pInVarchar, java.sql.Date pInDate) {
org.jooq.test.oracle.generatedclasses.test.routines.PDefault p = new org.jooq.test.oracle.generatedclasses.test.routines.PDefault();
p.setPInNumber(pInNumber);
p.setPInVarchar(pInVarchar);
p.setPInDate(pInDate);
p.execute(configuration);
return p;
}
/**
* Call TEST.P_ENHANCE_ADDRESS1
*

View File

@ -0,0 +1,90 @@
/**
* This class is generated by jOOQ
*/
package org.jooq.test.oracle.generatedclasses.test.routines;
/**
* This class is generated by jOOQ.
*/
public class PDefault extends org.jooq.impl.AbstractRoutine<java.lang.Void> {
private static final long serialVersionUID = 1180154706;
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.math.BigDecimal> P_IN_NUMBER = createParameter("P_IN_NUMBER", org.jooq.impl.SQLDataType.NUMERIC, true);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.math.BigDecimal> P_OUT_NUMBER = createParameter("P_OUT_NUMBER", org.jooq.impl.SQLDataType.NUMERIC);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_IN_VARCHAR = createParameter("P_IN_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR, true);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_OUT_VARCHAR = createParameter("P_OUT_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_IN_DATE = createParameter("P_IN_DATE", org.jooq.impl.SQLDataType.DATE, true);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_OUT_DATE = createParameter("P_OUT_DATE", org.jooq.impl.SQLDataType.DATE);
/**
* Create a new routine call instance
*/
public PDefault() {
super("P_DEFAULT", org.jooq.test.oracle.generatedclasses.test.Test.TEST);
addInParameter(P_IN_NUMBER);
addOutParameter(P_OUT_NUMBER);
addInParameter(P_IN_VARCHAR);
addOutParameter(P_OUT_VARCHAR);
addInParameter(P_IN_DATE);
addOutParameter(P_OUT_DATE);
}
/**
* Set the <code>P_IN_NUMBER</code> parameter to the routine
*/
public void setPInNumber(java.lang.Number value) {
setNumber(P_IN_NUMBER, value);
}
/**
* Set the <code>P_IN_VARCHAR</code> parameter to the routine
*/
public void setPInVarchar(java.lang.String value) {
setValue(P_IN_VARCHAR, value);
}
/**
* Set the <code>P_IN_DATE</code> parameter to the routine
*/
public void setPInDate(java.sql.Date value) {
setValue(P_IN_DATE, value);
}
public java.math.BigDecimal getPOutNumber() {
return getValue(P_OUT_NUMBER);
}
public java.lang.String getPOutVarchar() {
return getValue(P_OUT_VARCHAR);
}
public java.sql.Date getPOutDate() {
return getValue(P_OUT_DATE);
}
}

View File

@ -17,6 +17,7 @@ DROP PROCEDURE p_create_author/
DROP PROCEDURE p_create_author_by_name/
DROP PROCEDURE p_author_exists/
DROP PROCEDURE p391/
DROP PROCEDURE p_default/
DROP FUNCTION f_many_parameters/
DROP FUNCTION f_author_exists/
DROP FUNCTION f_one/
@ -573,6 +574,21 @@ BEGIN
END;
/
CREATE PROCEDURE p_default (
@p_in_number INTEGER = 0,
@p_out_number INTEGER OUT,
@p_in_varchar VARCHAR(10) = '0',
@p_out_varchar VARCHAR(10) OUT,
@p_in_date DATE = '1981-07-10',
@p_out_date DATE OUT
) AS
BEGIN
SET @p_out_number = @p_in_number;
SET @p_out_varchar = @p_in_varchar;
SET @p_out_date = @p_in_date;
END;
/
CREATE FUNCTION f_author_exists (@author_name VARCHAR(50))
RETURNS int
AS

View File

@ -206,6 +206,30 @@ public final class Routines {
p.execute(configuration);
}
/**
* Call dbo.p_default
*
* @param pInNumber IN parameter
* @param pOutNumber IN OUT parameter
* @param pInVarchar IN parameter
* @param pOutVarchar IN OUT parameter
* @param pInDate IN parameter
* @param pOutDate IN OUT parameter
* @throws org.jooq.exception.DataAccessException if something went wrong executing the query
*/
public static org.jooq.test.sqlserver.generatedclasses.routines.PDefault pDefault(org.jooq.Configuration configuration, java.lang.Integer pInNumber, java.lang.Integer pOutNumber, java.lang.String pInVarchar, java.lang.String pOutVarchar, java.sql.Date pInDate, java.sql.Date pOutDate) {
org.jooq.test.sqlserver.generatedclasses.routines.PDefault p = new org.jooq.test.sqlserver.generatedclasses.routines.PDefault();
p.setPInNumber(pInNumber);
p.setPOutNumber(pOutNumber);
p.setPInVarchar(pInVarchar);
p.setPOutVarchar(pOutVarchar);
p.setPInDate(pInDate);
p.setPOutDate(pOutDate);
p.execute(configuration);
return p;
}
/**
* Call dbo.p_unused
*

View File

@ -0,0 +1,111 @@
/**
* This class is generated by jOOQ
*/
package org.jooq.test.sqlserver.generatedclasses.routines;
/**
* This class is generated by jOOQ.
*/
public class PDefault extends org.jooq.impl.AbstractRoutine<java.lang.Void> {
private static final long serialVersionUID = -401798427;
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.Integer> P_IN_NUMBER = createParameter("p_in_number", org.jooq.impl.SQLDataType.INTEGER);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.Integer> P_OUT_NUMBER = createParameter("p_out_number", org.jooq.impl.SQLDataType.INTEGER);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_IN_VARCHAR = createParameter("p_in_varchar", org.jooq.impl.SQLDataType.VARCHAR);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.lang.String> P_OUT_VARCHAR = createParameter("p_out_varchar", org.jooq.impl.SQLDataType.VARCHAR);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_IN_DATE = createParameter("p_in_date", org.jooq.impl.SQLDataType.DATE);
/**
* An uncommented item
*/
public static final org.jooq.Parameter<java.sql.Date> P_OUT_DATE = createParameter("p_out_date", org.jooq.impl.SQLDataType.DATE);
/**
* Create a new routine call instance
*/
public PDefault() {
super("p_default", org.jooq.test.sqlserver.generatedclasses.Dbo.DBO);
addInParameter(P_IN_NUMBER);
addInOutParameter(P_OUT_NUMBER);
addInParameter(P_IN_VARCHAR);
addInOutParameter(P_OUT_VARCHAR);
addInParameter(P_IN_DATE);
addInOutParameter(P_OUT_DATE);
}
/**
* Set the <code>p_in_number</code> parameter to the routine
*/
public void setPInNumber(java.lang.Integer value) {
setValue(P_IN_NUMBER, value);
}
/**
* Set the <code>p_out_number</code> parameter to the routine
*/
public void setPOutNumber(java.lang.Integer value) {
setValue(P_OUT_NUMBER, value);
}
/**
* Set the <code>p_in_varchar</code> parameter to the routine
*/
public void setPInVarchar(java.lang.String value) {
setValue(P_IN_VARCHAR, value);
}
/**
* Set the <code>p_out_varchar</code> parameter to the routine
*/
public void setPOutVarchar(java.lang.String value) {
setValue(P_OUT_VARCHAR, value);
}
/**
* Set the <code>p_in_date</code> parameter to the routine
*/
public void setPInDate(java.sql.Date value) {
setValue(P_IN_DATE, value);
}
/**
* Set the <code>p_out_date</code> parameter to the routine
*/
public void setPOutDate(java.sql.Date value) {
setValue(P_OUT_DATE, value);
}
public java.lang.Integer getPOutNumber() {
return getValue(P_OUT_NUMBER);
}
public java.lang.String getPOutVarchar() {
return getValue(P_OUT_VARCHAR);
}
public java.sql.Date getPOutDate() {
return getValue(P_OUT_DATE);
}
}

View File

@ -36,6 +36,8 @@
package org.jooq;
import static org.jooq.SQLDialect.ORACLE;
/**
* A parameter to a stored procedure or function.
*
@ -44,4 +46,35 @@ package org.jooq;
*/
public interface Parameter<T> extends NamedTypeProviderQueryPart<T> {
/**
* Whether this parameter has a default value
* <p>
* Procedures and functions with defaulted parameters behave slightly
* different from ones without defaulted parameters. In PL/SQL and other
* procedural languages, it is possible to pass parameters by name,
* reordering names and omitting defaulted parameters: <code><pre>
* CREATE PROCEDURE MY_PROCEDURE (P_DEFAULTED IN NUMBER := 0
* P_MANDATORY IN NUMBER);
*
* -- The above procedure can be called as such:
* BEGIN
* -- Assign parameters by index
* MY_PROCEDURE(1, 2);
*
* -- Assign parameters by name
* MY_PROCEDURE(P_DEFAULTED => 1,
* P_MANDATORY => 2);
*
* -- Omitting defaulted parameters
* MY_PROCEDURE(P_MANDATORY => 2);
* END;
* </pre></code>
* <p>
* If a procedure has defaulted parameters, jOOQ binds them by name, rather
* than by index.
* <p>
* Currently, this is only supported for Oracle
*/
@Support({ ORACLE })
boolean isDefaulted();
}

View File

@ -66,8 +66,10 @@ import org.jooq.exception.DataAccessException;
* <li>DB2, H2, and HSQLDB don't allow for JDBC escape syntax when calling
* functions. Functions must be used in a SELECT statement</li>
* <li>H2 only knows functions (without OUT parameters)</li>
* <li>Oracle functions may have OUT parameters/li>
* <li>Oracle functions may have OUT parameters</li>
* <li>Oracle knows functions that mustn't be used in SQL statements</li>
* <li>Oracle parameters can have default values (to support this, jOOQ renders
* PL/SQL instead of the JDBC escape syntax)</li>
* <li>Postgres only knows functions (with all features combined)</li>
* <li>The Sybase JDBC driver doesn't handle null values correctly when using
* the JDBC escape syntax on functions</li>

View File

@ -46,8 +46,10 @@ import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jooq.ArrayRecord;
import org.jooq.Attachable;
@ -82,21 +84,30 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
*/
private static final long serialVersionUID = 6330037113167106443L;
// ------------------------------------------------------------------------
// Meta-data attributes (the same for every call)
// ------------------------------------------------------------------------
private final Package pkg;
private final List<Parameter<?>> allParameters;
private final List<Parameter<?>> inParameters;
private final List<Parameter<?>> outParameters;
private final Map<Parameter<?>, Field<?>> inValues;
private final DataType<T> type;
private Parameter<T> returnParameter;
private boolean overloaded;
// ------------------------------------------------------------------------
// Call-data attributes (call-specific)
// ------------------------------------------------------------------------
private final Map<Parameter<?>, Field<?>> inValues;
private final Set<Parameter<?>> inValuesNonDefaulted;
private transient Field<T> function;
private final AttachableImpl attachable;
private final Map<Parameter<?>, Object> results;
private final Map<Parameter<?>, Integer> parameterIndexes;
private Parameter<T> returnParameter;
private boolean overloaded;
private transient Field<T> function;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
@ -160,6 +171,7 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
this.inParameters = new ArrayList<Parameter<?>>();
this.outParameters = new ArrayList<Parameter<?>>();
this.inValues = new HashMap<Parameter<?>, Field<?>>();
this.inValuesNonDefaulted = new HashSet<Parameter<?>>();
this.results = new HashMap<Parameter<?>, Object>();
this.type = type;
}
@ -189,9 +201,10 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
setField(parameter, val(null, parameter.getDataType()));
}
// Add the field to the in-values
// [#1183] Add the field to the in-values and mark them as non-defaulted
else {
inValues.put(parameter, value);
inValuesNonDefaulted.add(parameter);
}
}
@ -326,6 +339,12 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
@Override
public final void bind(BindContext context) {
for (Parameter<?> parameter : getParameters()) {
// [#1183] Skip defaulted parameters
if (getInParameters().contains(parameter) && !inValuesNonDefaulted.contains(parameter)) {
continue;
}
int index = context.peekIndex();
parameterIndexes.put(parameter, index);
@ -349,19 +368,17 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
@Override
public final void toSQL(RenderContext context) {
context.sql("{ ");
toSQLBegin(context);
if (getReturnParameter() != null) {
context.sql("? = ");
toSQLAssign(context);
}
context.sql("call ");
toSQLQualifiedName(context);
toSQLCall(context);
context.sql("(");
String separator = "";
for (Parameter<?> parameter : getParameters()) {
context.sql(separator);
// The return value has already been written
if (parameter.equals(getReturnParameter())) {
@ -370,7 +387,13 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
// OUT and IN OUT parameters are always written as a '?' bind variable
else if (getOutParameters().contains(parameter)) {
context.sql("?");
context.sql(separator);
toSQLOutParam(context, parameter);
}
// [#1183] Omit defaulted parameters
else if (!inValuesNonDefaulted.contains(parameter)) {
continue;
}
// IN parameters are rendered normally
@ -382,13 +405,97 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
value = value.cast(parameter.getType());
}
context.sql(value);
context.sql(separator);
toSQLInParam(context, parameter, value);
}
separator = ", ";
}
context.sql(") }");
context.sql(")");
toSQLEnd(context);
}
private final void toSQLEnd(RenderContext context) {
switch (context.getDialect()) {
case ORACLE:
context.sql(";")
.formatIndentEnd()
.formatSeparator()
.keyword("end;");
break;
default:
context.sql(" }");
break;
}
}
private final void toSQLBegin(RenderContext context) {
switch (context.getDialect()) {
case ORACLE:
context.keyword("begin")
.formatIndentStart()
.formatSeparator();
break;
default:
context.sql("{ ");
break;
}
}
private final void toSQLAssign(RenderContext context) {
switch (context.getDialect()) {
case ORACLE:
context.sql("? := ");
break;
default:
context.sql("? = ");
break;
}
}
private final void toSQLCall(RenderContext context) {
switch (context.getDialect()) {
case ORACLE:
break;
default:
context.sql("call ");
break;
}
toSQLQualifiedName(context);
}
private final void toSQLOutParam(RenderContext context, Parameter<?> parameter) {
switch (context.getDialect()) {
case ORACLE:
context.sql(parameter);
context.sql(" => ");
break;
default:
break;
}
context.sql("?");
}
private final void toSQLInParam(RenderContext context, Parameter<?> parameter, Field<?> value) {
switch (context.getDialect()) {
case ORACLE:
context.sql(parameter);
context.sql(" => ");
break;
default:
break;
}
context.sql(value);
}
private final void toSQLQualifiedName(RenderContext context) {
@ -407,11 +514,10 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
private final void fetchOutParameters(ExecuteContext ctx) throws SQLException {
for (Parameter<?> parameter : getParameters()) {
int index = parameterIndexes.get(parameter);
if (parameter.equals(getReturnParameter()) ||
getOutParameters().contains(parameter)) {
int index = parameterIndexes.get(parameter);
results.put(parameter, FieldTypeHelper.getFromStatement(ctx, parameter.getType(), index));
}
}
@ -527,6 +633,11 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
// IN parameters are initialised with null by default
inValues.put(parameter, val(null, parameter.getDataType()));
// [#1183] non-defaulted parameters are marked as such
if (!parameter.isDefaulted()) {
inValuesNonDefaulted.add(parameter);
}
}
protected final void addInOutParameter(Parameter<?> parameter) {
@ -564,7 +675,20 @@ public abstract class AbstractRoutine<T> extends AbstractSchemaProviderQueryPart
* @param type The data type of the field
*/
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type) {
return new ParameterImpl<T>(name, type);
return createParameter(name, type, false);
}
/**
* 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()}
*/
protected static final <T> Parameter<T> createParameter(String name, DataType<T> type, boolean isDefaulted) {
return new ParameterImpl<T>(name, type, isDefaulted);
}
/**

View File

@ -54,8 +54,12 @@ class ParameterImpl<T> extends AbstractNamedTypeProviderQueryPart<T> implements
private static final long serialVersionUID = -5277225593751085577L;
ParameterImpl(String name, DataType<T> type) {
private final boolean isDefaulted;
ParameterImpl(String name, DataType<T> type, boolean isDefaulted) {
super(name, type);
this.isDefaulted = isDefaulted;
}
@Override
@ -70,4 +74,9 @@ class ParameterImpl<T> extends AbstractNamedTypeProviderQueryPart<T> implements
public final void toSQL(RenderContext context) {
context.literal(getName());
}
@Override
public final boolean isDefaulted() {
return isDefaulted;
}
}