diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGenerator.java index 1c251c7ec9..1aeda377dd 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/DefaultGenerator.java @@ -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(");"); } 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 bcab0b7d94..cae81f2c13 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/DefaultParameterDefinition.java @@ -46,7 +46,20 @@ public class DefaultParameterDefinition extends AbstractTypedElementDefinition 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; } } 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 0cfe7077ba..6c621f7df2 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/ParameterDefinition.java @@ -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 { + /** + * Whether the parameter has a default value. + *

+ * @see Parameter#isDefaulted() + */ + boolean isDefaulted(); } diff --git a/jOOQ-meta/src/main/java/org/jooq/util/oracle/OracleRoutineDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/oracle/OracleRoutineDefinition.java index 66fbdb7d06..6b87b3f34b 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/oracle/OracleRoutineDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/oracle/OracleRoutineDefinition.java @@ -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); } diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/RoutineAndUDTTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/RoutineAndUDTTests.java index 4bad61ff3d..fc4668acde 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/RoutineAndUDTTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/RoutineAndUDTTests.java @@ -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 BaseTestget()) { + 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").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").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) { diff --git a/jOOQ-test/src/org/jooq/test/db2/create.sql b/jOOQ-test/src/org/jooq/test/db2/create.sql index 7f19f292cf..04da9973e8 100644 --- a/jOOQ-test/src/org/jooq/test/db2/create.sql +++ b/jOOQ-test/src/org/jooq/test/db2/create.sql @@ -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 diff --git a/jOOQ-test/src/org/jooq/test/db2/generatedclasses/Routines.java b/jOOQ-test/src/org/jooq/test/db2/generatedclasses/Routines.java index c2ae5fce74..9bd7086db7 100644 --- a/jOOQ-test/src/org/jooq/test/db2/generatedclasses/Routines.java +++ b/jOOQ-test/src/org/jooq/test/db2/generatedclasses/Routines.java @@ -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 * diff --git a/jOOQ-test/src/org/jooq/test/db2/generatedclasses/routines/PDefault.java b/jOOQ-test/src/org/jooq/test/db2/generatedclasses/routines/PDefault.java new file mode 100644 index 0000000000..da4d2db0c0 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/db2/generatedclasses/routines/PDefault.java @@ -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 { + + private static final long serialVersionUID = -140946464; + + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_NUMBER = createParameter("P_IN_NUMBER", org.jooq.impl.SQLDataType.INTEGER); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_NUMBER = createParameter("P_OUT_NUMBER", org.jooq.impl.SQLDataType.INTEGER); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_VARCHAR = createParameter("P_IN_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_VARCHAR = createParameter("P_OUT_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_DATE = createParameter("P_IN_DATE", org.jooq.impl.SQLDataType.DATE); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter 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 P_IN_NUMBER parameter to the routine + */ + public void setPInNumber(java.lang.Integer value) { + setValue(P_IN_NUMBER, value); + } + + /** + * Set the P_IN_VARCHAR parameter to the routine + */ + public void setPInVarchar(java.lang.String value) { + setValue(P_IN_VARCHAR, value); + } + + /** + * Set the P_IN_DATE 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); + } +} diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 98d7b4827a..0bca417dab 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -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(); diff --git a/jOOQ-test/src/org/jooq/test/oracle/create.sql b/jOOQ-test/src/org/jooq/test/oracle/create.sql index 1ebcf179b8..51bf38763b 100644 --- a/jOOQ-test/src/org/jooq/test/oracle/create.sql +++ b/jOOQ-test/src/org/jooq/test/oracle/create.sql @@ -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, diff --git a/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/Routines.java b/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/Routines.java index d26eb202c1..55b5ba54b9 100644 --- a/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/Routines.java +++ b/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/Routines.java @@ -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 * diff --git a/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/routines/PDefault.java b/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/routines/PDefault.java new file mode 100644 index 0000000000..b89fe158c7 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/oracle/generatedclasses/test/routines/PDefault.java @@ -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 { + + private static final long serialVersionUID = 1180154706; + + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_NUMBER = createParameter("P_IN_NUMBER", org.jooq.impl.SQLDataType.NUMERIC, true); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_NUMBER = createParameter("P_OUT_NUMBER", org.jooq.impl.SQLDataType.NUMERIC); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_VARCHAR = createParameter("P_IN_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR, true); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_VARCHAR = createParameter("P_OUT_VARCHAR", org.jooq.impl.SQLDataType.VARCHAR); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_DATE = createParameter("P_IN_DATE", org.jooq.impl.SQLDataType.DATE, true); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter 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 P_IN_NUMBER parameter to the routine + */ + public void setPInNumber(java.lang.Number value) { + setNumber(P_IN_NUMBER, value); + } + + /** + * Set the P_IN_VARCHAR parameter to the routine + */ + public void setPInVarchar(java.lang.String value) { + setValue(P_IN_VARCHAR, value); + } + + /** + * Set the P_IN_DATE 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); + } +} diff --git a/jOOQ-test/src/org/jooq/test/sqlserver/create.sql b/jOOQ-test/src/org/jooq/test/sqlserver/create.sql index 39972464e3..c090fe272d 100644 --- a/jOOQ-test/src/org/jooq/test/sqlserver/create.sql +++ b/jOOQ-test/src/org/jooq/test/sqlserver/create.sql @@ -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 diff --git a/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/Routines.java b/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/Routines.java index c00ab8db9d..6e128a0244 100644 --- a/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/Routines.java +++ b/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/Routines.java @@ -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 * diff --git a/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/routines/PDefault.java b/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/routines/PDefault.java new file mode 100644 index 0000000000..bd2e1df889 --- /dev/null +++ b/jOOQ-test/src/org/jooq/test/sqlserver/generatedclasses/routines/PDefault.java @@ -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 { + + private static final long serialVersionUID = -401798427; + + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_NUMBER = createParameter("p_in_number", org.jooq.impl.SQLDataType.INTEGER); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_NUMBER = createParameter("p_out_number", org.jooq.impl.SQLDataType.INTEGER); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_VARCHAR = createParameter("p_in_varchar", org.jooq.impl.SQLDataType.VARCHAR); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_OUT_VARCHAR = createParameter("p_out_varchar", org.jooq.impl.SQLDataType.VARCHAR); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter P_IN_DATE = createParameter("p_in_date", org.jooq.impl.SQLDataType.DATE); + + /** + * An uncommented item + */ + public static final org.jooq.Parameter 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 p_in_number parameter to the routine + */ + public void setPInNumber(java.lang.Integer value) { + setValue(P_IN_NUMBER, value); + } + + /** + * Set the p_out_number parameter to the routine + */ + public void setPOutNumber(java.lang.Integer value) { + setValue(P_OUT_NUMBER, value); + } + + /** + * Set the p_in_varchar parameter to the routine + */ + public void setPInVarchar(java.lang.String value) { + setValue(P_IN_VARCHAR, value); + } + + /** + * Set the p_out_varchar parameter to the routine + */ + public void setPOutVarchar(java.lang.String value) { + setValue(P_OUT_VARCHAR, value); + } + + /** + * Set the p_in_date parameter to the routine + */ + public void setPInDate(java.sql.Date value) { + setValue(P_IN_DATE, value); + } + + /** + * Set the p_out_date 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); + } +} diff --git a/jOOQ/src/main/java/org/jooq/Parameter.java b/jOOQ/src/main/java/org/jooq/Parameter.java index 657c79913b..093aa69591 100644 --- a/jOOQ/src/main/java/org/jooq/Parameter.java +++ b/jOOQ/src/main/java/org/jooq/Parameter.java @@ -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 extends NamedTypeProviderQueryPart { + /** + * Whether this parameter has a default value + *

+ * 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:

+     * 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;
+     * 
+ *

+ * If a procedure has defaulted parameters, jOOQ binds them by name, rather + * than by index. + *

+ * Currently, this is only supported for Oracle + */ + @Support({ ORACLE }) + boolean isDefaulted(); } diff --git a/jOOQ/src/main/java/org/jooq/Routine.java b/jOOQ/src/main/java/org/jooq/Routine.java index b6f4c52f25..a711cc3472 100644 --- a/jOOQ/src/main/java/org/jooq/Routine.java +++ b/jOOQ/src/main/java/org/jooq/Routine.java @@ -66,8 +66,10 @@ import org.jooq.exception.DataAccessException; *

  • DB2, H2, and HSQLDB don't allow for JDBC escape syntax when calling * functions. Functions must be used in a SELECT statement
  • *
  • H2 only knows functions (without OUT parameters)
  • - *
  • Oracle functions may have OUT parameters/li> + *
  • Oracle functions may have OUT parameters
  • *
  • Oracle knows functions that mustn't be used in SQL statements
  • + *
  • Oracle parameters can have default values (to support this, jOOQ renders + * PL/SQL instead of the JDBC escape syntax)
  • *
  • Postgres only knows functions (with all features combined)
  • *
  • The Sybase JDBC driver doesn't handle null values correctly when using * the JDBC escape syntax on functions
  • diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 2ab3f37e8b..634782cdf2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -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 extends AbstractSchemaProviderQueryPart */ private static final long serialVersionUID = 6330037113167106443L; + // ------------------------------------------------------------------------ + // Meta-data attributes (the same for every call) + // ------------------------------------------------------------------------ + private final Package pkg; private final List> allParameters; private final List> inParameters; private final List> outParameters; - private final Map, Field> inValues; private final DataType type; + private Parameter returnParameter; + private boolean overloaded; + + // ------------------------------------------------------------------------ + // Call-data attributes (call-specific) + // ------------------------------------------------------------------------ + + private final Map, Field> inValues; + private final Set> inValuesNonDefaulted; + private transient Field function; private final AttachableImpl attachable; private final Map, Object> results; private final Map, Integer> parameterIndexes; - private Parameter returnParameter; - private boolean overloaded; - private transient Field function; - // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ @@ -160,6 +171,7 @@ public abstract class AbstractRoutine extends AbstractSchemaProviderQueryPart this.inParameters = new ArrayList>(); this.outParameters = new ArrayList>(); this.inValues = new HashMap, Field>(); + this.inValuesNonDefaulted = new HashSet>(); this.results = new HashMap, Object>(); this.type = type; } @@ -189,9 +201,10 @@ public abstract class AbstractRoutine 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 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 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 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 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 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 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 extends AbstractSchemaProviderQueryPart * @param type The data type of the field */ protected static final Parameter createParameter(String name, DataType type) { - return new ParameterImpl(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 Parameter createParameter(String name, DataType type, boolean isDefaulted) { + return new ParameterImpl(name, type, isDefaulted); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index 9664e14480..985db2b724 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -54,8 +54,12 @@ class ParameterImpl extends AbstractNamedTypeProviderQueryPart implements private static final long serialVersionUID = -5277225593751085577L; - ParameterImpl(String name, DataType type) { + private final boolean isDefaulted; + + ParameterImpl(String name, DataType type, boolean isDefaulted) { super(name, type); + + this.isDefaulted = isDefaulted; } @Override @@ -70,4 +74,9 @@ class ParameterImpl extends AbstractNamedTypeProviderQueryPart implements public final void toSQL(RenderContext context) { context.literal(getName()); } + + @Override + public final boolean isDefaulted() { + return isDefaulted; + } }