Repository: databendlabs/databend (Rust)

Issue:
bug: `trim_both` function panic if the `trim_str` is empty
### Search before asking

- [X] I had searched in the [issues](https://github.com/datafuselabs/databend/issues) and found no similar issues.


### Version

v1.2.99-nightly

### What's Wrong?

`trim_both` function panic if the `trim_str` is empty

### How to Reproduce?

```sql
mysql> SELECT trim_both('aaa', '');
ERROR 1105 (HY000): PanicError. Code: 1104, Text = chunk size must be non-zero.
```

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!


Discussion:
/assignme 
`trim_leading` and `trim_trailing` have the same problem, it's best to fix them together.




Patch:
diff --git a/src/query/functions/src/scalars/string.rs b/src/query/functions/src/scalars/string.rs
index 6460af32548d2..98e876611275a 100644
--- a/src/query/functions/src/scalars/string.rs
+++ b/src/query/functions/src/scalars/string.rs
@@ -433,6 +433,12 @@ pub fn register(registry: &mut FunctionRegistry) {
             |col, _| col.data().len(),
             |val, trim_str, _, output| {
                 let chunk_size = trim_str.len();
+                if chunk_size == 0 {
+                    output.put_slice(val);
+                    output.commit_row();
+                    return;
+                }
+
                 let pos = val.chunks(chunk_size).position(|chunk| chunk != trim_str);
                 if let Some(idx) = pos {
                     output.put_slice(&val.as_bytes()[idx * chunk_size..]);
@@ -449,6 +455,12 @@ pub fn register(registry: &mut FunctionRegistry) {
             |col, _| col.data().len(),
             |val, trim_str, _, output| {
                 let chunk_size = trim_str.len();
+                if chunk_size == 0 {
+                    output.put_slice(val);
+                    output.commit_row();
+                    return;
+                }
+
                 let pos = val.rchunks(chunk_size).position(|chunk| chunk != trim_str);
                 if let Some(idx) = pos {
                     output.put_slice(&val.as_bytes()[..val.len() - idx * chunk_size]);
@@ -465,6 +477,12 @@ pub fn register(registry: &mut FunctionRegistry) {
             |col, _| col.data().len(),
             |val, trim_str, _, output| {
                 let chunk_size = trim_str.len();
+                if chunk_size == 0 {
+                    output.put_slice(val);
+                    output.commit_row();
+                    return;
+                }
+
                 let start_pos = val.chunks(chunk_size).position(|chunk| chunk != trim_str);
 
                 // Trim all


Test patch:
diff --git a/src/query/functions/tests/it/scalars/string.rs b/src/query/functions/tests/it/scalars/string.rs
index ac4d6309daeda..9d9f7067d5d43 100644
--- a/src/query/functions/tests/it/scalars/string.rs
+++ b/src/query/functions/tests/it/scalars/string.rs
@@ -208,10 +208,14 @@ fn test_trim_leading(file: &mut impl Write) {
     run_ast(file, "trim_leading('aaabbaaa', 'b')", &[]);
     run_ast(file, "trim_leading(NULL, 'a')", &[]);
     run_ast(file, "trim_leading('aaaaaaaa', NULL)", &[]);
+    run_ast(file, "trim_leading('aaaaaaaa', '')", &[]);
 
     let table = [
-        ("a", StringType::from_data(&["aabbaa", "bbccbb", "ccddcc"])),
-        ("b", StringType::from_data(&["a", "b", "c"])),
+        (
+            "a",
+            StringType::from_data(&["aabbaa", "bbccbb", "ccddcc", "aabbaa"]),
+        ),
+        ("b", StringType::from_data(&["a", "b", "c", ""])),
     ];
 
     run_ast(file, "trim_leading(a, 'a')", &table);
@@ -226,10 +230,14 @@ fn test_trim_trailing(file: &mut impl Write) {
     run_ast(file, "trim_trailing('aaabbaaa', 'b')", &[]);
     run_ast(file, "trim_trailing(NULL, 'a')", &[]);
     run_ast(file, "trim_trailing('aaaaaaaa', NULL)", &[]);
+    run_ast(file, "trim_trailing('aaaaaaaa', '')", &[]);
 
     let table = [
-        ("a", StringType::from_data(&["aabbaa", "bbccbb", "ccddcc"])),
-        ("b", StringType::from_data(&["a", "b", "c"])),
+        (
+            "a",
+            StringType::from_data(&["aabbaa", "bbccbb", "ccddcc", "aabbaa"]),
+        ),
+        ("b", StringType::from_data(&["a", "b", "c", ""])),
     ];
 
     run_ast(file, "trim_trailing(a, 'b')", &table);
@@ -244,10 +252,14 @@ fn test_trim_both(file: &mut impl Write) {
     run_ast(file, "trim_both('aaabbaaa', 'b')", &[]);
     run_ast(file, "trim_both(NULL, 'a')", &[]);
     run_ast(file, "trim_both('aaaaaaaa', NULL)", &[]);
+    run_ast(file, "trim_both('aaaaaaaa', '')", &[]);
 
     let table = [
-        ("a", StringType::from_data(&["aabbaa", "bbccbb", "ccddcc"])),
-        ("b", StringType::from_data(&["a", "b", "c"])),
+        (
+            "a",
+            StringType::from_data(&["aabbaa", "bbccbb", "ccddcc", "aabbaa"]),
+        ),
+        ("b", StringType::from_data(&["a", "b", "c", ""])),
     ];
 
     run_ast(file, "trim_both(a, 'a')", &table);
diff --git a/src/query/functions/tests/it/scalars/testdata/string.txt b/src/query/functions/tests/it/scalars/testdata/string.txt
index d6c26d8389bd1..0ab25312f3fd7 100644
--- a/src/query/functions/tests/it/scalars/testdata/string.txt
+++ b/src/query/functions/tests/it/scalars/testdata/string.txt
@@ -815,6 +815,15 @@ output domain  : {NULL}
 output         : NULL
 
 
+ast            : trim_leading('aaaaaaaa', '')
+raw expr       : trim_leading('aaaaaaaa', '')
+checked expr   : trim_leading<String, String>("aaaaaaaa", "")
+optimized expr : "aaaaaaaa"
+output type    : String
+output domain  : {"aaaaaaaa"..="aaaaaaaa"}
+output         : 'aaaaaaaa'
+
+
 ast            : trim_leading(a, 'a')
 raw expr       : trim_leading(a::String, 'a')
 checked expr   : trim_leading<String, String>(a, "a")
@@ -827,59 +836,62 @@ evaluation:
 | Row 0  | 'aabbaa'              | 'bbaa'   |
 | Row 1  | 'bbccbb'              | 'bbccbb' |
 | Row 2  | 'ccddcc'              | 'ccddcc' |
+| Row 3  | 'aabbaa'              | 'bbaa'   |
 +--------+-----------------------+----------+
 evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| Output | StringColumn { data: 0x62626161626263636262636364646363, offsets: [0, 4, 10, 16] }     |
-+--------+----------------------------------------------------------------------------------------+
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| Output | StringColumn { data: 0x6262616162626363626263636464636362626161, offsets: [0, 4, 10, 16, 20] }         |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_leading(a, b)
 raw expr       : trim_leading(a::String, b::String)
 checked expr   : trim_leading<String, String>(a, b)
 evaluation:
-+--------+-----------------------+-------------+--------+
-|        | a                     | b           | Output |
-+--------+-----------------------+-------------+--------+
-| Type   | String                | String      | String |
-| Domain | {"aabbaa"..="ccddcc"} | {"a"..="c"} | {""..} |
-| Row 0  | 'aabbaa'              | 'a'         | 'bbaa' |
-| Row 1  | 'bbccbb'              | 'b'         | 'ccbb' |
-| Row 2  | 'ccddcc'              | 'c'         | 'ddcc' |
-+--------+-----------------------+-------------+--------+
-evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }                                 |
-| Output | StringColumn { data: 0x626261616363626264646363, offsets: [0, 4, 8, 12] }              |
-+--------+----------------------------------------------------------------------------------------+
++--------+-----------------------+------------+----------+
+|        | a                     | b          | Output   |
++--------+-----------------------+------------+----------+
+| Type   | String                | String     | String   |
+| Domain | {"aabbaa"..="ccddcc"} | {""..="c"} | {""..}   |
+| Row 0  | 'aabbaa'              | 'a'        | 'bbaa'   |
+| Row 1  | 'bbccbb'              | 'b'        | 'ccbb'   |
+| Row 2  | 'ccddcc'              | 'c'        | 'ddcc'   |
+| Row 3  | 'aabbaa'              | ''         | 'aabbaa' |
++--------+-----------------------+------------+----------+
+evaluation (internal):
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                                              |
+| Output | StringColumn { data: 0x626261616363626264646363616162626161, offsets: [0, 4, 8, 12, 18] }              |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_leading('aba', b)
 raw expr       : trim_leading('aba', b::String)
 checked expr   : trim_leading<String, String>("aba", b)
 evaluation:
-+--------+-------------+--------+
-|        | b           | Output |
-+--------+-------------+--------+
-| Type   | String      | String |
-| Domain | {"a"..="c"} | {""..} |
-| Row 0  | 'a'         | 'ba'   |
-| Row 1  | 'b'         | 'aba'  |
-| Row 2  | 'c'         | 'aba'  |
-+--------+-------------+--------+
-evaluation (internal):
-+--------+------------------------------------------------------------------+
-| Column | Data                                                             |
-+--------+------------------------------------------------------------------+
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }           |
-| Output | StringColumn { data: 0x6261616261616261, offsets: [0, 2, 5, 8] } |
-+--------+------------------------------------------------------------------+
++--------+------------+--------+
+|        | b          | Output |
++--------+------------+--------+
+| Type   | String     | String |
+| Domain | {""..="c"} | {""..} |
+| Row 0  | 'a'        | 'ba'   |
+| Row 1  | 'b'        | 'aba'  |
+| Row 2  | 'c'        | 'aba'  |
+| Row 3  | ''         | 'aba'  |
++--------+------------+--------+
+evaluation (internal):
++--------+----------------------------------------------------------------------------+
+| Column | Data                                                                       |
++--------+----------------------------------------------------------------------------+
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                  |
+| Output | StringColumn { data: 0x6261616261616261616261, offsets: [0, 2, 5, 8, 11] } |
++--------+----------------------------------------------------------------------------+
 
 
 ast            : trim_trailing('aaabbaaa', 'a')
@@ -936,6 +948,15 @@ output domain  : {NULL}
 output         : NULL
 
 
+ast            : trim_trailing('aaaaaaaa', '')
+raw expr       : trim_trailing('aaaaaaaa', '')
+checked expr   : trim_trailing<String, String>("aaaaaaaa", "")
+optimized expr : "aaaaaaaa"
+output type    : String
+output domain  : {"aaaaaaaa"..="aaaaaaaa"}
+output         : 'aaaaaaaa'
+
+
 ast            : trim_trailing(a, 'b')
 raw expr       : trim_trailing(a::String, 'b')
 checked expr   : trim_trailing<String, String>(a, "b")
@@ -948,59 +969,62 @@ evaluation:
 | Row 0  | 'aabbaa'              | 'aabbaa' |
 | Row 1  | 'bbccbb'              | 'bbcc'   |
 | Row 2  | 'ccddcc'              | 'ccddcc' |
+| Row 3  | 'aabbaa'              | 'aabbaa' |
 +--------+-----------------------+----------+
 evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| Output | StringColumn { data: 0x61616262616162626363636364646363, offsets: [0, 6, 10, 16] }     |
-+--------+----------------------------------------------------------------------------------------+
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| Output | StringColumn { data: 0x61616262616162626363636364646363616162626161, offsets: [0, 6, 10, 16, 22] }     |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_trailing(a, b)
 raw expr       : trim_trailing(a::String, b::String)
 checked expr   : trim_trailing<String, String>(a, b)
 evaluation:
-+--------+-----------------------+-------------+--------+
-|        | a                     | b           | Output |
-+--------+-----------------------+-------------+--------+
-| Type   | String                | String      | String |
-| Domain | {"aabbaa"..="ccddcc"} | {"a"..="c"} | {""..} |
-| Row 0  | 'aabbaa'              | 'a'         | 'aabb' |
-| Row 1  | 'bbccbb'              | 'b'         | 'bbcc' |
-| Row 2  | 'ccddcc'              | 'c'         | 'ccdd' |
-+--------+-----------------------+-------------+--------+
-evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }                                 |
-| Output | StringColumn { data: 0x616162626262636363636464, offsets: [0, 4, 8, 12] }              |
-+--------+----------------------------------------------------------------------------------------+
++--------+-----------------------+------------+----------+
+|        | a                     | b          | Output   |
++--------+-----------------------+------------+----------+
+| Type   | String                | String     | String   |
+| Domain | {"aabbaa"..="ccddcc"} | {""..="c"} | {""..}   |
+| Row 0  | 'aabbaa'              | 'a'        | 'aabb'   |
+| Row 1  | 'bbccbb'              | 'b'        | 'bbcc'   |
+| Row 2  | 'ccddcc'              | 'c'        | 'ccdd'   |
+| Row 3  | 'aabbaa'              | ''         | 'aabbaa' |
++--------+-----------------------+------------+----------+
+evaluation (internal):
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                                              |
+| Output | StringColumn { data: 0x616162626262636363636464616162626161, offsets: [0, 4, 8, 12, 18] }              |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_trailing('aba', b)
 raw expr       : trim_trailing('aba', b::String)
 checked expr   : trim_trailing<String, String>("aba", b)
 evaluation:
-+--------+-------------+--------+
-|        | b           | Output |
-+--------+-------------+--------+
-| Type   | String      | String |
-| Domain | {"a"..="c"} | {""..} |
-| Row 0  | 'a'         | 'ab'   |
-| Row 1  | 'b'         | 'aba'  |
-| Row 2  | 'c'         | 'aba'  |
-+--------+-------------+--------+
-evaluation (internal):
-+--------+------------------------------------------------------------------+
-| Column | Data                                                             |
-+--------+------------------------------------------------------------------+
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }           |
-| Output | StringColumn { data: 0x6162616261616261, offsets: [0, 2, 5, 8] } |
-+--------+------------------------------------------------------------------+
++--------+------------+--------+
+|        | b          | Output |
++--------+------------+--------+
+| Type   | String     | String |
+| Domain | {""..="c"} | {""..} |
+| Row 0  | 'a'        | 'ab'   |
+| Row 1  | 'b'        | 'aba'  |
+| Row 2  | 'c'        | 'aba'  |
+| Row 3  | ''         | 'aba'  |
++--------+------------+--------+
+evaluation (internal):
++--------+----------------------------------------------------------------------------+
+| Column | Data                                                                       |
++--------+----------------------------------------------------------------------------+
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                  |
+| Output | StringColumn { data: 0x6162616261616261616261, offsets: [0, 2, 5, 8, 11] } |
++--------+----------------------------------------------------------------------------+
 
 
 ast            : trim_both('aaabbaaa', 'a')
@@ -1057,6 +1081,15 @@ output domain  : {NULL}
 output         : NULL
 
 
+ast            : trim_both('aaaaaaaa', '')
+raw expr       : trim_both('aaaaaaaa', '')
+checked expr   : trim_both<String, String>("aaaaaaaa", "")
+optimized expr : "aaaaaaaa"
+output type    : String
+output domain  : {"aaaaaaaa"..="aaaaaaaa"}
+output         : 'aaaaaaaa'
+
+
 ast            : trim_both(a, 'a')
 raw expr       : trim_both(a::String, 'a')
 checked expr   : trim_both<String, String>(a, "a")
@@ -1069,59 +1102,62 @@ evaluation:
 | Row 0  | 'aabbaa'              | 'bb'     |
 | Row 1  | 'bbccbb'              | 'bbccbb' |
 | Row 2  | 'ccddcc'              | 'ccddcc' |
+| Row 3  | 'aabbaa'              | 'bb'     |
 +--------+-----------------------+----------+
 evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| Output | StringColumn { data: 0x6262626263636262636364646363, offsets: [0, 2, 8, 14] }          |
-+--------+----------------------------------------------------------------------------------------+
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| Output | StringColumn { data: 0x62626262636362626363646463636262, offsets: [0, 2, 8, 14, 16] }                  |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_both(a, b)
 raw expr       : trim_both(a::String, b::String)
 checked expr   : trim_both<String, String>(a, b)
 evaluation:
-+--------+-----------------------+-------------+--------+
-|        | a                     | b           | Output |
-+--------+-----------------------+-------------+--------+
-| Type   | String                | String      | String |
-| Domain | {"aabbaa"..="ccddcc"} | {"a"..="c"} | {""..} |
-| Row 0  | 'aabbaa'              | 'a'         | 'bb'   |
-| Row 1  | 'bbccbb'              | 'b'         | 'cc'   |
-| Row 2  | 'ccddcc'              | 'c'         | 'dd'   |
-+--------+-----------------------+-------------+--------+
-evaluation (internal):
-+--------+----------------------------------------------------------------------------------------+
-| Column | Data                                                                                   |
-+--------+----------------------------------------------------------------------------------------+
-| a      | StringColumn { data: 0x616162626161626263636262636364646363, offsets: [0, 6, 12, 18] } |
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }                                 |
-| Output | StringColumn { data: 0x626263636464, offsets: [0, 2, 4, 6] }                           |
-+--------+----------------------------------------------------------------------------------------+
++--------+-----------------------+------------+----------+
+|        | a                     | b          | Output   |
++--------+-----------------------+------------+----------+
+| Type   | String                | String     | String   |
+| Domain | {"aabbaa"..="ccddcc"} | {""..="c"} | {""..}   |
+| Row 0  | 'aabbaa'              | 'a'        | 'bb'     |
+| Row 1  | 'bbccbb'              | 'b'        | 'cc'     |
+| Row 2  | 'ccddcc'              | 'c'        | 'dd'     |
+| Row 3  | 'aabbaa'              | ''         | 'aabbaa' |
++--------+-----------------------+------------+----------+
+evaluation (internal):
++--------+--------------------------------------------------------------------------------------------------------+
+| Column | Data                                                                                                   |
++--------+--------------------------------------------------------------------------------------------------------+
+| a      | StringColumn { data: 0x616162626161626263636262636364646363616162626161, offsets: [0, 6, 12, 18, 24] } |
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                                              |
+| Output | StringColumn { data: 0x626263636464616162626161, offsets: [0, 2, 4, 6, 12] }                           |
++--------+--------------------------------------------------------------------------------------------------------+
 
 
 ast            : trim_both('aba', b)
 raw expr       : trim_both('aba', b::String)
 checked expr   : trim_both<String, String>("aba", b)
 evaluation:
-+--------+-------------+--------+
-|        | b           | Output |
-+--------+-------------+--------+
-| Type   | String      | String |
-| Domain | {"a"..="c"} | {""..} |
-| Row 0  | 'a'         | 'b'    |
-| Row 1  | 'b'         | 'aba'  |
-| Row 2  | 'c'         | 'aba'  |
-+--------+-------------+--------+
-evaluation (internal):
-+--------+----------------------------------------------------------------+
-| Column | Data                                                           |
-+--------+----------------------------------------------------------------+
-| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3] }         |
-| Output | StringColumn { data: 0x62616261616261, offsets: [0, 1, 4, 7] } |
-+--------+----------------------------------------------------------------+
++--------+------------+--------+
+|        | b          | Output |
++--------+------------+--------+
+| Type   | String     | String |
+| Domain | {""..="c"} | {""..} |
+| Row 0  | 'a'        | 'b'    |
+| Row 1  | 'b'        | 'aba'  |
+| Row 2  | 'c'        | 'aba'  |
+| Row 3  | ''         | 'aba'  |
++--------+------------+--------+
+evaluation (internal):
++--------+--------------------------------------------------------------------------+
+| Column | Data                                                                     |
++--------+--------------------------------------------------------------------------+
+| b      | StringColumn { data: 0x616263, offsets: [0, 1, 2, 3, 3] }                |
+| Output | StringColumn { data: 0x62616261616261616261, offsets: [0, 1, 4, 7, 10] } |
++--------+--------------------------------------------------------------------------+
 
 
 ast            : trim('   abc   ')
diff --git a/tests/sqllogictests/suites/query/02_function/02_0055_function_strings_trim b/tests/sqllogictests/suites/query/02_function/02_0055_function_strings_trim
index 9bc30bdff252e..9ffc2b20c32c8 100644
--- a/tests/sqllogictests/suites/query/02_function/02_0055_function_strings_trim
+++ b/tests/sqllogictests/suites/query/02_function/02_0055_function_strings_trim
@@ -10,3 +10,33 @@ query T
 select trim('abc')
 ----
 abc
+
+query T
+select trim_both('aaabbaaa', 'aa')
+----
+abba
+
+query T
+select trim_both('aaabbaaa', '')
+----
+aaabbaaa
+
+query T
+select trim_leading('aaabbaaa', 'aa')
+----
+abbaaa
+
+query T
+select trim_leading('aaabbaaa', '')
+----
+aaabbaaa
+
+query T
+select trim_trailing('aaabbaaa', 'aa')
+----
+aaabba
+
+query T
+select trim_trailing('aaabbaaa', '')
+----
+aaabbaaa