Skip to content

Commit 98f58ff

Browse files
Resolved comments
1 parent d8f92c3 commit 98f58ff

2 files changed

Lines changed: 306 additions & 30 deletions

File tree

oracle-plugin/src/main/java/io/cdap/plugin/oracle/OracleSourceDBRecord.java

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
import java.time.ZoneOffset;
4949
import java.time.ZonedDateTime;
5050
import java.util.List;
51+
import java.util.Map;
52+
import java.util.TreeMap;
5153

5254
/**
5355
* Oracle Source implementation {@link org.apache.hadoop.mapreduce.lib.db.DBWritable} and
@@ -151,24 +153,8 @@ protected void setFieldValue(StructuredRecord.Builder recordBuilder, Schema.Fiel
151153
recordBuilder.set(field.getName(), attrValue.toString());
152154
return;
153155
}
154-
if ("oracle.sql.NCLOB".equals(attrClassName)) {
155-
recordBuilder.set(field.getName(), attrValue.toString());
156-
return;
157-
}
158156
if (attrValue instanceof BigDecimal) {
159-
BigDecimal bigDecimal = (BigDecimal) attrValue;
160-
if (Schema.LogicalType.DECIMAL.equals(fieldSchema.getLogicalType())) {
161-
recordBuilder.setDecimal(field.getName(), bigDecimal.setScale(fieldSchema.getScale(), RoundingMode.HALF_UP));
162-
return;
163-
}
164-
switch (fieldSchema.getType()) {
165-
case DOUBLE: recordBuilder.set(field.getName(), bigDecimal.doubleValue()); break;
166-
case FLOAT: recordBuilder.set(field.getName(), bigDecimal.floatValue()); break;
167-
case INT: recordBuilder.set(field.getName(), bigDecimal.intValue()); break;
168-
case LONG: recordBuilder.set(field.getName(), bigDecimal.longValue()); break;
169-
case STRING: recordBuilder.set(field.getName(), bigDecimal.toString()); break;
170-
default: recordBuilder.set(field.getName(), bigDecimal);
171-
}
157+
populateDecimalValue(attrValue, fieldSchema, recordBuilder, field);
172158
return;
173159
}
174160

@@ -262,6 +248,34 @@ protected void writeNonNullToDB(PreparedStatement stmt, Schema fieldSchema,
262248
}
263249
}
264250

251+
private void populateDecimalValue(Object attrValue, Schema fieldSchema,
252+
StructuredRecord.Builder recordBuilder, Schema.Field field) {
253+
BigDecimal bigDecimal = (BigDecimal) attrValue;
254+
if (Schema.LogicalType.DECIMAL.equals(fieldSchema.getLogicalType())) {
255+
recordBuilder.setDecimal(field.getName(), bigDecimal.setScale(fieldSchema.getScale(), RoundingMode.HALF_UP));
256+
return;
257+
}
258+
switch (fieldSchema.getType()) {
259+
case DOUBLE:
260+
recordBuilder.set(field.getName(), bigDecimal.doubleValue());
261+
break;
262+
case FLOAT:
263+
recordBuilder.set(field.getName(), bigDecimal.floatValue());
264+
break;
265+
case INT:
266+
recordBuilder.set(field.getName(), bigDecimal.intValue());
267+
break;
268+
case LONG:
269+
recordBuilder.set(field.getName(), bigDecimal.longValue());
270+
break;
271+
case STRING:
272+
recordBuilder.set(field.getName(), bigDecimal.toString());
273+
break;
274+
default:
275+
recordBuilder.set(field.getName(), bigDecimal);
276+
}
277+
}
278+
265279
/**
266280
* Creates an instance of 'oracle.sql.TIMESTAMPTZ' which corresponds to the specified timestamp with time zone string.
267281
* @param connection sql connection.
@@ -474,20 +488,57 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil
474488
}
475489
}
476490

491+
/**
492+
* Converts a JDBC {@link Struct} into a {@link StructuredRecord} based on the provided schema.
493+
*
494+
* @param struct the SQL structured type containing the source data attributes
495+
* @param schema the target record schema defining the fields to map
496+
* @return a populated {@code StructuredRecord} instance
497+
* @throws SQLException if an error occurs reading the struct attributes or metadata
498+
*/
477499
protected StructuredRecord convertStructToRecord(Struct struct, Schema schema)
478500
throws SQLException {
479-
Object[] attributes = struct.getAttributes();
480-
List<Schema.Field> fields = schema.getFields();
501+
Map<String, Object> attributeMap = getAttributeMap(struct, schema);
481502
StructuredRecord.Builder builder = StructuredRecord.builder(schema);
482503

483-
for (int index = 0; index < attributes.length; index++) {
484-
Schema.Field field = fields.get(index);
485-
Object attrValue = attributes[index];
504+
for (Schema.Field field : schema.getFields()) {
505+
Object attrValue = attributeMap.get(field.getName());
486506
setFieldValue(builder, field, attrValue);
487507
}
488508
return builder.build();
489509
}
490510

511+
/**
512+
* Extracts attributes from a {@link Struct} into a case-insensitive map indexed by column name.
513+
* Uses reflection to extract underlying metadata (e.g., from Oracle StructDescriptor).
514+
*
515+
* @param struct the source SQL structured type
516+
* @param schema the target schema used for context in error messages
517+
* @return a case-insensitive {@code Map} linking column names to their attribute values
518+
* @throws SQLException if metadata extraction fails or driver-specific methods are inaccessible
519+
*/
520+
private Map<String, Object> getAttributeMap(Struct struct, Schema schema) throws SQLException {
521+
Map<String, Object> attributeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
522+
Object[] attributes = struct.getAttributes();
523+
524+
try {
525+
Object descriptor = struct.getClass().getMethod("getDescriptor").invoke(struct);
526+
ResultSetMetaData metaData =
527+
(ResultSetMetaData) descriptor.getClass().getMethod("getMetaData").invoke(descriptor);
528+
for (int i = 1; i <= metaData.getColumnCount() && (i - 1) < attributes.length; i++) {
529+
attributeMap.put(metaData.getColumnName(i), attributes[i - 1]);
530+
}
531+
} catch (SQLException | NoSuchMethodException e) {
532+
throw new SQLException(String.format("Failed to retrieve attribute metadata for Oracle STRUCT schema '%s': %s",
533+
schema.getRecordName(), e.getMessage()), e);
534+
} catch (InvocationTargetException | IllegalAccessException e) {
535+
throw new SQLException(String.format("Unable to retrieve attribute metadata for Oracle STRUCT schema '%s'. "
536+
+ "Ensure the Oracle JDBC driver supports JDBC StructDescriptor metadata.",
537+
schema.getRecordName()), e);
538+
}
539+
return attributeMap;
540+
}
541+
491542
/**
492543
* Get the scale set in Non-nullable schema associated with the schema
493544
* */

0 commit comments

Comments
 (0)