[#2674] Add support for stored procedures in MockConnection / MockDataProvider / MockExecuteContext
This commit is contained in:
parent
587155ab1f
commit
68b6ec5b60
@ -46,6 +46,7 @@ import java.sql.Statement;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.ResultQuery;
|
||||
|
||||
/**
|
||||
@ -94,12 +95,12 @@ public interface MockDataProvider {
|
||||
* <li><code>MockStatement</code> does not distinguish between "batch" and
|
||||
* "single" statements. However...
|
||||
* <ul>
|
||||
* <li>A {@link MockExecuteContext#batchSQL()} with more than one SQL
|
||||
* string is a strong indicator for a "multi-batch statement", as understood
|
||||
* by jOOQ's {@link DSLContext#batch(Query...)}.</li>
|
||||
* <li>A {@link MockExecuteContext#batchBindings()} with more than one
|
||||
* bind variable array is a strong indicator for a "single-batch statement",
|
||||
* as understood by jOOQ's {@link DSLContext#batch(Query)}.</li>
|
||||
* <li>A {@link MockExecuteContext#batchSQL()} with more than one SQL string
|
||||
* is a strong indicator for a "multi-batch statement", as understood by
|
||||
* jOOQ's {@link DSLContext#batch(Query...)}.</li>
|
||||
* <li>A {@link MockExecuteContext#batchBindings()} with more than one bind
|
||||
* variable array is a strong indicator for a "single-batch statement", as
|
||||
* understood by jOOQ's {@link DSLContext#batch(Query)}.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>It is recommended to return as many <code>MockResult</code> objects
|
||||
@ -133,6 +134,10 @@ public interface MockDataProvider {
|
||||
* <li> {@link MockExecuteContext#columnNames()}</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>OUT parameters from stored procedures are generated from the first
|
||||
* {@link MockResult}'s first {@link Record}. If OUT parameters are
|
||||
* requested, implementors must ensure the presence of such a
|
||||
* <code>Record</code>.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param ctx The execution context.
|
||||
|
||||
@ -64,6 +64,8 @@ public class MockExecuteContext {
|
||||
private final int[] columnIndexes;
|
||||
private final String[] columnNames;
|
||||
|
||||
private final int[] outParameterTypes;
|
||||
|
||||
/**
|
||||
* Create a new mock execution context.
|
||||
*
|
||||
@ -71,7 +73,7 @@ public class MockExecuteContext {
|
||||
* @param bindings The bind variable(s)
|
||||
*/
|
||||
public MockExecuteContext(String[] sql, Object[][] bindings) {
|
||||
this(sql, bindings, Statement.NO_GENERATED_KEYS, null, null);
|
||||
this(sql, bindings, Statement.NO_GENERATED_KEYS, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +85,7 @@ public class MockExecuteContext {
|
||||
* <code>MockStatement</code>
|
||||
*/
|
||||
public MockExecuteContext(String[] sql, Object[][] bindings, int autoGeneratedKeys) {
|
||||
this(sql, bindings, autoGeneratedKeys, null, null);
|
||||
this(sql, bindings, autoGeneratedKeys, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +98,7 @@ public class MockExecuteContext {
|
||||
* <code>MockStatement</code>
|
||||
*/
|
||||
public MockExecuteContext(String[] sql, Object[][] bindings, int[] columnIndexes) {
|
||||
this(sql, bindings, Statement.RETURN_GENERATED_KEYS, columnIndexes, null);
|
||||
this(sql, bindings, Statement.RETURN_GENERATED_KEYS, columnIndexes, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,16 +111,17 @@ public class MockExecuteContext {
|
||||
* <code>MockStatement</code>
|
||||
*/
|
||||
public MockExecuteContext(String[] sql, Object[][] bindings, String[] columnNames) {
|
||||
this(sql, bindings, Statement.RETURN_GENERATED_KEYS, null, columnNames);
|
||||
this(sql, bindings, Statement.RETURN_GENERATED_KEYS, null, columnNames, null);
|
||||
}
|
||||
|
||||
MockExecuteContext(String[] sql, Object[][] bindings, int autoGeneratedKeys, int[] columnIndexes,
|
||||
String[] columnNames) {
|
||||
String[] columnNames, int[] outParameterTypes) {
|
||||
this.sql = sql;
|
||||
this.bindings = bindings;
|
||||
this.autoGeneratedKeys = autoGeneratedKeys;
|
||||
this.columnIndexes = columnIndexes;
|
||||
this.columnNames = columnNames;
|
||||
this.outParameterTypes = outParameterTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,4 +219,13 @@ public class MockExecuteContext {
|
||||
public String[] columnNames() {
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the types of registered out parameters, if any.
|
||||
*
|
||||
* @return the types of registered out parameters, if any.
|
||||
*/
|
||||
public int[] outParameterTypes() {
|
||||
return outParameterTypes == null ? new int[0] : outParameterTypes.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,8 @@ import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jooq.Record;
|
||||
|
||||
/**
|
||||
* A mock statement.
|
||||
* <p>
|
||||
@ -86,26 +88,28 @@ import java.util.Map;
|
||||
*/
|
||||
public class MockStatement extends JDBC41Statement implements CallableStatement {
|
||||
|
||||
private final MockConnection connection;
|
||||
private final MockConnection connection;
|
||||
|
||||
private final MockDataProvider data;
|
||||
private final List<String> sql;
|
||||
private final List<List<Object>> bindings;
|
||||
private MockResult[] result;
|
||||
private int resultIndex;
|
||||
private boolean isClosed;
|
||||
private final MockDataProvider data;
|
||||
private final List<String> sql;
|
||||
private final List<List<Object>> bindings;
|
||||
private final List<Integer> outParameterTypes;
|
||||
private MockResult[] result;
|
||||
private int resultIndex;
|
||||
private boolean resultWasNull;
|
||||
private boolean isClosed;
|
||||
|
||||
// Execution parameters
|
||||
int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
|
||||
int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
|
||||
int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
|
||||
int autoGeneratedKeys = Statement.NO_GENERATED_KEYS;
|
||||
int[] columnIndexes;
|
||||
String[] columnNames;
|
||||
int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
|
||||
int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
|
||||
int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
|
||||
int autoGeneratedKeys = Statement.NO_GENERATED_KEYS;
|
||||
int[] columnIndexes;
|
||||
String[] columnNames;
|
||||
|
||||
// Statement properties
|
||||
private int queryTimeout;
|
||||
private int maxRows;
|
||||
private int queryTimeout;
|
||||
private int maxRows;
|
||||
|
||||
public MockStatement(MockConnection connection, MockDataProvider data) {
|
||||
this(connection, data, null);
|
||||
@ -116,6 +120,7 @@ public class MockStatement extends JDBC41Statement implements CallableStatement
|
||||
this.data = data;
|
||||
this.sql = new ArrayList<String>();
|
||||
this.bindings = new ArrayList<List<Object>>();
|
||||
this.outParameterTypes = new ArrayList<Integer>();
|
||||
|
||||
if (sql != null) {
|
||||
this.sql.add(sql);
|
||||
@ -140,6 +145,12 @@ public class MockStatement extends JDBC41Statement implements CallableStatement
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureOutParameterTypesCapacity(int index) {
|
||||
if (outParameterTypes.size() < index) {
|
||||
outParameterTypes.addAll(nCopies(index - outParameterTypes.size(), (Integer) null));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNotClosed() throws SQLException {
|
||||
if (isClosed) {
|
||||
throw new SQLException("Connection is already closed");
|
||||
@ -172,13 +183,25 @@ public class MockStatement extends JDBC41Statement implements CallableStatement
|
||||
new Object[][] { bindings().toArray() },
|
||||
localAutoGeneratedKeys,
|
||||
localColumnIndexes,
|
||||
localColumnNames
|
||||
localColumnNames,
|
||||
unbox(outParameterTypes)
|
||||
);
|
||||
|
||||
MockStatement.this.result = data.execute(context);
|
||||
result = data.execute(context);
|
||||
return result != null && result.length > 0 && result[resultIndex].data != null;
|
||||
}
|
||||
|
||||
private static final int[] unbox(List<Integer> list) {
|
||||
int[] array = new int[list.size()];
|
||||
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Integer value = list.get(i);
|
||||
array[i] = value == null ? 0 : value;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet getGeneratedKeys() throws SQLException {
|
||||
return getResultSet();
|
||||
@ -481,137 +504,200 @@ public class MockStatement extends JDBC41Statement implements CallableStatement
|
||||
// XXX: Bind variables from CallableStatement
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private Record outParameters() throws SQLException {
|
||||
if (result == null || result.length == 0 || result[0].data == null || result[0].data.size() == 0)
|
||||
throw new SQLException("No OUT Parameters available");
|
||||
|
||||
return result[0].data.get(0);
|
||||
}
|
||||
|
||||
private int translate(int parameterIndex) throws SQLException {
|
||||
if (parameterIndex > outParameterTypes.size())
|
||||
throw new SQLException("OUT parameter index too high: " + parameterIndex);
|
||||
|
||||
int index = -1;
|
||||
|
||||
for (int i = 0; i < parameterIndex; i++)
|
||||
if (outParameterTypes.get(i) != null)
|
||||
index++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException {
|
||||
checkNotClosed();
|
||||
ensureBindingsCapacity(parameterIndex);
|
||||
ensureOutParameterTypesCapacity(parameterIndex);
|
||||
outParameterTypes.set(parameterIndex - 1, sqlType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException {
|
||||
checkNotClosed();
|
||||
ensureBindingsCapacity(parameterIndex);
|
||||
registerOutParameter(parameterIndex, sqlType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException {
|
||||
checkNotClosed();
|
||||
ensureBindingsCapacity(parameterIndex);
|
||||
registerOutParameter(parameterIndex, sqlType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasNull() throws SQLException {
|
||||
return false;
|
||||
return resultWasNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
String value = outParameters().getValue(translate(parameterIndex), String.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNString(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
String value = outParameters().getValue(translate(parameterIndex), String.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(int parameterIndex) throws SQLException {
|
||||
return false;
|
||||
Boolean value = outParameters().getValue(translate(parameterIndex), Boolean.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? false : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Byte value = outParameters().getValue(translate(parameterIndex), Byte.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? (byte) 0 : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Short value = outParameters().getValue(translate(parameterIndex), Short.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? (short) 0 : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Integer value = outParameters().getValue(translate(parameterIndex), Integer.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Long value = outParameters().getValue(translate(parameterIndex), Long.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Float value = outParameters().getValue(translate(parameterIndex), Float.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? 0.0f : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int parameterIndex) throws SQLException {
|
||||
return 0;
|
||||
Double value = outParameters().getValue(translate(parameterIndex), Double.class);
|
||||
resultWasNull = value == null;
|
||||
return value == null ? 0.0 : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Time getTime(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
BigDecimal value = outParameters().getValue(translate(parameterIndex), BigDecimal.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
BigDecimal value = outParameters().getValue(translate(parameterIndex), BigDecimal.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(int parameterIndex) throws SQLException {
|
||||
byte[] value = outParameters().getValue(translate(parameterIndex), byte[].class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int parameterIndex, Map<String, Class<?>> map) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array getArray(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
public Date getDate(int parameterIndex) throws SQLException {
|
||||
Date value = outParameters().getValue(translate(parameterIndex), Date.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate(int parameterIndex, Calendar cal) throws SQLException {
|
||||
return null;
|
||||
Date value = outParameters().getValue(translate(parameterIndex), Date.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Time getTime(int parameterIndex) throws SQLException {
|
||||
Time value = outParameters().getValue(translate(parameterIndex), Time.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Time getTime(int parameterIndex, Calendar cal) throws SQLException {
|
||||
return null;
|
||||
Time value = outParameters().getValue(translate(parameterIndex), Time.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int parameterIndex) throws SQLException {
|
||||
Timestamp value = outParameters().getValue(translate(parameterIndex), Timestamp.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException {
|
||||
return null;
|
||||
Timestamp value = outParameters().getValue(translate(parameterIndex), Timestamp.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int parameterIndex) throws SQLException {
|
||||
Object value = outParameters().getValue(translate(parameterIndex));
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int parameterIndex, Map<String, Class<?>> map) throws SQLException {
|
||||
Object value = outParameters().getValue(translate(parameterIndex));
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array getArray(int parameterIndex) throws SQLException {
|
||||
Array value = outParameters().getValue(translate(parameterIndex), Array.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL(int parameterIndex) throws SQLException {
|
||||
return null;
|
||||
URL value = outParameters().getValue(translate(parameterIndex), URL.class);
|
||||
resultWasNull = value == null;
|
||||
return value;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -81,6 +81,7 @@ public abstract class AbstractTest {
|
||||
protected PreparedStatement statement;
|
||||
protected DSLContext create;
|
||||
protected Result<Table1Record> resultEmpty;
|
||||
protected Table1Record recordOne;
|
||||
protected Result<Table1Record> resultOne;
|
||||
protected Result<Table1Record> resultTwo;
|
||||
protected Result<Record3<String, String, String>> resultStrings;
|
||||
@ -102,7 +103,7 @@ public abstract class AbstractTest {
|
||||
resultEmpty = create.newResult(TABLE1);
|
||||
|
||||
resultOne = create.newResult(TABLE1);
|
||||
resultOne.add(create.newRecord(TABLE1));
|
||||
resultOne.add(recordOne = create.newRecord(TABLE1));
|
||||
resultOne.get(0).setValue(FIELD_ID1, 1);
|
||||
resultOne.get(0).setValue(FIELD_NAME1, "1");
|
||||
resultOne.get(0).changed(false);
|
||||
|
||||
@ -59,8 +59,10 @@ import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Constants;
|
||||
@ -154,7 +156,7 @@ public class MockTest extends AbstractTest {
|
||||
execute0(ctx);
|
||||
|
||||
return new MockResult[] {
|
||||
new MockResult(0, resultOne)
|
||||
new MockResult(recordOne)
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -509,4 +511,47 @@ public class MockTest extends AbstractTest {
|
||||
|
||||
assertFalse(r.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallableStatements() throws SQLException {
|
||||
MockConnection connection = new MockConnection(new MockDataProvider() {
|
||||
@Override
|
||||
public MockResult[] execute(MockExecuteContext ctx) throws SQLException {
|
||||
assertEquals("{ ? = call my_function(?, ?, ?, ?) }", ctx.sql());
|
||||
|
||||
assertEquals(5, ctx.bindings().length);
|
||||
assertEquals(null, ctx.bindings()[0]);
|
||||
assertEquals(2, ctx.bindings()[1]);
|
||||
assertEquals(null, ctx.bindings()[2]);
|
||||
assertEquals(4, ctx.bindings()[3]);
|
||||
assertEquals(null, ctx.bindings()[4]);
|
||||
|
||||
assertEquals(5, ctx.outParameterTypes().length);
|
||||
assertEquals(Types.INTEGER, ctx.outParameterTypes()[0]);
|
||||
assertEquals(0, ctx.outParameterTypes()[1]);
|
||||
assertEquals(Types.VARCHAR, ctx.outParameterTypes()[2]);
|
||||
assertEquals(0, ctx.outParameterTypes()[3]);
|
||||
assertEquals(Types.DATE, ctx.outParameterTypes()[4]);
|
||||
|
||||
return new MockResult[] { new MockResult(recordOne) };
|
||||
}
|
||||
});
|
||||
|
||||
CallableStatement stmt = connection.prepareCall("{ ? = call my_function(?, ?, ?, ?) }");
|
||||
|
||||
stmt.registerOutParameter(1, Types.INTEGER);
|
||||
stmt.setInt(2, 2);
|
||||
stmt.registerOutParameter(3, Types.VARCHAR);
|
||||
stmt.setInt(4, 4);
|
||||
stmt.registerOutParameter(5, Types.DATE);
|
||||
|
||||
stmt.executeUpdate();
|
||||
|
||||
assertEquals(1, stmt.getInt(1));
|
||||
assertFalse(stmt.wasNull());
|
||||
assertEquals("1", stmt.getString(3));
|
||||
assertFalse(stmt.wasNull());
|
||||
assertNull(stmt.getDate(5));
|
||||
assertTrue(stmt.wasNull());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user