datafusion icon indicating copy to clipboard operation
datafusion copied to clipboard

Update `concat_ws` scalar function to support `Utf8View`

Open tshauck opened this issue 1 year ago • 7 comments

Part of https://github.com/apache/datafusion/issues/11752 and https://github.com/apache/datafusion/issues/11790

Currently, a call to CONCAT_WS with a Utf8View datatypes induces a cast. After the change that fixes this issue, it should not.

query TT
EXPLAIN SELECT
  concat_ws(', ', column1_utf8view, column2_utf8view) as c
FROM test;
----
logical_plan
01)Projection: concat_ws(Utf8(", "), CAST(test.column1_utf8view AS Utf8), CAST(test.column2_utf8view AS Utf8)) AS c
02)--TableScan: test projection=[column1_utf8view, column2_utf8view]

concat_ws is defined here: https://github.com/apache/datafusion/blob/main/datafusion/functions/src/string/concat_ws.rs

Is your feature request related to a problem or challenge?

We are working to add complete StringView support in DataFusion, which permits potentially much faster processing of string data. See https://github.com/apache/datafusion/issues/10918 for more background.

Today, most DataFusion string functions support DataType::Utf8 and DataType::LargeUtf8 and when called with a StringView argument DataFusion will cast the argument back to DataType::Utf8 which is expensive.

To realize the full speed of StringView, we need to ensure that all string functions support the DataType::Utf8View directly.

Describe the solution you'd like

Update the function to support DataType::Utf8View directly

Describe alternatives you've considered

The typical steps are:

  1. Write some tests showing the function doesn't support Utf8View (see the tests in string_view.slt to ensure the arguments are not being cast
  2. Change the Signature of the function to accept Utf8View in addition to Utf8/LargeUtf8
  3. Update the implementation of the function to operate on Utf8View

Example PRs

  • Update to use an arrow kernel that already supports StringView: https://github.com/apache/datafusion/pull/11787
  • Change the implementation to support StringView directly: https://github.com/apache/datafusion/pull/11676
  • Change implementation (option 2): https://github.com/apache/datafusion/pull/11556

Additional context

The documentation of string functions can be found here: https://datafusion.apache.org/user-guide/sql/scalar_functions.html#string-functions

To test a function with StringView with datafusion-cli you can use an example like this (replacing starts_with with the relevant function)

> create table foo as values (arrow_cast('foo', 'Utf8View'), arrow_cast('bar', 'Utf8View'));
0 row(s) fetched.
Elapsed 0.043 seconds.

> select starts_with(column1, column2) from foo;
+--------------------------------------+
| starts_with(foo.column1,foo.column2) |
+--------------------------------------+
| false                                |
+--------------------------------------+
1 row(s) fetched.
Elapsed 0.015 seconds.

To see if it is using utf8 view, use EXPLAIN to see the plan and verify there is no CAST. In this example the CAST(column1@0 AS Utf8) indicates that the function is not using Utf8View natively

> explain select starts_with(column1, column2) from foo;
+---------------+------------------------------------------------------------------------------------------------------------------------------+
| plan_type     | plan                                                                                                                         |
+---------------+------------------------------------------------------------------------------------------------------------------------------+
| logical_plan  | Projection: starts_with(CAST(foo.column1 AS Utf8), CAST(foo.column2 AS Utf8))                                                |
|               |   TableScan: foo projection=[column1, column2]                                                                               |
| physical_plan | ProjectionExec: expr=[starts_with(CAST(column1@0 AS Utf8), CAST(column2@1 AS Utf8)) as starts_with(foo.column1,foo.column2)] |
|               |   MemoryExec: partitions=1, partition_sizes=[1]                                                                              |
|               |                                                                                                                              |
+---------------+------------------------------------------------------------------------------------------------------------------------------+
2 row(s) fetched.
Elapsed 0.006 seconds.

It is also often good to test with a constant as well (likewise there should be no cast):

> explain select starts_with(column1, 'foo') from foo;
+---------------+----------------------------------------------------------------------------------------------------------+
| plan_type     | plan                                                                                                     |
+---------------+----------------------------------------------------------------------------------------------------------+
| logical_plan  | Projection: starts_with(CAST(foo.column1 AS Utf8), Utf8("foo"))                                          |
|               |   TableScan: foo projection=[column1]                                                                    |
| physical_plan | ProjectionExec: expr=[starts_with(CAST(column1@0 AS Utf8), foo) as starts_with(foo.column1,Utf8("foo"))] |
|               |   MemoryExec: partitions=1, partition_sizes=[1]                                                          |
|               |                                                                                                          |
+---------------+----------------------------------------------------------------------------------------------------------+
2 row(s) fetched.
Elapsed 0.002 seconds.

### Additional context

_No response_

tshauck avatar Aug 05 '24 23:08 tshauck

take

dharanad avatar Aug 06 '24 01:08 dharanad

@dharanad Are you still working on this? If not I can take it on. I have done a few of the other StringView implementations so its fresh for me and can likely get it done relatively quickly.

devanbenz avatar Aug 28 '24 01:08 devanbenz

@dharanad Are you still working on this? If not I can take it on. I have done a few of the other StringView implementations so its fresh for me and can likely get it done relatively quickly.

Hey @devanbenz please feel free to take it.

dharanad avatar Aug 28 '24 01:08 dharanad

take

devanbenz avatar Aug 28 '24 02:08 devanbenz

@devanbenz Do you maybe want to also look at https://github.com/apache/datafusion/issues/11836? It looks like concat and concat_ws share a good chunk of code, so perhaps it'd be better to have the approach to be similar.

tshauck avatar Aug 28 '24 03:08 tshauck

@tshauck can do!

devanbenz avatar Aug 28 '24 04:08 devanbenz

I have a PR out for https://github.com/apache/datafusion/pull/12224 Will wait for that to get a review then work on this so I can adapt the changes here. 👍

devanbenz avatar Aug 29 '24 03:08 devanbenz