Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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 @@ -12,6 +12,8 @@

import io.vertx.core.Future;
import io.vertx.core.VertxException;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
Expand All @@ -30,6 +32,8 @@

public class Helper {

private static final Logger logger = LoggerFactory.getLogger(Helper.class);

public static void closeQuietly(AutoCloseable autoCloseable) {
if (autoCloseable != null) {
try {
Expand Down Expand Up @@ -175,39 +179,52 @@ public static Object convertSqlValue(Object value) throws SQLException {
}

private static Object convertOracleJsonValue(OracleJsonValue oracleJson) {
if (oracleJson instanceof OracleJsonObject) {
OracleJsonObject obj = (OracleJsonObject) oracleJson;
Map<String, Object> map = new LinkedHashMap<>(obj.size());
for (Map.Entry<String, OracleJsonValue> entry : obj.entrySet()) {
map.put(entry.getKey(), convertOracleJsonValue(entry.getValue()));
}
return new JsonObject(map);
} else if (oracleJson instanceof OracleJsonArray) {
OracleJsonArray arr = (OracleJsonArray) oracleJson;
List<Object> list = new ArrayList<>(arr.size());
for (OracleJsonValue element : arr) {
list.add(convertOracleJsonValue(element));
switch (oracleJson.getOracleJsonType()) {
case OBJECT: {
OracleJsonObject obj = (OracleJsonObject) oracleJson;
Map<String, Object> map = new LinkedHashMap<>(obj.size());
for (Map.Entry<String, OracleJsonValue> entry : obj.entrySet()) {
map.put(entry.getKey(), convertOracleJsonValue(entry.getValue()));
}
return new JsonObject(map);
}
return new JsonArray(list);
} else if (oracleJson instanceof OracleJsonString) {
return ((OracleJsonString) oracleJson).getString();
} else if (oracleJson instanceof OracleJsonDecimal) {
return ((OracleJsonDecimal) oracleJson).bigDecimalValue();
} else if (oracleJson instanceof OracleJsonDouble) {
return ((OracleJsonDouble) oracleJson).doubleValue();
} else if (oracleJson instanceof OracleJsonFloat) {
return ((OracleJsonFloat) oracleJson).floatValue();
} else {
switch (oracleJson.getOracleJsonType()) {
case TRUE:
return Boolean.TRUE;
case FALSE:
return Boolean.FALSE;
case NULL:
return Tuple.JSON_NULL;
default:
return null;
case ARRAY: {
OracleJsonArray arr = (OracleJsonArray) oracleJson;
List<Object> list = new ArrayList<>(arr.size());
for (OracleJsonValue element : arr) {
list.add(convertOracleJsonValue(element));
}
return new JsonArray(list);
}
case STRING:
return ((OracleJsonString) oracleJson).getString();
case DECIMAL:
return ((OracleJsonDecimal) oracleJson).bigDecimalValue();
case DOUBLE:
return ((OracleJsonDouble) oracleJson).doubleValue();
case FLOAT:
return ((OracleJsonFloat) oracleJson).floatValue();
case TIMESTAMP:
return ((OracleJsonTimestamp) oracleJson).getLocalDateTime();
case TIMESTAMPTZ:
return ((OracleJsonTimestampTZ) oracleJson).getOffsetDateTime();
case DATE:
return ((OracleJsonDate) oracleJson).getLocalDateTime();
case BINARY:
return Buffer.buffer(((OracleJsonBinary) oracleJson).getBytes());
case INTERVALDS:
return ((OracleJsonIntervalDS) oracleJson).getDuration();
case INTERVALYM:
return ((OracleJsonIntervalYM) oracleJson).getPeriod();
case TRUE:
return Boolean.TRUE;
case FALSE:
return Boolean.FALSE;
case NULL:
return Tuple.JSON_NULL;
default:
logger.warn("Unsupported Oracle JSON type: " + oracleJson.getOracleJsonType());
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.vertx.core.buffer.Buffer;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.oracleclient.OraclePrepareOptions;
Expand All @@ -30,9 +31,10 @@
import oracle.jdbc.OraclePreparedStatement;
import oracle.sql.TIMESTAMPTZ;
import oracle.sql.json.OracleJsonFactory;
import oracle.sql.json.OracleJsonGenerator;
import oracle.sql.json.OracleJsonValue;

import java.io.StringReader;
import java.io.ByteArrayOutputStream;
import java.sql.*;
import java.time.Instant;
import java.time.LocalDate;
Expand Down Expand Up @@ -179,16 +181,28 @@ protected Object adaptType(Connection conn, Object value) throws SQLException {
Buffer buffer = (Buffer) value;
return buffer.getBytes();
} else if (value instanceof JsonObject) {
return JSON_FACTORY.createJsonTextValue(new StringReader(((JsonObject) value).encode()));
return encodeJsonBinary(value);
} else if (value instanceof JsonArray) {
return JSON_FACTORY.createJsonTextValue(new StringReader(((JsonArray) value).encode()));
} else if (value == Tuple.JSON_NULL) {
return JSON_FACTORY.createNull();
return encodeJsonBinary(value);
} else {
if (value == Tuple.JSON_NULL) {
return JSON_FACTORY.createNull();
}
}

return value;
}

private static OracleJsonValue encodeJsonBinary(Object value) {
ByteArrayOutputStream textBuf = new ByteArrayOutputStream();
Json.encodeTo(value, textBuf);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (OracleJsonGenerator gen = JSON_FACTORY.createJsonBinaryGenerator(out)) {
gen.write(JSON_FACTORY.createJsonTextValue(new java.io.ByteArrayInputStream(textBuf.toByteArray())));
}
return JSON_FACTORY.createJsonBinaryValue(new java.io.ByteArrayInputStream(out.toByteArray()));
}

protected abstract Future<Boolean> doExecute(OraclePreparedStatement ps, boolean returnAutoGeneratedKeys);

protected OracleResponse<R> decode(Statement statement, boolean returnedResultSet, boolean returnedKeys) throws SQLException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import tests.oracleclient.junit.OracleRule;

import java.math.BigDecimal;
import java.time.*;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -301,4 +302,84 @@ public void testInsertJsonBooleanWithSqlWorkaround(TestContext ctx) {
ctx.assertEquals(Boolean.TRUE, rows.iterator().next().getJson(0));
}));
}

@Test
public void testDecodeOsonTimestamp(TestContext ctx) {
pool.query("INSERT INTO json_test (id, data) VALUES (50, JSON_OBJECT('ts' VALUE TIMESTAMP '2023-09-21 10:00:00' RETURNING JSON))")
.execute()
.compose(v -> pool.preparedQuery("SELECT data FROM json_test WHERE id = ?").execute(Tuple.of(50)))
.onComplete(ctx.asyncAssertSuccess(rows -> {
ctx.assertEquals(1, rows.size());
Row row = rows.iterator().next();
JsonObject obj = row.getJsonObject(0);
ctx.assertNotNull(obj);
Object ts = obj.getValue("ts");
ctx.assertNotNull(ts);
ctx.assertTrue(ts instanceof LocalDateTime, "Expected LocalDateTime but got: " + (ts == null ? "null" : ts.getClass().getName()));
ctx.assertEquals(LocalDateTime.of(2023, 9, 21, 10, 0, 0), ts);
}));
}

@Test
public void testDecodeOsonTimestampTZ(TestContext ctx) {
pool.query("INSERT INTO json_test (id, data) VALUES (51, JSON_OBJECT('ts' VALUE TIMESTAMP '2023-09-21 10:00:00 +02:00' RETURNING JSON))")
.execute()
.compose(v -> pool.preparedQuery("SELECT data FROM json_test WHERE id = ?").execute(Tuple.of(51)))
.onComplete(ctx.asyncAssertSuccess(rows -> {
ctx.assertEquals(1, rows.size());
Row row = rows.iterator().next();
JsonObject obj = row.getJsonObject(0);
ctx.assertNotNull(obj);
Object ts = obj.getValue("ts");
ctx.assertNotNull(ts);
ctx.assertTrue(ts instanceof OffsetDateTime, "Expected OffsetDateTime or LocalDateTime but got: " + (ts == null ? "null" : ts.getClass().getName()));
}));
}

@Test
public void testDecodeOsonDate(TestContext ctx) {
pool.query("INSERT INTO json_test (id, data) VALUES (52, JSON_OBJECT('d' VALUE DATE '2023-09-21' RETURNING JSON))")
.execute()
.compose(v -> pool.preparedQuery("SELECT data FROM json_test WHERE id = ?").execute(Tuple.of(52)))
.onComplete(ctx.asyncAssertSuccess(rows -> {
ctx.assertEquals(1, rows.size());
Row row = rows.iterator().next();
JsonObject obj = row.getJsonObject(0);
ctx.assertNotNull(obj);
Object d = obj.getValue("d");
ctx.assertNotNull(d);
ctx.assertTrue(d instanceof LocalDateTime, "Expected LocalDateTime but got: " + (d == null ? "null" : d.getClass().getName()));
}));
}

@Test
public void testDecodeOsonIntervalYearToMonth(TestContext ctx) {
pool.query("INSERT INTO json_test (id, data) VALUES (53, JSON_OBJECT('iv' VALUE INTERVAL '2-3' YEAR TO MONTH RETURNING JSON))")
.execute()
.compose(v -> pool.preparedQuery("SELECT data FROM json_test WHERE id = ?").execute(Tuple.of(53)))
.onComplete(ctx.asyncAssertSuccess(rows -> {
ctx.assertEquals(1, rows.size());
JsonObject obj = rows.iterator().next().getJsonObject(0);
ctx.assertNotNull(obj);
Object iv = obj.getValue("iv");
ctx.assertTrue(iv instanceof Period, "Expected Period but got: " + (iv == null ? "null" : iv.getClass().getName()));
ctx.assertEquals(Period.of(2, 3, 0), iv);
}));
}

@Test
public void testDecodeOsonIntervalDayToSecond(TestContext ctx) {
pool.query("INSERT INTO json_test (id, data) VALUES (54, JSON_OBJECT('iv' VALUE INTERVAL '1 02:03:04.5' DAY TO SECOND RETURNING JSON))")
.execute()
.compose(v -> pool.preparedQuery("SELECT data FROM json_test WHERE id = ?").execute(Tuple.of(54)))
.onComplete(ctx.asyncAssertSuccess(rows -> {
ctx.assertEquals(1, rows.size());
JsonObject obj = rows.iterator().next().getJsonObject(0);
ctx.assertNotNull(obj);
Object iv = obj.getValue("iv");
ctx.assertTrue(iv instanceof Duration, "Expected Duration but got: " + (iv == null ? "null" : iv.getClass().getName()));
Duration expected = Duration.ofDays(1).plusHours(2).plusMinutes(3).plusSeconds(4).plusMillis(500);
ctx.assertEquals(expected, iv);
}));
}
}
Loading