Skip to content

Array Types #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 15, 2022
4 changes: 2 additions & 2 deletions .github/workflows/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,6 @@ echo "HOST=localhost" >> src/test/resources/config.properties
echo "PORT=1521" >> src/test/resources/config.properties
echo "USER=test" >> src/test/resources/config.properties
echo "PASSWORD=test" >> src/test/resources/config.properties
echo "CONNECT_TIMEOUT=120" >> src/test/resources/config.properties
echo "SQL_TIMEOUT=120" >> src/test/resources/config.properties
echo "CONNECT_TIMEOUT=180" >> src/test/resources/config.properties
echo "SQL_TIMEOUT=180" >> src/test/resources/config.properties
mvn clean compile test
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,55 @@ prefetched entirely, a smaller prefetch size can be configured using the
option, and the LOB can be consumed as a stream. By mapping LOB columns to
`Blob` or `Clob` objects, the content can be consumed as a reactive stream.

### REF Cursors
### ARRAY
Oracle Database supports `ARRAY` as a user defined type only. A `CREATE TYPE`
command is used to define an `ARRAY` type:
```sql
CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
```
Oracle R2DBC defines `oracle.r2dbc.OracleR2dbcType.ArrayType` as a `Type` for
representing user defined `ARRAY` types. A `Parameter` with a type of
`ArrayType` must be used when binding array values to a `Statement`.
```java
Publisher<Result> arrayBindExample(Connection connection) {
Statement statement =
connection.createStatement("INSERT INTO example VALUES (:array_bind)");

// Use the name defined for an ARRAY type:
// CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
Integer[] arrayValues = {1, 2, 3};
statement.bind("arrayBind", Parameters.in(arrayType, arrayValues));

return statement.execute();
}
```
A `Parameter` with a type of `ArrayType` must also be used when binding OUT
parameters of a PL/SQL call.
```java
Publisher<Result> arrayOutBindExample(Connection connection) {
Statement statement =
connection.createStatement("BEGIN; exampleCall(:array_bind); END;");

// Use the name defined for an ARRAY type:
// CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
statement.bind("arrayBind", Parameters.out(arrayType));

return statement.execute();
}
```
`ARRAY` values may be consumed from a `Row` or `OutParameter` as a Java array.
The element type of the Java array may be any Java type that is supported as
a mapping for the SQL type of the `ARRAY`. For instance, if the `ARRAY` type is
`NUMBER`, then a `Integer[]` mapping is supported:
```java
Publisher<Integer[]> arrayMapExample(Result result) {
return result.map(readable -> readable.get("arrayValue", Integer[].class));
}
```

### REF Cursor
Use the `oracle.r2dbc.OracleR2dbcTypes.REF_CURSOR` type to bind `SYS_REFCURSOR` out
parameters:
```java
Expand Down
98 changes: 97 additions & 1 deletion src/main/java/oracle/r2dbc/OracleR2dbcTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
*/
package oracle.r2dbc;

import io.r2dbc.spi.Parameter;
import io.r2dbc.spi.R2dbcType;
import io.r2dbc.spi.Result;
import io.r2dbc.spi.Statement;
import io.r2dbc.spi.Type;
import oracle.sql.json.OracleJsonObject;

Expand All @@ -29,6 +32,7 @@
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.Objects;

/**
* SQL types supported by Oracle Database that are not defined as standard types
Expand Down Expand Up @@ -99,10 +103,102 @@ private OracleR2dbcTypes() {}
public static final Type REF_CURSOR =
new TypeImpl(Result.class, "SYS_REFCURSOR");

/**
* <p>
* Creates an {@link ArrayType} representing a user defined {@code ARRAY}
* type. The {@code name} passed to this method must identify the name of a
* user defined {@code ARRAY} type.
* </p><p>
* Typically, the name passed to this method should be UPPER CASE, unless the
* {@code CREATE TYPE} command that created the type used an "enquoted" type
* name.
* </p><p>
* The {@code ArrayType} object returned by this method may be used to create
* a {@link Parameter} that binds an array value to a {@link Statement}.
* </p><pre>{@code
* Publisher<Result> arrayBindExample(Connection connection) {
* Statement statement =
* connection.createStatement("INSERT INTO example VALUES (:array_bind)");
*
* // Use the name defined for an ARRAY type:
* // CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
* ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
* Integer[] arrayValues = {1, 2, 3};
* statement.bind("arrayBind", Parameters.in(arrayType, arrayValues));
*
* return statement.execute();
* }
* }</pre>
* @param name Name of a user defined ARRAY type. Not null.
* @return A {@code Type} object representing the user defined ARRAY type. Not
* null.
*/
public static ArrayType arrayType(String name) {
return new ArrayTypeImpl(Objects.requireNonNull(name, "name is null"));
}

/**
* Extension of the standard {@link Type} interface used to represent user
* defined ARRAY types. An instance of {@code ArrayType} must be used when
* binding an array value to a {@link Statement} created by the Oracle R2DBC
* Driver.
* </p><p>
* Oracle Database does not support an anonymous {@code ARRAY} type, which is
* what the standard {@link R2dbcType#COLLECTION} type represents. Oracle
* Database only supports {@code ARRAY} types which are declared as a user
* defined type, as in:
* <pre>{@code
* CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
* }</pre>
* In order to bind an array, the name of a user defined ARRAY type must
* be known to Oracle R2DBC. Instances of {@code ArrayType} retain the name
* that is provided to the {@link #arrayType(String)} factory method.
*/
public interface ArrayType extends Type {

/**
* {@inheritDoc}
* Returns {@code Object[].class}, which is the standard mapping for
* {@link R2dbcType#COLLECTION}. The true default type mapping is the array
* variant of the default mapping for the element type of the {@code ARRAY}.
* For instance, an {@code ARRAY} of {@code VARCHAR} maps to a
* {@code String[]} by default.
*/
@Override
Class<?> getJavaType();

/**
* {@inheritDoc}
* Returns the name of this user defined {@code ARRAY} type. For instance,
* this method returns "MY_ARRAY" if the type is declared as:
* <pre>{@code
* CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
* }</pre>
*/
@Override
String getName();
}

/** Concrete implementation of the {@code ArrayType} interface */
private static final class ArrayTypeImpl
extends TypeImpl implements ArrayType {

/**
* Constructs an ARRAY type with the given {@code name}. The constructed
* {@code ArrayType} as a default Java type mapping of
* {@code Object[].class}. This is consistent with the standard
* {@link R2dbcType#COLLECTION} type.
* @param name User defined name of the type. Not null.
*/
ArrayTypeImpl(String name) {
super(Object[].class, name);
}
}

/**
* Implementation of the {@link Type} SPI.
*/
private static final class TypeImpl implements Type {
private static class TypeImpl implements Type {

/**
* The Java Language mapping of this SQL type.
Expand Down
Loading