|
30 | 30 | import java.lang.reflect.InvocationTargetException; |
31 | 31 | import java.math.BigDecimal; |
32 | 32 | import java.nio.ByteBuffer; |
| 33 | +import java.sql.Blob; |
| 34 | +import java.sql.Clob; |
33 | 35 | import java.sql.Connection; |
34 | 36 | import java.sql.PreparedStatement; |
35 | 37 | import java.sql.ResultSet; |
@@ -258,6 +260,31 @@ private byte[] getBfileBytes(ResultSet resultSet, String columnName) throws SQLE |
258 | 260 | } |
259 | 261 | } |
260 | 262 |
|
| 263 | + private byte[] getBfileBytes(Object bfile) throws SQLException { |
| 264 | + if (bfile == null) { |
| 265 | + return null; |
| 266 | + } |
| 267 | + try { |
| 268 | + ClassLoader classLoader = bfile.getClass().getClassLoader(); |
| 269 | + Class<?> oracleBfileClass = classLoader.loadClass("oracle.jdbc.OracleBfile"); |
| 270 | + boolean isFileExist = (boolean) oracleBfileClass.getMethod("fileExists").invoke(bfile); |
| 271 | + if (!isFileExist) { |
| 272 | + return null; |
| 273 | + } |
| 274 | + |
| 275 | + oracleBfileClass.getMethod("openFile").invoke(bfile); |
| 276 | + InputStream binaryStream = (InputStream) oracleBfileClass.getMethod("getBinaryStream").invoke(bfile); |
| 277 | + byte[] bytes = ByteStreams.toByteArray(binaryStream); |
| 278 | + oracleBfileClass.getMethod("closeFile").invoke(bfile); |
| 279 | + return bytes; |
| 280 | + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { |
| 281 | + throw new InvalidStageException("Field is of type 'BFILE', which is not supported " + |
| 282 | + "with this version of the JDBC driver.", e); |
| 283 | + } catch (IOException e) { |
| 284 | + throw new InvalidStageException("Error reading the contents of the BFILE.", e); |
| 285 | + } |
| 286 | + } |
| 287 | + |
261 | 288 | private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Builder recordBuilder, Schema.Field field, |
262 | 289 | int columnIndex, int sqlType, int precision, int scale) |
263 | 290 | throws SQLException { |
@@ -343,10 +370,9 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil |
343 | 370 | recordBuilder.set(field.getName(), resultSet.getBytes(columnIndex)); |
344 | 371 | break; |
345 | 372 | case Types.STRUCT: |
346 | | - java.sql.Struct structValue = (java.sql.Struct) resultSet.getObject(columnIndex); |
| 373 | + Struct structValue = (Struct) resultSet.getObject(columnIndex); |
347 | 374 | if (structValue != null) { |
348 | | - recordBuilder.set(field.getName(), convertStructToRecord(structValue, nonNullSchema, |
349 | | - resultSet.getStatement().getConnection())); |
| 375 | + recordBuilder.set(field.getName(), convertStructToRecord(structValue, nonNullSchema, resultSet)); |
350 | 376 | } |
351 | 377 | break; |
352 | 378 | case Types.DECIMAL: |
@@ -379,39 +405,80 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil |
379 | 405 | } |
380 | 406 | } |
381 | 407 |
|
382 | | - private StructuredRecord convertStructToRecord(java.sql.Struct struct, Schema schema, |
383 | | - Connection connection) throws SQLException { |
| 408 | + private StructuredRecord convertStructToRecord(Struct struct, Schema schema, ResultSet resultSet) |
| 409 | + throws SQLException { |
384 | 410 | Object[] attributes = struct.getAttributes(); |
385 | 411 | List<Schema.Field> fields = schema.getFields(); |
386 | 412 | StructuredRecord.Builder builder = StructuredRecord.builder(schema); |
387 | 413 |
|
388 | | - for (int i = 0; i < fields.size() && i < attributes.length; i++) { |
389 | | - Schema.Field field = fields.get(i); |
390 | | - Object attrValue = attributes[i]; |
| 414 | + for (int index = 0; index < attributes.length; index++) { |
| 415 | + Schema.Field field = fields.get(index); |
| 416 | + Object attrValue = attributes[index]; |
391 | 417 |
|
392 | 418 | if (attrValue == null) { |
393 | 419 | builder.set(field.getName(), null); |
394 | 420 | continue; |
395 | 421 | } |
396 | | - |
397 | | - Schema fieldSchema = field.getSchema().isNullable() |
398 | | - ? field.getSchema().getNonNullable() : field.getSchema(); |
399 | | - |
| 422 | + // If it is an internal nested STRUCT, recurse down |
400 | 423 | if (attrValue instanceof Struct) { |
401 | | - builder.set(field.getName(), convertStructToRecord((Struct) attrValue, fieldSchema, connection)); |
402 | | - } else if (attrValue instanceof java.sql.Date) { |
403 | | - builder.setDate(field.getName(), ((java.sql.Date) attrValue).toLocalDate()); |
404 | | - } else if (attrValue instanceof java.sql.Time) { |
405 | | - builder.setTime(field.getName(), ((java.sql.Time) attrValue).toLocalTime()); |
| 424 | + Schema fieldSchema = field.getSchema().isNullable() ? field.getSchema().getNonNullable() : field.getSchema(); |
| 425 | + builder.set(field.getName(), convertStructToRecord((Struct) attrValue, fieldSchema, resultSet)); |
| 426 | + continue; |
| 427 | + } |
| 428 | + |
| 429 | + String attrClassName = attrValue.getClass().getName(); |
| 430 | + Schema fieldSchema = field.getSchema().isNullable() ? field.getSchema().getNonNullable() : field.getSchema(); |
| 431 | + if (attrValue instanceof BigDecimal) { |
| 432 | + if (Schema.LogicalType.DECIMAL.equals(fieldSchema.getLogicalType())) { |
| 433 | + builder.setDecimal(field.getName(), ((BigDecimal) attrValue).setScale(getScale(field.getSchema()), |
| 434 | + java.math.RoundingMode.HALF_UP)); |
| 435 | + } else if (Schema.Type.DOUBLE.equals(fieldSchema.getType())) { |
| 436 | + builder.set(field.getName(), ((BigDecimal) attrValue).doubleValue()); |
| 437 | + } else if (Schema.Type.FLOAT.equals(fieldSchema.getType())) { |
| 438 | + builder.set(field.getName(), ((BigDecimal) attrValue).floatValue()); |
| 439 | + } else if (Schema.Type.INT.equals(fieldSchema.getType())) { |
| 440 | + builder.set(field.getName(), ((BigDecimal) attrValue).intValue()); |
| 441 | + } else if (Schema.Type.LONG.equals(fieldSchema.getType())) { |
| 442 | + builder.set(field.getName(), ((BigDecimal) attrValue).longValue()); |
| 443 | + } else { |
| 444 | + builder.set(field.getName(), attrValue.toString()); |
| 445 | + } |
406 | 446 | } else if (attrValue instanceof Timestamp) { |
| 447 | + Timestamp timestamp = (Timestamp) attrValue; |
407 | 448 | if (Schema.LogicalType.DATETIME.equals(fieldSchema.getLogicalType())) { |
408 | | - builder.setDateTime(field.getName(), ((Timestamp) attrValue).toLocalDateTime()); |
| 449 | + builder.setDateTime(field.getName(), timestamp.toLocalDateTime()); |
| 450 | + } else if (Schema.LogicalType.DATE.equals(fieldSchema.getLogicalType())) { |
| 451 | + builder.setDate(field.getName(), timestamp.toLocalDateTime().toLocalDate()); |
| 452 | + } else { |
| 453 | + builder.set(field.getName(), attrValue.toString()); |
| 454 | + } |
| 455 | + } else if (attrValue instanceof OffsetDateTime || attrValue instanceof ZonedDateTime) { |
| 456 | + ZonedDateTime zonedDateTime = (attrValue instanceof OffsetDateTime) |
| 457 | + ? ((OffsetDateTime) attrValue).atZoneSameInstant(ZoneId.of("UTC")) |
| 458 | + : ((ZonedDateTime) attrValue).withZoneSameInstant(ZoneId.of("UTC")); |
| 459 | + if (fieldSchema.getLogicalType() != null && |
| 460 | + (Schema.LogicalType.TIMESTAMP_MICROS.equals(fieldSchema.getLogicalType()) || |
| 461 | + Schema.LogicalType.TIMESTAMP_MILLIS.equals(fieldSchema.getLogicalType()))) { |
| 462 | + builder.setTimestamp(field.getName(), zonedDateTime); |
| 463 | + } else if (Schema.Type.LONG.equals(fieldSchema.getType())) { |
| 464 | + builder.set(field.getName(), zonedDateTime.toInstant().toEpochMilli()); |
409 | 465 | } else { |
410 | | - builder.setTimestamp(field.getName(), |
411 | | - ((Timestamp) attrValue).toInstant().atZone(java.time.ZoneId.of("UTC"))); |
| 466 | + builder.set(field.getName(), zonedDateTime.toString()); |
412 | 467 | } |
413 | | - } else if (attrValue instanceof BigDecimal) { |
414 | | - builder.setDecimal(field.getName(), (BigDecimal) attrValue); |
| 468 | + } else if (attrValue instanceof Clob) { |
| 469 | + Clob clob = (Clob) attrValue; |
| 470 | + builder.set(field.getName(), clob.getSubString(1, (int) clob.length())); |
| 471 | + } else if (attrValue instanceof Blob) { |
| 472 | + Blob blob = (Blob) attrValue; |
| 473 | + builder.set(field.getName(), blob.getBytes(1, (int) blob.length())); |
| 474 | + } else if ("oracle.jdbc.OracleBfile".equals(attrClassName)) { |
| 475 | + builder.set(field.getName(), getBfileBytes(attrValue)); |
| 476 | + } else if (attrValue instanceof byte[]) { |
| 477 | + byte[] bytesValue = (byte[]) attrValue; |
| 478 | + builder.set(field.getName(), bytesValue); |
| 479 | + } else if ("oracle.sql.INTERVALDS".equals(attrClassName) |
| 480 | + || "oracle.sql.INTERVALYM".equals(attrClassName)) { |
| 481 | + builder.set(field.getName(), attrValue.toString()); |
415 | 482 | } else { |
416 | 483 | builder.set(field.getName(), attrValue); |
417 | 484 | } |
|
0 commit comments