Skip to content

Commit f6fd4c3

Browse files
committed
check the type class for char and character
1 parent 42fde52 commit f6fd4c3

File tree

2 files changed

+99
-32
lines changed

2 files changed

+99
-32
lines changed

fluss-client/src/main/java/org/apache/fluss/client/utils/PojoConverterUtils.java

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import org.apache.fluss.row.TimestampLtz;
2525
import org.apache.fluss.row.TimestampNtz;
2626
import org.apache.fluss.types.DataType;
27-
import org.apache.fluss.types.DataTypeRoot;
2827
import org.apache.fluss.types.DataTypeChecks;
28+
import org.apache.fluss.types.DataTypeRoot;
2929
import org.apache.fluss.types.DecimalType;
3030
import org.apache.fluss.types.RowType;
3131

@@ -391,7 +391,26 @@ private RowToFieldConverter createRowToFieldConverter(DataType fieldType, Field
391391
return null;
392392
}
393393
BinaryString binaryString = row.getString(pos);
394-
return binaryString.toString();
394+
String value = binaryString.toString();
395+
if (fieldClass == String.class) {
396+
return value;
397+
} else if (fieldClass == Character.class || fieldClass == char.class) {
398+
if (value.isEmpty()) {
399+
throw new IllegalArgumentException(
400+
String.format(
401+
"Field %s expects Character/char, but the string value is empty.",
402+
field.getName()));
403+
}
404+
return value.charAt(0);
405+
} else {
406+
// This should normally be prevented by constructor-time validation of
407+
// supported types.
408+
// Keep a defensive check here for clarity.
409+
throw new IllegalArgumentException(
410+
String.format(
411+
"Field %s is not a String or Character/char. Cannot convert from string.",
412+
field.getName()));
413+
}
395414
};
396415
case BINARY:
397416
case BYTES:
@@ -422,35 +441,38 @@ private RowToFieldConverter createRowToFieldConverter(DataType fieldType, Field
422441
int millis = row.getInt(pos);
423442
return LocalTime.ofNanoOfDay(millis * 1_000_000L);
424443
};
425-
case TIMESTAMP_WITHOUT_TIME_ZONE: {
426-
final int precision = DataTypeChecks.getPrecision(fieldType);
427-
return (row, pos) -> {
428-
if (row.isNullAt(pos)) {
429-
return null;
430-
}
431-
TimestampNtz timestampNtz = row.getTimestampNtz(pos, precision);
432-
return timestampNtz.toLocalDateTime();
433-
};
434-
}
435-
case TIMESTAMP_WITH_LOCAL_TIME_ZONE: {
436-
final int precision = DataTypeChecks.getPrecision(fieldType);
437-
return (row, pos) -> {
438-
if (row.isNullAt(pos)) {
439-
return null;
440-
}
441-
TimestampLtz timestampLtz = row.getTimestampLtz(pos, precision);
442-
if (fieldClass == Instant.class) {
443-
return timestampLtz.toInstant();
444-
} else if (fieldClass == OffsetDateTime.class) {
445-
return OffsetDateTime.ofInstant(timestampLtz.toInstant(), ZoneOffset.UTC);
446-
} else {
447-
throw new IllegalArgumentException(
448-
String.format(
449-
"Field %s is not an Instant or OffsetDateTime. Cannot convert from TimestampData.",
450-
field.getName()));
451-
}
452-
};
453-
}
444+
case TIMESTAMP_WITHOUT_TIME_ZONE:
445+
{
446+
final int precision = DataTypeChecks.getPrecision(fieldType);
447+
return (row, pos) -> {
448+
if (row.isNullAt(pos)) {
449+
return null;
450+
}
451+
TimestampNtz timestampNtz = row.getTimestampNtz(pos, precision);
452+
return timestampNtz.toLocalDateTime();
453+
};
454+
}
455+
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
456+
{
457+
final int precision = DataTypeChecks.getPrecision(fieldType);
458+
return (row, pos) -> {
459+
if (row.isNullAt(pos)) {
460+
return null;
461+
}
462+
TimestampLtz timestampLtz = row.getTimestampLtz(pos, precision);
463+
if (fieldClass == Instant.class) {
464+
return timestampLtz.toInstant();
465+
} else if (fieldClass == OffsetDateTime.class) {
466+
return OffsetDateTime.ofInstant(
467+
timestampLtz.toInstant(), ZoneOffset.UTC);
468+
} else {
469+
throw new IllegalArgumentException(
470+
String.format(
471+
"Field %s is not an Instant or OffsetDateTime. Cannot convert from TimestampData.",
472+
field.getName()));
473+
}
474+
};
475+
}
454476
default:
455477
throw new UnsupportedOperationException(
456478
String.format(
@@ -538,7 +560,6 @@ public T fromRow(InternalRow row) {
538560
}
539561
}
540562

541-
542563
/**
543564
* Utility method to create a Set containing the specified Java type classes.
544565
*

fluss-client/src/test/java/org/apache/fluss/client/utils/PojoConverterUtilsTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,52 @@ public DecimalWrongTypePojo(String decimalField) {
360360
}
361361
}
362362

363+
@Test
364+
public void testCharacterAndCharFieldConversionRoundTrip() {
365+
// Use STRING schema to test both Character and primitive char fields
366+
RowType rowType = RowType.builder().field("charField", DataTypes.STRING()).build();
367+
368+
// Character wrapper field
369+
PojoConverterUtils<CharacterFieldPojo> characterConverter =
370+
PojoConverterUtils.getConverter(CharacterFieldPojo.class, rowType);
371+
CharacterFieldPojo characterPojo = new CharacterFieldPojo('A');
372+
GenericRow row1 = characterConverter.toRow(characterPojo);
373+
assertThat(row1.getString(0).toString()).isEqualTo("A");
374+
CharacterFieldPojo characterPojo2 = characterConverter.fromRow(row1);
375+
assertThat(characterPojo2.charField).isEqualTo('A');
376+
377+
// primitive char field
378+
PojoConverterUtils<PrimitiveCharFieldPojo> primitiveConverter =
379+
PojoConverterUtils.getConverter(PrimitiveCharFieldPojo.class, rowType);
380+
PrimitiveCharFieldPojo primitivePojo = new PrimitiveCharFieldPojo('Z');
381+
GenericRow row2 = primitiveConverter.toRow(primitivePojo);
382+
assertThat(row2.getString(0).toString()).isEqualTo("Z");
383+
PrimitiveCharFieldPojo primitivePojo2 = primitiveConverter.fromRow(row2);
384+
assertThat(primitivePojo2.charField).isEqualTo('Z');
385+
}
386+
387+
/** POJO with a Character field for testing CHAR/STRING conversions to Character. */
388+
public static class CharacterFieldPojo {
389+
private Character charField;
390+
391+
public CharacterFieldPojo() {}
392+
393+
public CharacterFieldPojo(Character charField) {
394+
this.charField = charField;
395+
}
396+
}
397+
398+
/** POJO with a primitive char field for testing CHAR/STRING conversions to char. */
399+
public static class PrimitiveCharFieldPojo {
400+
private char charField;
401+
402+
public PrimitiveCharFieldPojo() {}
403+
404+
public PrimitiveCharFieldPojo(char charField) {
405+
this.charField = charField;
406+
}
407+
}
408+
363409
/** Helper POJO to trigger DATE field type mismatch. */
364410
public static class DateWrongTypePojo {
365411
private String dateField;

0 commit comments

Comments
 (0)