Skip to content

Commit f3975da

Browse files
authored
Fix type coercion for unsigned and signed integers (Int64 vs UInt64, etc) (#15341)
* type coercion fix for uint/int's. * Refactored common numerical coercion logic into a single function. * Cargo fmt.
1 parent 3e56ed2 commit f3975da

File tree

7 files changed

+153
-90
lines changed

7 files changed

+153
-90
lines changed

datafusion/expr-common/src/type_coercion/binary.rs

+131-68
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,6 @@ pub fn binary_numeric_coercion(
838838
lhs_type: &DataType,
839839
rhs_type: &DataType,
840840
) -> Option<DataType> {
841-
use arrow::datatypes::DataType::*;
842841
if !lhs_type.is_numeric() || !rhs_type.is_numeric() {
843842
return None;
844843
};
@@ -852,39 +851,7 @@ pub fn binary_numeric_coercion(
852851
return Some(t);
853852
}
854853

855-
// These are ordered from most informative to least informative so
856-
// that the coercion does not lose information via truncation
857-
match (lhs_type, rhs_type) {
858-
(Float64, _) | (_, Float64) => Some(Float64),
859-
(_, Float32) | (Float32, _) => Some(Float32),
860-
// The following match arms encode the following logic: Given the two
861-
// integral types, we choose the narrowest possible integral type that
862-
// accommodates all values of both types. Note that to avoid information
863-
// loss when combining UInt64 with signed integers we use Decimal128(20, 0).
864-
(UInt64, Int64 | Int32 | Int16 | Int8)
865-
| (Int64 | Int32 | Int16 | Int8, UInt64) => Some(Decimal128(20, 0)),
866-
(UInt64, _) | (_, UInt64) => Some(UInt64),
867-
(Int64, _)
868-
| (_, Int64)
869-
| (UInt32, Int8)
870-
| (Int8, UInt32)
871-
| (UInt32, Int16)
872-
| (Int16, UInt32)
873-
| (UInt32, Int32)
874-
| (Int32, UInt32) => Some(Int64),
875-
(Int32, _)
876-
| (_, Int32)
877-
| (UInt16, Int16)
878-
| (Int16, UInt16)
879-
| (UInt16, Int8)
880-
| (Int8, UInt16) => Some(Int32),
881-
(UInt32, _) | (_, UInt32) => Some(UInt32),
882-
(Int16, _) | (_, Int16) | (Int8, UInt8) | (UInt8, Int8) => Some(Int16),
883-
(UInt16, _) | (_, UInt16) => Some(UInt16),
884-
(Int8, _) | (_, Int8) => Some(Int8),
885-
(UInt8, _) | (_, UInt8) => Some(UInt8),
886-
_ => None,
887-
}
854+
numerical_coercion(lhs_type, rhs_type)
888855
}
889856

890857
/// Decimal coercion rules.
@@ -1045,15 +1012,36 @@ fn mathematics_numerical_coercion(
10451012
(_, Dictionary(_, value_type)) => {
10461013
mathematics_numerical_coercion(lhs_type, value_type)
10471014
}
1015+
_ => numerical_coercion(lhs_type, rhs_type),
1016+
}
1017+
}
1018+
1019+
/// A common set of numerical coercions that are applied for mathematical and binary ops
1020+
/// to `lhs_type` and `rhs_type`.
1021+
fn numerical_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
1022+
use arrow::datatypes::DataType::*;
1023+
1024+
match (lhs_type, rhs_type) {
10481025
(Float64, _) | (_, Float64) => Some(Float64),
10491026
(_, Float32) | (Float32, _) => Some(Float32),
1050-
(Int64, _) | (_, Int64) => Some(Int64),
1051-
(Int32, _) | (_, Int32) => Some(Int32),
1052-
(Int16, _) | (_, Int16) => Some(Int16),
1053-
(Int8, _) | (_, Int8) => Some(Int8),
1027+
// The following match arms encode the following logic: Given the two
1028+
// integral types, we choose the narrowest possible integral type that
1029+
// accommodates all values of both types. Note that to avoid information
1030+
// loss when combining UInt64 with signed integers we use Decimal128(20, 0).
1031+
(UInt64, Int64 | Int32 | Int16 | Int8)
1032+
| (Int64 | Int32 | Int16 | Int8, UInt64) => Some(Decimal128(20, 0)),
10541033
(UInt64, _) | (_, UInt64) => Some(UInt64),
1034+
(Int64, _)
1035+
| (_, Int64)
1036+
| (UInt32, Int32 | Int16 | Int8)
1037+
| (Int32 | Int16 | Int8, UInt32) => Some(Int64),
10551038
(UInt32, _) | (_, UInt32) => Some(UInt32),
1039+
(Int32, _) | (_, Int32) | (UInt16, Int16 | Int8) | (Int16 | Int8, UInt16) => {
1040+
Some(Int32)
1041+
}
10561042
(UInt16, _) | (_, UInt16) => Some(UInt16),
1043+
(Int16, _) | (_, Int16) | (Int8, UInt8) | (UInt8, Int8) => Some(Int16),
1044+
(Int8, _) | (_, Int8) => Some(Int8),
10571045
(UInt8, _) | (_, UInt8) => Some(UInt8),
10581046
_ => None,
10591047
}
@@ -1632,7 +1620,7 @@ mod tests {
16321620

16331621
/// Test coercion rules for binary operators
16341622
///
1635-
/// Applies coercion rules for `$LHS_TYPE $OP $RHS_TYPE` and asserts that the
1623+
/// Applies coercion rules for `$LHS_TYPE $OP $RHS_TYPE` and asserts that
16361624
/// the result type is `$RESULT_TYPE`
16371625
macro_rules! test_coercion_binary_rule {
16381626
($LHS_TYPE:expr, $RHS_TYPE:expr, $OP:expr, $RESULT_TYPE:expr) => {{
@@ -1643,6 +1631,26 @@ mod tests {
16431631
}};
16441632
}
16451633

1634+
/// Test coercion rules for binary operators
1635+
///
1636+
/// Applies coercion rules for each RHS_TYPE in $RHS_TYPES such that
1637+
/// `$LHS_TYPE $OP RHS_TYPE` and asserts that the result type is `$RESULT_TYPE`.
1638+
/// Also tests that the inverse `RHS_TYPE $OP $LHS_TYPE` is true
1639+
macro_rules! test_coercion_binary_rule_multiple {
1640+
($LHS_TYPE:expr, $RHS_TYPES:expr, $OP:expr, $RESULT_TYPE:expr) => {{
1641+
for rh_type in $RHS_TYPES {
1642+
let (lhs, rhs) = BinaryTypeCoercer::new(&$LHS_TYPE, &$OP, &rh_type)
1643+
.get_input_types()?;
1644+
assert_eq!(lhs, $RESULT_TYPE);
1645+
assert_eq!(rhs, $RESULT_TYPE);
1646+
1647+
BinaryTypeCoercer::new(&rh_type, &$OP, &$LHS_TYPE).get_input_types()?;
1648+
assert_eq!(lhs, $RESULT_TYPE);
1649+
assert_eq!(rhs, $RESULT_TYPE);
1650+
}
1651+
}};
1652+
}
1653+
16461654
/// Test coercion rules for like
16471655
///
16481656
/// Applies coercion rules for both
@@ -2002,39 +2010,94 @@ mod tests {
20022010

20032011
#[test]
20042012
fn test_type_coercion_arithmetic() -> Result<()> {
2005-
// integer
2006-
test_coercion_binary_rule!(
2007-
DataType::Int32,
2008-
DataType::UInt32,
2013+
use DataType::*;
2014+
2015+
// (Float64, _) | (_, Float64) => Some(Float64),
2016+
test_coercion_binary_rule_multiple!(
2017+
Float64,
2018+
[
2019+
Float64, Float32, Float16, Int64, UInt64, Int32, UInt32, Int16, UInt16,
2020+
Int8, UInt8
2021+
],
20092022
Operator::Plus,
2010-
DataType::Int32
2011-
);
2012-
test_coercion_binary_rule!(
2013-
DataType::Int32,
2014-
DataType::UInt16,
2023+
Float64
2024+
);
2025+
// (_, Float32) | (Float32, _) => Some(Float32),
2026+
test_coercion_binary_rule_multiple!(
2027+
Float32,
2028+
[
2029+
Float32, Float16, Int64, UInt64, Int32, UInt32, Int16, UInt16, Int8,
2030+
UInt8
2031+
],
2032+
Operator::Plus,
2033+
Float32
2034+
);
2035+
// (UInt64, Int64 | Int32 | Int16 | Int8) | (Int64 | Int32 | Int16 | Int8, UInt64) => Some(Decimal128(20, 0)),
2036+
test_coercion_binary_rule_multiple!(
2037+
UInt64,
2038+
[Int64, Int32, Int16, Int8],
2039+
Operator::Divide,
2040+
Decimal128(20, 0)
2041+
);
2042+
// (UInt64, _) | (_, UInt64) => Some(UInt64),
2043+
test_coercion_binary_rule_multiple!(
2044+
UInt64,
2045+
[UInt64, UInt32, UInt16, UInt8],
2046+
Operator::Modulo,
2047+
UInt64
2048+
);
2049+
// (Int64, _) | (_, Int64) => Some(Int64),
2050+
test_coercion_binary_rule_multiple!(
2051+
Int64,
2052+
[Int64, Int32, UInt32, Int16, UInt16, Int8, UInt8],
2053+
Operator::Modulo,
2054+
Int64
2055+
);
2056+
// (UInt32, Int32 | Int16 | Int8) | (Int32 | Int16 | Int8, UInt32) => Some(Int64)
2057+
test_coercion_binary_rule_multiple!(
2058+
UInt32,
2059+
[Int32, Int16, Int8],
2060+
Operator::Modulo,
2061+
Int64
2062+
);
2063+
// (UInt32, _) | (_, UInt32) => Some(UInt32),
2064+
test_coercion_binary_rule_multiple!(
2065+
UInt32,
2066+
[UInt32, UInt16, UInt8],
2067+
Operator::Modulo,
2068+
UInt32
2069+
);
2070+
// (Int32, _) | (_, Int32) => Some(Int32),
2071+
test_coercion_binary_rule_multiple!(
2072+
Int32,
2073+
[Int32, Int16, Int8],
2074+
Operator::Modulo,
2075+
Int32
2076+
);
2077+
// (UInt16, Int16 | Int8) | (Int16 | Int8, UInt16) => Some(Int32)
2078+
test_coercion_binary_rule_multiple!(
2079+
UInt16,
2080+
[Int16, Int8],
20152081
Operator::Minus,
2016-
DataType::Int32
2082+
Int32
20172083
);
2018-
test_coercion_binary_rule!(
2019-
DataType::Int8,
2020-
DataType::Int64,
2021-
Operator::Multiply,
2022-
DataType::Int64
2023-
);
2024-
// float
2025-
test_coercion_binary_rule!(
2026-
DataType::Float32,
2027-
DataType::Int32,
2084+
// (UInt16, _) | (_, UInt16) => Some(UInt16),
2085+
test_coercion_binary_rule_multiple!(
2086+
UInt16,
2087+
[UInt16, UInt8, UInt8],
20282088
Operator::Plus,
2029-
DataType::Float32
2030-
);
2031-
test_coercion_binary_rule!(
2032-
DataType::Float32,
2033-
DataType::Float64,
2034-
Operator::Multiply,
2035-
DataType::Float64
2036-
);
2037-
// TODO add other data type
2089+
UInt16
2090+
);
2091+
// (Int16, _) | (_, Int16) => Some(Int16),
2092+
test_coercion_binary_rule_multiple!(Int16, [Int16, Int8], Operator::Plus, Int16);
2093+
// (UInt8, Int8) | (Int8, UInt8) => Some(Int16)
2094+
test_coercion_binary_rule!(Int8, UInt8, Operator::Minus, Int16);
2095+
test_coercion_binary_rule!(UInt8, Int8, Operator::Multiply, Int16);
2096+
// (UInt8, _) | (_, UInt8) => Some(UInt8),
2097+
test_coercion_binary_rule!(UInt8, UInt8, Operator::Minus, UInt8);
2098+
// (Int8, _) | (_, Int8) => Some(Int8),
2099+
test_coercion_binary_rule!(Int8, Int8, Operator::Plus, Int8);
2100+
20382101
Ok(())
20392102
}
20402103

datafusion/optimizer/src/single_distinct_to_groupby.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ mod tests {
410410

411411
let expected = "Projection: count(alias1) AS count(DISTINCT Int32(2) * test.b) [count(DISTINCT Int32(2) * test.b):Int64]\
412412
\n Aggregate: groupBy=[[]], aggr=[[count(alias1)]] [count(alias1):Int64]\
413-
\n Aggregate: groupBy=[[Int32(2) * test.b AS alias1]], aggr=[[]] [alias1:Int32]\
413+
\n Aggregate: groupBy=[[Int32(2) * test.b AS alias1]], aggr=[[]] [alias1:Int64]\
414414
\n TableScan: test [a:UInt32, b:UInt32, c:UInt32]";
415415

416416
assert_optimized_plan_equal(plan, expected)
@@ -497,9 +497,9 @@ mod tests {
497497
.build()?;
498498

499499
// Should work
500-
let expected = "Projection: group_alias_0 AS test.a + Int32(1), count(alias1) AS count(DISTINCT test.c) [test.a + Int32(1):Int32, count(DISTINCT test.c):Int64]\
501-
\n Aggregate: groupBy=[[group_alias_0]], aggr=[[count(alias1)]] [group_alias_0:Int32, count(alias1):Int64]\
502-
\n Aggregate: groupBy=[[test.a + Int32(1) AS group_alias_0, test.c AS alias1]], aggr=[[]] [group_alias_0:Int32, alias1:UInt32]\
500+
let expected = "Projection: group_alias_0 AS test.a + Int32(1), count(alias1) AS count(DISTINCT test.c) [test.a + Int32(1):Int64, count(DISTINCT test.c):Int64]\
501+
\n Aggregate: groupBy=[[group_alias_0]], aggr=[[count(alias1)]] [group_alias_0:Int64, count(alias1):Int64]\
502+
\n Aggregate: groupBy=[[test.a + Int32(1) AS group_alias_0, test.c AS alias1]], aggr=[[]] [group_alias_0:Int64, alias1:UInt32]\
503503
\n TableScan: test [a:UInt32, b:UInt32, c:UInt32]";
504504

505505
assert_optimized_plan_equal(plan, expected)

datafusion/optimizer/tests/optimizer_integration.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ fn push_down_filter_groupby_expr_contains_alias() {
267267
let sql = "SELECT * FROM (SELECT (col_int32 + col_uint32) AS c, count(*) FROM test GROUP BY 1) where c > 3";
268268
let plan = test_sql(sql).unwrap();
269269
let expected = "Projection: test.col_int32 + test.col_uint32 AS c, count(Int64(1)) AS count(*)\
270-
\n Aggregate: groupBy=[[test.col_int32 + CAST(test.col_uint32 AS Int32)]], aggr=[[count(Int64(1))]]\
271-
\n Filter: test.col_int32 + CAST(test.col_uint32 AS Int32) > Int32(3)\
270+
\n Aggregate: groupBy=[[CAST(test.col_int32 AS Int64) + CAST(test.col_uint32 AS Int64)]], aggr=[[count(Int64(1))]]\
271+
\n Filter: CAST(test.col_int32 AS Int64) + CAST(test.col_uint32 AS Int64) > Int64(3)\
272272
\n TableScan: test projection=[col_int32, col_uint32]";
273273
assert_eq!(expected, format!("{plan}"));
274274
}

datafusion/physical-expr/src/expressions/binary.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1025,9 +1025,9 @@ mod tests {
10251025
DataType::UInt32,
10261026
vec![1u32, 2u32],
10271027
Operator::Plus,
1028-
Int32Array,
1029-
DataType::Int32,
1030-
[2i32, 4i32],
1028+
Int64Array,
1029+
DataType::Int64,
1030+
[2i64, 4i64],
10311031
);
10321032
test_coercion!(
10331033
Int32Array,

datafusion/sqllogictest/test_files/math.slt

+5-5
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,22 @@ INSERT into test_nullable_integer values(127, 32767, 2147483647, 922337203685477
164164
----
165165
1
166166

167-
query IIIIIIII
167+
query IIIIIIIR
168168
SELECT c1*0, c2*0, c3*0, c4*0, c5*0, c6*0, c7*0, c8*0 FROM test_nullable_integer where dataset = 'nulls'
169169
----
170170
NULL NULL NULL NULL NULL NULL NULL NULL
171171

172-
query IIIIIIII
172+
query IIIIIIIR
173173
SELECT c1/0, c2/0, c3/0, c4/0, c5/0, c6/0, c7/0, c8/0 FROM test_nullable_integer where dataset = 'nulls'
174174
----
175175
NULL NULL NULL NULL NULL NULL NULL NULL
176176

177-
query IIIIIIII
177+
query IIIIIIIR
178178
SELECT c1%0, c2%0, c3%0, c4%0, c5%0, c6%0, c7%0, c8%0 FROM test_nullable_integer where dataset = 'nulls'
179179
----
180180
NULL NULL NULL NULL NULL NULL NULL NULL
181181

182-
query IIIIIIII rowsort
182+
query IIIIIIIR rowsort
183183
select c1*0, c2*0, c3*0, c4*0, c5*0, c6*0, c7*0, c8*0 from test_nullable_integer where dataset != 'maxs'
184184
----
185185
0 0 0 0 0 0 0 0
@@ -300,7 +300,7 @@ INSERT INTO test_non_nullable_integer VALUES(1, 1, 1, 1, 1, 1, 1, 1)
300300
----
301301
1
302302

303-
query IIIIIIII rowsort
303+
query IIIIIIIR rowsort
304304
select c1*0, c2*0, c3*0, c4*0, c5*0, c6*0, c7*0, c8*0 from test_non_nullable_integer
305305
----
306306
0 0 0 0 0 0 0 0

datafusion/sqllogictest/test_files/operator.slt

+4-4
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ select
7070
arrow_typeof(decimal + 2)
7171
from numeric_types;
7272
----
73-
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Int64 Float32 Float64 Decimal128(23, 2)
73+
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Decimal128(21, 0) Float32 Float64 Decimal128(23, 2)
7474

7575
# Plus with literal decimal
7676
query TTTTTTTTTTT
@@ -127,7 +127,7 @@ select
127127
arrow_typeof(decimal - 2)
128128
from numeric_types;
129129
----
130-
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Int64 Float32 Float64 Decimal128(23, 2)
130+
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Decimal128(21, 0) Float32 Float64 Decimal128(23, 2)
131131

132132
# Minus with literal decimal
133133
query TTTTTTTTTTT
@@ -184,7 +184,7 @@ select
184184
arrow_typeof(decimal * 2)
185185
from numeric_types;
186186
----
187-
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Int64 Float32 Float64 Decimal128(26, 2)
187+
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Decimal128(38, 0) Float32 Float64 Decimal128(26, 2)
188188

189189
# Multiply with literal decimal
190190
query TTTTTTTTTTT
@@ -242,7 +242,7 @@ select
242242
arrow_typeof(decimal / 2)
243243
from numeric_types;
244244
----
245-
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Int64 Float32 Float64 Decimal128(9, 6)
245+
Int64 Int64 Int64 Int64 Int64 Int64 Int64 Decimal128(24, 4) Float32 Float64 Decimal128(9, 6)
246246

247247
# Divide with literal decimal
248248
query TTTTTTTTTTT

datafusion/sqllogictest/test_files/window.slt

+4-4
Original file line numberDiff line numberDiff line change
@@ -2448,15 +2448,15 @@ EXPLAIN SELECT c5, c9, rn1 FROM (SELECT c5, c9,
24482448
LIMIT 5
24492449
----
24502450
logical_plan
2451-
01)Sort: rn1 ASC NULLS LAST, CAST(aggregate_test_100.c9 AS Int32) + aggregate_test_100.c5 DESC NULLS FIRST, fetch=5
2451+
01)Sort: rn1 ASC NULLS LAST, CAST(aggregate_test_100.c9 AS Decimal128(20, 0)) + CAST(aggregate_test_100.c5 AS Decimal128(20, 0)) DESC NULLS FIRST, fetch=5
24522452
02)--Projection: aggregate_test_100.c5, aggregate_test_100.c9, row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS rn1
2453-
03)----WindowAggr: windowExpr=[[row_number() ORDER BY [CAST(aggregate_test_100.c9 AS Int32) + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
2453+
03)----WindowAggr: windowExpr=[[row_number() ORDER BY [CAST(aggregate_test_100.c9 AS Decimal128(20, 0)) + CAST(aggregate_test_100.c5 AS Decimal128(20, 0)) DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]
24542454
04)------TableScan: aggregate_test_100 projection=[c5, c9]
24552455
physical_plan
24562456
01)ProjectionExec: expr=[c5@0 as c5, c9@1 as c9, row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@2 as rn1]
24572457
02)--GlobalLimitExec: skip=0, fetch=5
2458-
03)----BoundedWindowAggExec: wdw=[row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Ok(Field { name: "row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", data_type: UInt64, nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }), frame: WindowFrame { units: Range, start_bound: Preceding(Int32(NULL)), end_bound: CurrentRow, is_causal: false }], mode=[Sorted]
2459-
04)------SortExec: expr=[CAST(c9@1 AS Int32) + c5@0 DESC], preserve_partitioning=[false]
2458+
03)----BoundedWindowAggExec: wdw=[row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Ok(Field { name: "row_number() ORDER BY [aggregate_test_100.c9 + aggregate_test_100.c5 DESC NULLS FIRST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", data_type: UInt64, nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} }), frame: WindowFrame { units: Range, start_bound: Preceding(Decimal128(None,21,0)), end_bound: CurrentRow, is_causal: false }], mode=[Sorted]
2459+
04)------SortExec: expr=[CAST(c9@1 AS Decimal128(20, 0)) + CAST(c5@0 AS Decimal128(20, 0)) DESC], preserve_partitioning=[false]
24602460
05)--------DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/testing/data/csv/aggregate_test_100.csv]]}, projection=[c5, c9], file_type=csv, has_header=true
24612461

24622462
# Ordering equivalence should be preserved during cast expression

0 commit comments

Comments
 (0)