1919import com .google .common .io .ByteStreams ;
2020import io .cdap .cdap .api .common .Bytes ;
2121import io .cdap .cdap .api .data .format .StructuredRecord ;
22+ import io .cdap .cdap .api .data .format .StructuredRecord .Builder ;
2223import io .cdap .cdap .api .data .schema .Schema ;
24+ import io .cdap .cdap .api .data .schema .Schema .Field ;
25+ import io .cdap .cdap .api .data .schema .Schema .LogicalType ;
26+ import io .cdap .cdap .api .data .schema .Schema .Type ;
2327import io .cdap .cdap .etl .api .validation .InvalidStageException ;
2428import io .cdap .plugin .db .ColumnType ;
2529import io .cdap .plugin .db .DBRecord ;
2630import io .cdap .plugin .db .SchemaReader ;
31+ import org .apache .hadoop .io .Writable ;
32+ import org .apache .hadoop .mapreduce .lib .db .DBWritable ;
2733
2834import java .io .IOException ;
2935import java .io .InputStream ;
4551import java .util .List ;
4652
4753/**
48- * Oracle Source implementation {@link org.apache.hadoop.mapreduce.lib.db. DBWritable} and
49- * {@link org.apache.hadoop.io. Writable}.
54+ * Oracle Source implementation {@link DBWritable} and
55+ * {@link Writable}.
5056 */
5157public class OracleSourceDBRecord extends DBRecord {
5258
@@ -77,11 +83,11 @@ protected SchemaReader getSchemaReader() {
7783 public void readFields (ResultSet resultSet ) throws SQLException {
7884 Schema schema = getSchema ();
7985 ResultSetMetaData metadata = resultSet .getMetaData ();
80- StructuredRecord . Builder recordBuilder = StructuredRecord .builder (schema );
86+ Builder recordBuilder = StructuredRecord .builder (schema );
8187
8288 // All LONG or LONG RAW columns have to be retrieved from the ResultSet prior to all the other columns.
8389 // Otherwise, we will face java.sql.SQLException: Stream has already been closed
84- for (Schema . Field field : schema .getFields ()) {
90+ for (Field field : schema .getFields ()) {
8591 // Index of a field in the schema may not be same in the ResultSet,
8692 // hence find the field by name in the given resultSet
8793 int columnIndex = resultSet .findColumn (field .getName ());
@@ -91,7 +97,7 @@ public void readFields(ResultSet resultSet) throws SQLException {
9197 }
9298
9399 // Read fields of other types
94- for (Schema . Field field : schema .getFields ()) {
100+ for (Field field : schema .getFields ()) {
95101 // Index of a field in the schema may not be same in the ResultSet,
96102 // hence find the field by name in the given resultSet
97103 int columnIndex = resultSet .findColumn (field .getName ());
@@ -104,7 +110,7 @@ record = recordBuilder.build();
104110 }
105111
106112 @ Override
107- protected void handleField (ResultSet resultSet , StructuredRecord . Builder recordBuilder , Schema . Field field ,
113+ protected void handleField (ResultSet resultSet , Builder recordBuilder , Field field ,
108114 int columnIndex , int sqlType , int sqlPrecision , int sqlScale ) throws SQLException {
109115 if (OracleSourceSchemaReader .ORACLE_TYPES .contains (sqlType ) || sqlType == Types .NCLOB ) {
110116 handleOracleSpecificType (resultSet , recordBuilder , field , columnIndex , sqlType , sqlPrecision , sqlScale );
@@ -116,8 +122,21 @@ protected void handleField(ResultSet resultSet, StructuredRecord.Builder recordB
116122 @ Override
117123 protected void writeNonNullToDB (PreparedStatement stmt , Schema fieldSchema ,
118124 String fieldName , int fieldIndex ) throws SQLException {
119- int sqlType = columnTypes .get (fieldIndex ).getType ();
120125 int sqlIndex = fieldIndex + 1 ;
126+ int sqlType = Types .OTHER ;
127+ boolean isFieldTypeFound = false ;
128+ // avoid OOB exception in case of mismatch between columnTypes and record schema fields
129+ for (ColumnType columnType : columnTypes ) {
130+ if (columnType .getName ().equals (fieldName )) {
131+ sqlType = columnType .getType ();
132+ isFieldTypeFound = true ;
133+ break ;
134+ }
135+ }
136+
137+ if (!isFieldTypeFound ) {
138+ throw new IllegalArgumentException ("Unable to find the column type for field '" + fieldName + "'." );
139+ }
121140
122141 // TIMESTAMP and TIMESTAMPTZ types needs to be handled using the specific oracle types to ensure that the data
123142 // inserted matches with the provided value. As Oracle driver internally alters the values provided
@@ -126,7 +145,7 @@ protected void writeNonNullToDB(PreparedStatement stmt, Schema fieldSchema,
126145 // More details here : https://docs.oracle.com/cd/E13222_01/wls/docs91/jdbc_drivers/oracle.html
127146 // Handle the case when TimestampTZ type is set to CDAP String type or Timestamp type
128147 if (sqlType == OracleSourceSchemaReader .TIMESTAMP_TZ ) {
129- if (Schema . Type .STRING .equals (fieldSchema .getType ())) {
148+ if (Type .STRING .equals (fieldSchema .getType ())) {
130149 // Deprecated: Handle the case when the TimestampTZ is mapped to CDAP String type
131150 String timestampString = record .get (fieldName );
132151 Object timestampTZ = createOracleTimestampWithTimeZone (stmt .getConnection (), timestampString );
@@ -140,21 +159,21 @@ protected void writeNonNullToDB(PreparedStatement stmt, Schema fieldSchema,
140159 stmt .setObject (sqlIndex , timestampWithTimeZone );
141160 }
142161 } else if (sqlType == OracleSourceSchemaReader .TIMESTAMP_LTZ ) {
143- if (Schema . LogicalType .TIMESTAMP_MICROS .equals (fieldSchema .getLogicalType ())) {
162+ if (LogicalType .TIMESTAMP_MICROS .equals (fieldSchema .getLogicalType ())) {
144163 // Deprecated: Handle the case when the TimestampLTZ is mapped to CDAP Timestamp type
145164 ZonedDateTime timestamp = record .getTimestamp (fieldName );
146165 String timestampString = Timestamp .valueOf (timestamp .toLocalDateTime ()).toString ();
147166 Object timestampWithTimeZone = createOracleTimestampWithLocalTimeZone (stmt .getConnection (), timestampString );
148167 stmt .setObject (sqlIndex , timestampWithTimeZone );
149- } else if (Schema . LogicalType .DATETIME .equals (fieldSchema .getLogicalType ())) {
168+ } else if (LogicalType .DATETIME .equals (fieldSchema .getLogicalType ())) {
150169 // Handle the case when the TimestampLTZ is mapped to CDAP Datetime type
151170 LocalDateTime localDateTime = record .getDateTime (fieldName );
152171 String timestampString = Timestamp .valueOf (localDateTime ).toString ();
153172 Object timestampWithTimeZone = createOracleTimestampWithLocalTimeZone (stmt .getConnection (), timestampString );
154173 stmt .setObject (sqlIndex , timestampWithTimeZone );
155174 }
156175 } else if (sqlType == Types .TIMESTAMP ) {
157- if (Schema . LogicalType .DATETIME .equals (fieldSchema .getLogicalType ())) {
176+ if (LogicalType .DATETIME .equals (fieldSchema .getLogicalType ())) {
158177 // Handle the case when Timestamp is mapped to CDAP Datetime type.
159178 LocalDateTime localDateTime = record .getDateTime (fieldName );
160179 String timestampString = Timestamp .valueOf (localDateTime ).toString ();
@@ -257,7 +276,7 @@ private byte[] getBfileBytes(ResultSet resultSet, String columnName) throws SQLE
257276 }
258277 }
259278
260- private void handleOracleSpecificType (ResultSet resultSet , StructuredRecord . Builder recordBuilder , Schema . Field field ,
279+ private void handleOracleSpecificType (ResultSet resultSet , Builder recordBuilder , Field field ,
261280 int columnIndex , int sqlType , int precision , int scale )
262281 throws SQLException {
263282 Schema nonNullSchema = field .getSchema ().isNullable () ?
@@ -270,7 +289,7 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil
270289 recordBuilder .set (field .getName (), resultSet .getString (columnIndex ));
271290 break ;
272291 case OracleSourceSchemaReader .TIMESTAMP_TZ :
273- if (Schema . Type .STRING .equals (nonNullSchema .getType ())) {
292+ if (Type .STRING .equals (nonNullSchema .getType ())) {
274293 recordBuilder .set (field .getName (), resultSet .getString (columnIndex ));
275294 } else {
276295 // In case of TimestampTZ datatype the getTimestamp(index, Calendar) method call does not
@@ -298,7 +317,7 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil
298317 case Types .TIMESTAMP :
299318 // Since Oracle Timestamp type does not have any timezone information, it should be converted into the
300319 // CDAP Datetime type.
301- if (Schema . LogicalType .DATETIME .equals (nonNullSchema .getLogicalType ())) {
320+ if (LogicalType .DATETIME .equals (nonNullSchema .getLogicalType ())) {
302321 Timestamp timestamp = resultSet .getTimestamp (columnIndex );
303322 if (timestamp != null ) {
304323 recordBuilder .setDateTime (field .getName (), timestamp .toLocalDateTime ());
@@ -315,7 +334,7 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil
315334 // super.setField sets this '0000-12-31 09:00:00.000Z[UTC]' in the recordBuilder which is incorrect and the
316335 // correct value should be '0001-01-01 09:00:00.000Z[UTC]'.
317336 Object timeStampObj = resultSet .getObject (columnIndex );
318- if (Schema . LogicalType .DATETIME .equals (nonNullSchema .getLogicalType ())) {
337+ if (LogicalType .DATETIME .equals (nonNullSchema .getLogicalType ())) {
319338 Timestamp timestampLTZ = resultSet .getTimestamp (columnIndex );
320339 if (timestampLTZ != null ) {
321340 recordBuilder .setDateTime (field .getName (),
@@ -351,7 +370,7 @@ private void handleOracleSpecificType(ResultSet resultSet, StructuredRecord.Buil
351370 if (precision == 0 ) {
352371 Schema nonNullableSchema = field .getSchema ().isNullable () ?
353372 field .getSchema ().getNonNullable () : field .getSchema ();
354- if (Schema . LogicalType .DECIMAL .equals (nonNullableSchema .getLogicalType ())) {
373+ if (LogicalType .DECIMAL .equals (nonNullableSchema .getLogicalType ())) {
355374 // Handle the field using the schema set in the output schema
356375 BigDecimal decimal = resultSet .getBigDecimal (columnIndex , getScale (field .getSchema ()));
357376 recordBuilder .setDecimal (field .getName (), decimal );
@@ -382,8 +401,8 @@ private boolean isLongOrLongRaw(int columnType) {
382401 return columnType == OracleSourceSchemaReader .LONG || columnType == OracleSourceSchemaReader .LONG_RAW ;
383402 }
384403
385- private void readField (int columnIndex , ResultSetMetaData metadata , ResultSet resultSet , Schema . Field field ,
386- StructuredRecord . Builder recordBuilder ) throws SQLException {
404+ private void readField (int columnIndex , ResultSetMetaData metadata , ResultSet resultSet , Field field ,
405+ Builder recordBuilder ) throws SQLException {
387406 int sqlType = metadata .getColumnType (columnIndex );
388407 int sqlPrecision = metadata .getPrecision (columnIndex );
389408 int sqlScale = metadata .getScale (columnIndex );
0 commit comments