Skip to content
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

Add support for prepared statements in JDBC #3116

Merged
merged 14 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public enum ErrorCode {
CANNOT_CONVERT_TYPE("22000"),
INVALID_ROW_COUNT_IN_LIMIT_CLAUSE("2201W"),
INVALID_PARAMETER("22023"),
ARRAY_ELEMENT_ERROR("2202E"),
INVALID_BINARY_REPRESENTATION("22F03"),
INVALID_ARGUMENT_FOR_FUNCTION("22F00"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import com.apple.foundationdb.relational.api.ArrayMetaData;
import com.apple.foundationdb.relational.api.Continuation;
import com.apple.foundationdb.relational.api.SqlTypeNamesSupport;
import com.apple.foundationdb.relational.api.StructMetaData;
import com.apple.foundationdb.relational.api.RelationalArray;
import com.apple.foundationdb.relational.api.RelationalResultSet;
Expand Down Expand Up @@ -251,23 +252,36 @@ private static Struct toStruct(RelationalStruct relationalStruct) throws SQLExce
public static Object fromColumn(int columnType, Column column) throws SQLException {
switch (columnType) {
case Types.ARRAY:
checkColumnType(columnType, column.hasArray());
return fromArray(column.getArray());
case Types.BIGINT:
checkColumnType(columnType, column.hasLong());
ScottDugas marked this conversation as resolved.
Show resolved Hide resolved
return column.getLong();
case Types.INTEGER:
checkColumnType(columnType, column.hasInteger());
return column.getInteger();
case Types.BOOLEAN:
checkColumnType(columnType, column.hasBoolean());
return column.getBoolean();
case Types.VARCHAR:
checkColumnType(columnType, column.hasString());
return column.getString();
case Types.BINARY:
checkColumnType(columnType, column.hasBinary());
return column.getBinary().toByteArray();
case Types.DOUBLE:
checkColumnType(columnType, column.hasDouble());
return column.getDouble();
default:
// TODO: NULL?
throw new SQLException("java.sql.Type=" + columnType + " not supported",
ErrorCode.UNSUPPORTED_OPERATION.getErrorCode());
// NULL (java.sql.Types value 0) is not a valid column type for an array and is likely the result of a default value for the
// (optional) array.getElementType() protobuf field.
throw new SQLException("java.sql.Type=" + columnType + " not supported", ErrorCode.ARRAY_ELEMENT_ERROR.getErrorCode());
}
}

private static void checkColumnType(final int expectedColumnType, final boolean columnHasType) throws SQLException {
if (!columnHasType) {
throw new SQLException("Column has wrong type (expected " + expectedColumnType + ")", ErrorCode.WRONG_OBJECT_TYPE.getErrorCode());
}
}

Expand Down Expand Up @@ -302,37 +316,44 @@ public static Array toArray(@Nonnull java.sql.Array array) throws SQLException {

/**
* Create {@link Column} from a Java object.
* Note: In case the column is of a composite type (array, struct) then the actual type has to be a SQL flavor
* ({@link java.sql.Array} or {@link java.sql.Struct}.
* Note: In case of {@link Types#NULL} null column, the obje parameter is expected to be the type of null
* Note: In case the column is of a composite type (array) then the actual type has to be a SQL flavor
* ({@link java.sql.Array}.
* Note: In case {@code columnType} is of value {@link Types#NULL}, the {@code obj} parameter is expected to be the
* type of null. That is, the {@code obj} will represent the {@link Types} constant for the type of variable whose
* value is null.
* @param columnType the SQL type to create (from {@link Types})
* @param obj the value to use for the column
* @return the created column
* @throws SQLException in case of error
*/
public static Column toColumn(int columnType, Object obj) throws SQLException {
public static Column toColumn(int columnType, @Nonnull Object obj) throws SQLException {
if (columnType != SqlTypeNamesSupport.getSqlTypeCodeFromObject(obj)) {
throw new SQLException("Column element type does not match object type: " + columnType + " / " + obj.getClass().getSimpleName(),
ErrorCode.WRONG_OBJECT_TYPE.getErrorCode());
}

Column.Builder builder = Column.newBuilder();
switch (columnType) {
ScottDugas marked this conversation as resolved.
Show resolved Hide resolved
case Types.BIGINT:
builder = (obj == null) ? builder.clearLong() : builder.setLong((Long)obj);
builder = builder.setLong((Long)obj);
break;
case Types.INTEGER:
builder = (obj == null) ? builder.clearInteger() : builder.setInteger((Integer)obj);
builder = builder.setInteger((Integer)obj);
break;
case Types.BOOLEAN:
builder = (obj == null) ? builder.clearBoolean() : builder.setBoolean((Boolean)obj);
builder = builder.setBoolean((Boolean)obj);
break;
case Types.VARCHAR:
builder = (obj == null) ? builder.clearString() : builder.setString((String)obj);
builder = builder.setString((String)obj);
break;
case Types.BINARY:
builder = (obj == null) ? builder.clearBinary() : builder.setBinary((ByteString)obj);
builder = builder.setBinary((ByteString)obj);
break;
case Types.DOUBLE:
builder = (obj == null) ? builder.clearDouble() : builder.setDouble((Double)obj);
builder = builder.setDouble((Double)obj);
break;
case Types.ARRAY:
builder = (obj == null) ? builder.clearArray() : builder.setArray(toArray((java.sql.Array)obj));
builder = builder.setArray(toArray((java.sql.Array)obj));
break;
case Types.NULL:
builder = builder.setNullType((Integer)obj);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ message Struct {

// Relational Array.
message Array {
ScottDugas marked this conversation as resolved.
Show resolved Hide resolved
repeated Column element = 1;
// The java.sql.Types of the elements of the array. This is somewhat redundant with the type of the
// columns, but it makes it easier to verify correctness.
int32 elementType = 1;
repeated Column element = 2;
int32 elementType = 2;
}

// `Column` represents a dynamically typed column which can be either
Expand All @@ -62,8 +62,8 @@ message Array {
message Column {
// The kind/type of column.
oneof kind {
// Represents a null column. These can be typed, so the value is the java.sql.Types of the parameter
int32 nullType = 1;
// Deprecated
NullColumn null = 1;
// Represents a double column.
double double = 2;

Expand All @@ -78,11 +78,18 @@ message Column {
Struct struct = 7;
Array array = 8;
bytes binary = 9;

float float = 10;
// Represents a null value. These can be typed, so the value is the java.sql.Types of the parameter
int32 nullType = 11;
}
}

// Deprecated
enum NullColumn {
// Null value.
NULL_COLUMN = 0;
}

// `ListColumn` is a wrapper around a repeated field of columns.
message ListColumn {
// Repeated field of dynamically typed columns.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void free() throws SQLException {
* @return the underlying protobuf struct
*/
@Nonnull
public com.apple.foundationdb.relational.jdbc.grpc.v1.column.Array getUnderlying() {
com.apple.foundationdb.relational.jdbc.grpc.v1.column.Array getUnderlying() {
return underlying;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static Parameter ofArray(final Array a) throws SQLException {
for (Object o : arrayElements) {
Parameter p = ofObject(o);
if (p.getJavaSqlTypesCode() != a.getBaseType()) {
throw new SQLException("Array base type does not match element type: " + a.getBaseType() + ":" + p.getJavaSqlTypesCode());
throw new SQLException("Array base type does not match element type: " + a.getBaseType() + ":" + p.getJavaSqlTypesCode(), ErrorCode.ARRAY_ELEMENT_ERROR.getErrorCode());
}
elements.add(p.getParameter());
}
Expand Down
3 changes: 0 additions & 3 deletions yaml-tests/src/test/resources/arrays.yamsql
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ test_block:
- count: 3
-
- query: SELECT * FROM A
- supported_version: !current_version # changes to array protobuf
- result: [{1, [1, 2, 3]}, {2, [2, 3, 4]}, {3, [3, 4, 5]}]
-
- query: INSERT INTO B VALUES (1, ['a', 'b', 'c']), (2, ['b', 'c', 'd']), (3, ['c', 'd', 'e'])
Expand All @@ -61,11 +60,9 @@ test_block:
-
# "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator)
- query: UPDATE A SET X = [11, 12, 13] WHERE PK = 1 RETURNING "new".X
- supported_version: !current_version # changes to array protobuf
- result: [{[11, 12, 13]}]
-
# "new" should not be quoted. TODO ([Post] Fix identifiers case-sensitivity matching in plan generator)
- query: UPDATE D SET X = [(11), (12), (13)] WHERE PK = 1 RETURNING "new".X
- supported_version: !current_version # changes to array protobuf
- result: [{[{F: 11}, {F: 12}, {F: 13}]}]
...
Loading