Skip to content

Commit 41b828a

Browse files
committed
[Feature][Transform] support boolean type for sql transform (apache#9136)
1 parent 2261646 commit 41b828a

File tree

6 files changed

+182
-5
lines changed

6 files changed

+182
-5
lines changed

docs/en/transform-v2/sql-functions.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -911,11 +911,17 @@ CALL FROM_UNIXTIME(1672502400, 'yyyy-MM-dd HH:mm:ss','UTC+6')
911911

912912
Converts a value to another data type.
913913

914-
Supported data types: STRING | VARCHAR, INT | INTEGER, LONG | BIGINT, BYTE, FLOAT, DOUBLE, DECIMAL(p,s), TIMESTAMP, DATE, TIME, BYTES
914+
Supported data types: STRING | VARCHAR, INT | INTEGER, LONG | BIGINT, BYTE, FLOAT, DOUBLE, DECIMAL(p,s), TIMESTAMP, DATE, TIME, BYTES, BOOLEAN
915915

916916
Example:
917+
* CAST(NAME AS INT)
918+
* CAST(FLAG AS BOOLEAN)
917919

918-
CONVERT(NAME AS INT)
920+
NOTE:
921+
Converts a value to a BOOLEAN data type according to the following rules:
922+
1. If the value can be interpreted as a boolean string (`'true'` or `'false'`), it returns the corresponding boolean value.
923+
2. If the value can be interpreted as a numeric value (`1` or `0`), it returns `true` for `1` and `false` for `0`.
924+
3. If the value cannot be interpreted according to the above rules, it throws a `TransformException`.
919925

920926
### TRY_CAST
921927

@@ -1010,7 +1016,9 @@ It is used to determine whether the condition is valid and return different valu
10101016

10111017
Example:
10121018

1013-
case when c_string in ('c_string') then 1 else 0 end
1019+
case when c_string in ('c_string') then 1 else 0 end
1020+
1021+
case when c_string in ('c_string') then true else false end
10141022

10151023
### UUID
10161024

docs/zh/transform-v2/sql-functions.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,15 @@ CALL FROM_UNIXTIME(1672502400, 'yyyy-MM-dd HH:mm:ss','UTC+6')
908908

909909
示例:
910910

911-
CONVERT(NAME AS INT)
911+
CAST(NAME AS INT)
912+
913+
CAST(FLAG AS BOOLEAN)
914+
915+
注意:将值转换为布尔数据类型时,遵循以下规则:
916+
917+
1. 如果值可以被解释为布尔字符串('true' 或 'false'),则返回相应的布尔值。
918+
2. 如果值可以被解释为数值(1 或 0),则对于 1 返回 true,对于 0 返回 false。
919+
3. 如果值无法根据以上规则进行解释,则抛出 TransformException 异常。
912920

913921
### TRY_CAST
914922

@@ -1001,6 +1009,8 @@ from
10011009

10021010
case when c_string in ('c_string') then 1 else 0 end
10031011

1012+
case when c_string in ('c_string') then true else false end
1013+
10041014
### UUID
10051015

10061016
```UUID()```

seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/ZetaSQLFunction.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ public Object computeForValue(Expression expression, Object[] inputFields) {
271271
columnName = columnName.substring(1, columnName.length() - 1);
272272
index = inputRowType.indexOf(columnName, false);
273273
}
274+
if (index == -1
275+
&& ("true".equalsIgnoreCase(columnName)
276+
|| "false".equalsIgnoreCase(columnName))) {
277+
return Boolean.parseBoolean(columnName);
278+
}
274279

275280
if (index != -1) {
276281
return inputFields[index];

seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/ZetaSQLType.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public class ZetaSQLType {
8282
public static final String DATETIME = "DATETIME";
8383
public static final String DATE = "DATE";
8484
public static final String TIME = "TIME";
85+
public static final String BOOLEAN = "BOOLEAN";
8586

8687
private final SeaTunnelRowType inputRowType;
8788

@@ -122,7 +123,11 @@ public SeaTunnelDataType<?> getExpressionType(Expression expression) {
122123
columnName = columnName.substring(1, columnName.length() - 1);
123124
index = inputRowType.indexOf(columnName, false);
124125
}
125-
126+
if (index == -1
127+
&& ("true".equalsIgnoreCase(columnName)
128+
|| "false".equalsIgnoreCase(columnName))) {
129+
return BasicType.BOOLEAN_TYPE;
130+
}
126131
if (index != -1) {
127132
return inputRowType.getFieldType(index);
128133
} else {
@@ -353,6 +358,8 @@ private SeaTunnelDataType<?> getCastType(CastExpression castExpression) {
353358
return LocalTimeType.LOCAL_DATE_TYPE;
354359
case TIME:
355360
return LocalTimeType.LOCAL_TIME_TYPE;
361+
case BOOLEAN:
362+
return BasicType.BOOLEAN_TYPE;
356363
default:
357364
throw new TransformException(
358365
CommonErrorCodeDeprecated.UNSUPPORTED_OPERATION,

seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/sql/zeta/functions/SystemFunction.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.time.LocalTime;
3434
import java.time.ZoneId;
3535
import java.util.ArrayList;
36+
import java.util.Arrays;
3637
import java.util.List;
3738

3839
public class SystemFunction {
@@ -177,6 +178,26 @@ public static Object castAs(List<Object> args) {
177178
BigDecimal bigDecimal = new BigDecimal(v1.toString());
178179
Integer scale = (Integer) args.get(3);
179180
return bigDecimal.setScale(scale, RoundingMode.CEILING);
181+
case "BOOLEAN":
182+
if (v1 instanceof Number) {
183+
if (Arrays.asList(1, 0).contains(((Number) v1).intValue())) {
184+
return ((Number) v1).intValue() == 1;
185+
} else {
186+
throw new TransformException(
187+
CommonErrorCodeDeprecated.UNSUPPORTED_OPERATION,
188+
String.format("Unsupported CAST AS Boolean: %s", v1));
189+
}
190+
} else if (v1 instanceof String) {
191+
if (Arrays.asList("TRUE", "FALSE").contains(v1.toString().toUpperCase())) {
192+
return Boolean.parseBoolean(v1.toString());
193+
} else {
194+
throw new TransformException(
195+
CommonErrorCodeDeprecated.UNSUPPORTED_OPERATION,
196+
String.format("Unsupported CAST AS Boolean: %s", v1));
197+
}
198+
} else if (v1 instanceof Boolean) {
199+
return v1;
200+
}
180201
}
181202
throw new TransformException(
182203
CommonErrorCodeDeprecated.UNSUPPORTED_OPERATION,

seatunnel-transforms-v2/src/test/java/org/apache/seatunnel/transform/sql/SQLTransformTest.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.seatunnel.api.table.type.SeaTunnelDataType;
3030
import org.apache.seatunnel.api.table.type.SeaTunnelRow;
3131
import org.apache.seatunnel.api.table.type.SeaTunnelRowType;
32+
import org.apache.seatunnel.transform.exception.TransformException;
3233

3334
import org.junit.jupiter.api.Assertions;
3435
import org.junit.jupiter.api.Test;
@@ -315,4 +316,129 @@ public void tesCaseWhenClausesWithBooleanField() {
315316
Assertions.assertEquals(false, result.get(0).getField(1));
316317
Assertions.assertEquals(2, result.get(0).getField(2));
317318
}
319+
320+
@Test
321+
public void tesCaseWhenBooleanClausesWithField() {
322+
String tableName = "test";
323+
String[] fields = new String[] {"id", "int", "string"};
324+
CatalogTable table =
325+
CatalogTableUtil.getCatalogTable(
326+
tableName,
327+
new SeaTunnelRowType(
328+
fields,
329+
new SeaTunnelDataType[] {
330+
BasicType.INT_TYPE, BasicType.INT_TYPE, BasicType.STRING_TYPE
331+
}));
332+
ReadonlyConfig config =
333+
ReadonlyConfig.fromMap(
334+
Collections.singletonMap(
335+
"query",
336+
"select `id`, `int`, (case when `int` = 1 then true else false end) as bool_1 , `string`, (case when `string` = 'true' then true else false end) as bool_2 from dual"));
337+
SQLTransform sqlTransform = new SQLTransform(config, table);
338+
List<SeaTunnelRow> result =
339+
sqlTransform.transformRow(new SeaTunnelRow(new Object[] {1, 1, "true"}));
340+
341+
Assertions.assertEquals(1, result.get(0).getField(0));
342+
Assertions.assertEquals(1, result.get(0).getField(1));
343+
Assertions.assertEquals(true, result.get(0).getField(2));
344+
Assertions.assertEquals("true", result.get(0).getField(3));
345+
Assertions.assertEquals(true, result.get(0).getField(4));
346+
347+
result = sqlTransform.transformRow(new SeaTunnelRow(new Object[] {1, 0, "false"}));
348+
Assertions.assertEquals(1, result.get(0).getField(0));
349+
Assertions.assertEquals(0, result.get(0).getField(1));
350+
Assertions.assertEquals(false, result.get(0).getField(2));
351+
Assertions.assertEquals("false", result.get(0).getField(3));
352+
Assertions.assertEquals(false, result.get(0).getField(4));
353+
}
354+
355+
@Test
356+
public void tesCastBooleanClausesWithField() {
357+
String tableName = "test";
358+
String[] fields = new String[] {"id", "int", "string"};
359+
CatalogTable table =
360+
CatalogTableUtil.getCatalogTable(
361+
tableName,
362+
new SeaTunnelRowType(
363+
fields,
364+
new SeaTunnelDataType[] {
365+
BasicType.INT_TYPE, BasicType.INT_TYPE, BasicType.STRING_TYPE
366+
}));
367+
ReadonlyConfig config =
368+
ReadonlyConfig.fromMap(
369+
Collections.singletonMap(
370+
"query",
371+
"select `id`, `int`, cast(`int` as boolean) as bool_1 , `string`, cast(`string` as boolean) as bool_2 from dual"));
372+
SQLTransform sqlTransform = new SQLTransform(config, table);
373+
List<SeaTunnelRow> result =
374+
sqlTransform.transformRow(
375+
new SeaTunnelRow(new Object[] {Integer.valueOf(1), 1, "true"}));
376+
377+
Assertions.assertEquals(1, result.get(0).getField(0));
378+
Assertions.assertEquals(1, result.get(0).getField(1));
379+
Assertions.assertEquals(true, result.get(0).getField(2));
380+
Assertions.assertEquals("true", result.get(0).getField(3));
381+
Assertions.assertEquals(true, result.get(0).getField(4));
382+
383+
result =
384+
sqlTransform.transformRow(
385+
new SeaTunnelRow(new Object[] {Integer.valueOf(1), 0, "false"}));
386+
Assertions.assertEquals(1, result.get(0).getField(0));
387+
Assertions.assertEquals(0, result.get(0).getField(1));
388+
Assertions.assertEquals(false, result.get(0).getField(2));
389+
Assertions.assertEquals("false", result.get(0).getField(3));
390+
Assertions.assertEquals(false, result.get(0).getField(4));
391+
392+
Assertions.assertThrows(
393+
TransformException.class,
394+
() -> {
395+
try {
396+
sqlTransform.transformRow(
397+
new SeaTunnelRow(new Object[] {Integer.valueOf(1), 3, "false"}));
398+
} catch (Exception e) {
399+
Assertions.assertEquals(
400+
"ErrorCode:[COMMON-05], ErrorDescription:[Unsupported operation] - Unsupported CAST AS Boolean: 3",
401+
e.getMessage());
402+
throw e;
403+
}
404+
});
405+
406+
Assertions.assertThrows(
407+
TransformException.class,
408+
() -> {
409+
try {
410+
sqlTransform.transformRow(
411+
new SeaTunnelRow(new Object[] {Integer.valueOf(1), 0, "false333"}));
412+
} catch (Exception e) {
413+
Assertions.assertEquals(
414+
"ErrorCode:[COMMON-05], ErrorDescription:[Unsupported operation] - Unsupported CAST AS Boolean: false333",
415+
e.getMessage());
416+
throw e;
417+
}
418+
});
419+
}
420+
421+
@Test
422+
public void tesBooleanField() {
423+
String tableName = "test";
424+
String[] fields = new String[] {"id", "int", "string"};
425+
CatalogTable table =
426+
CatalogTableUtil.getCatalogTable(
427+
tableName,
428+
new SeaTunnelRowType(
429+
fields,
430+
new SeaTunnelDataType[] {
431+
BasicType.INT_TYPE, BasicType.INT_TYPE, BasicType.STRING_TYPE
432+
}));
433+
ReadonlyConfig config =
434+
ReadonlyConfig.fromMap(
435+
Collections.singletonMap(
436+
"query", "select `id`, true as bool_1, false as bool_2 from dual"));
437+
SQLTransform sqlTransform = new SQLTransform(config, table);
438+
List<SeaTunnelRow> result =
439+
sqlTransform.transformRow(new SeaTunnelRow(new Object[] {1, 1, "true"}));
440+
Assertions.assertEquals(1, result.get(0).getField(0));
441+
Assertions.assertEquals(true, result.get(0).getField(1));
442+
Assertions.assertEquals(false, result.get(0).getField(2));
443+
}
318444
}

0 commit comments

Comments
 (0)