28
28
import org .apache .kafka .connect .data .Schema ;
29
29
import org .apache .kafka .connect .data .SchemaBuilder ;
30
30
import org .apache .kafka .connect .data .Struct ;
31
+ import org .apache .kafka .connect .data .Values ;
31
32
import org .apache .kafka .connect .errors .DataException ;
32
33
import org .apache .kafka .connect .transforms .util .SchemaUtil ;
33
34
import org .apache .kafka .connect .transforms .util .SimpleConfig ;
@@ -81,9 +82,16 @@ public String toString() {
81
82
82
83
private static final String PURPOSE = "cast types" ;
83
84
84
- private static final Set <Schema .Type > SUPPORTED_CAST_TYPES = EnumSet .of (
85
+ private static final Set <Schema .Type > SUPPORTED_CAST_INPUT_TYPES = EnumSet .of (
85
86
Schema .Type .INT8 , Schema .Type .INT16 , Schema .Type .INT32 , Schema .Type .INT64 ,
86
- Schema .Type .FLOAT32 , Schema .Type .FLOAT64 , Schema .Type .BOOLEAN , Schema .Type .STRING
87
+ Schema .Type .FLOAT32 , Schema .Type .FLOAT64 , Schema .Type .BOOLEAN ,
88
+ Schema .Type .STRING , Schema .Type .BYTES
89
+ );
90
+
91
+ private static final Set <Schema .Type > SUPPORTED_CAST_OUTPUT_TYPES = EnumSet .of (
92
+ Schema .Type .INT8 , Schema .Type .INT16 , Schema .Type .INT32 , Schema .Type .INT64 ,
93
+ Schema .Type .FLOAT32 , Schema .Type .FLOAT64 , Schema .Type .BOOLEAN ,
94
+ Schema .Type .STRING
87
95
);
88
96
89
97
// As a special case for casting the entire value (e.g. the incoming key is a int64 but you know it could be an
@@ -123,14 +131,14 @@ public void close() {
123
131
124
132
private R applySchemaless (R record ) {
125
133
if (wholeValueCastType != null ) {
126
- return newRecord (record , null , castValueToType (operatingValue (record ), wholeValueCastType ));
134
+ return newRecord (record , null , castValueToType (null , operatingValue (record ), wholeValueCastType ));
127
135
}
128
136
129
137
final Map <String , Object > value = requireMap (operatingValue (record ), PURPOSE );
130
138
final HashMap <String , Object > updatedValue = new HashMap <>(value );
131
139
for (Map .Entry <String , Schema .Type > fieldSpec : casts .entrySet ()) {
132
140
String field = fieldSpec .getKey ();
133
- updatedValue .put (field , castValueToType (value .get (field ), fieldSpec .getValue ()));
141
+ updatedValue .put (field , castValueToType (null , value .get (field ), fieldSpec .getValue ()));
134
142
}
135
143
return newRecord (record , null , updatedValue );
136
144
}
@@ -141,7 +149,7 @@ private R applyWithSchema(R record) {
141
149
142
150
// Whole-record casting
143
151
if (wholeValueCastType != null )
144
- return newRecord (record , updatedSchema , castValueToType (operatingValue (record ), wholeValueCastType ));
152
+ return newRecord (record , updatedSchema , castValueToType (valueSchema , operatingValue (record ), wholeValueCastType ));
145
153
146
154
// Casting within a struct
147
155
final Struct value = requireStruct (operatingValue (record ), PURPOSE );
@@ -150,7 +158,7 @@ private R applyWithSchema(R record) {
150
158
for (Field field : value .schema ().fields ()) {
151
159
final Object origFieldValue = value .get (field );
152
160
final Schema .Type targetType = casts .get (field .name ());
153
- final Object newFieldValue = targetType != null ? castValueToType (origFieldValue , targetType ) : origFieldValue ;
161
+ final Object newFieldValue = targetType != null ? castValueToType (field . schema (), origFieldValue , targetType ) : origFieldValue ;
154
162
log .trace ("Cast field '{}' from '{}' to '{}'" , field .name (), origFieldValue , newFieldValue );
155
163
updatedValue .put (updatedSchema .field (field .name ()), newFieldValue );
156
164
}
@@ -172,8 +180,10 @@ private Schema getOrBuildSchema(Schema valueSchema) {
172
180
SchemaBuilder fieldBuilder = convertFieldType (casts .get (field .name ()));
173
181
if (field .schema ().isOptional ())
174
182
fieldBuilder .optional ();
175
- if (field .schema ().defaultValue () != null )
176
- fieldBuilder .defaultValue (castValueToType (field .schema ().defaultValue (), fieldBuilder .type ()));
183
+ if (field .schema ().defaultValue () != null ) {
184
+ Schema fieldSchema = field .schema ();
185
+ fieldBuilder .defaultValue (castValueToType (fieldSchema , fieldSchema .defaultValue (), fieldBuilder .type ()));
186
+ }
177
187
builder .field (field .name (), fieldBuilder .build ());
178
188
} else {
179
189
builder .field (field .name (), field .schema ());
@@ -185,7 +195,7 @@ private Schema getOrBuildSchema(Schema valueSchema) {
185
195
if (valueSchema .isOptional ())
186
196
builder .optional ();
187
197
if (valueSchema .defaultValue () != null )
188
- builder .defaultValue (castValueToType (valueSchema .defaultValue (), builder .type ()));
198
+ builder .defaultValue (castValueToType (valueSchema , valueSchema .defaultValue (), builder .type ()));
189
199
190
200
updatedSchema = builder .build ();
191
201
schemaUpdateCache .put (valueSchema , updatedSchema );
@@ -216,11 +226,12 @@ private SchemaBuilder convertFieldType(Schema.Type type) {
216
226
217
227
}
218
228
219
- private static Object castValueToType (Object value , Schema .Type targetType ) {
229
+ private static Object castValueToType (Schema schema , Object value , Schema .Type targetType ) {
220
230
try {
221
231
if (value == null ) return null ;
222
232
223
- Schema .Type inferredType = ConnectSchema .schemaType (value .getClass ());
233
+ Schema .Type inferredType = schema == null ? ConnectSchema .schemaType (value .getClass ()) :
234
+ schema .type ();
224
235
if (inferredType == null ) {
225
236
throw new DataException ("Cast transformation was passed a value of type " + value .getClass ()
226
237
+ " which is not supported by Connect's data API" );
@@ -331,7 +342,12 @@ else if (value instanceof String)
331
342
}
332
343
333
344
private static String castToString (Object value ) {
334
- return value .toString ();
345
+ if (value instanceof java .util .Date ) {
346
+ java .util .Date dateValue = (java .util .Date ) value ;
347
+ return Values .dateFormatFor (dateValue ).format (dateValue );
348
+ } else {
349
+ return value .toString ();
350
+ }
335
351
}
336
352
337
353
protected abstract Schema operatingSchema (R record );
@@ -374,15 +390,19 @@ private enum FieldType {
374
390
}
375
391
376
392
private static Schema .Type validCastType (Schema .Type type , FieldType fieldType ) {
377
- if (!SUPPORTED_CAST_TYPES .contains (type )) {
378
- String message = "Cast transformation does not support casting to/from " + type
379
- + "; supported types are " + SUPPORTED_CAST_TYPES ;
380
- switch (fieldType ) {
381
- case INPUT :
382
- throw new DataException (message );
383
- case OUTPUT :
384
- throw new ConfigException (message );
385
- }
393
+ switch (fieldType ) {
394
+ case INPUT :
395
+ if (!SUPPORTED_CAST_INPUT_TYPES .contains (type )) {
396
+ throw new DataException ("Cast transformation does not support casting from " +
397
+ type + "; supported types are " + SUPPORTED_CAST_INPUT_TYPES );
398
+ }
399
+ break ;
400
+ case OUTPUT :
401
+ if (!SUPPORTED_CAST_OUTPUT_TYPES .contains (type )) {
402
+ throw new ConfigException ("Cast transformation does not support casting to " +
403
+ type + "; supported types are " + SUPPORTED_CAST_OUTPUT_TYPES );
404
+ }
405
+ break ;
386
406
}
387
407
return type ;
388
408
}
0 commit comments