Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/user/content/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Wrap your release notes at the 80 character mark.
`mz_kafka_consumer_partitions` has an additional column `initial_high_offset`
containing the first reported `hi_offset` from the broker for each partition.

- Add `left` to the [string function](/sql/functions#string-func) suite.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent, always be adding release notes. 😃


{{% version-header v0.8.1 %}}

- Add [timelines](/sql/timelines) to all sources to prevent
Expand Down
3 changes: 3 additions & 0 deletions doc/user/data/sql_funcs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@
description: Encode `b` using the specified textual representation.
url: encode

- signature: 'left(s: str, n: int) -> str'
description: The first `n` characters of `s`. If `n` is negative, all but the last `|n|` characters of `s`.

- signature: 'length(s: str) -> int'
description: Number of code points in `s`
url: length
Expand Down
34 changes: 32 additions & 2 deletions src/expr/src/scalar/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2814,6 +2814,7 @@ pub enum BinaryFunc {
MapContainsAnyKeys,
MapContainsMap,
ConvertFrom,
Left,
Position,
Right,
RepeatString,
Expand Down Expand Up @@ -2987,6 +2988,7 @@ impl BinaryFunc {
BinaryFunc::ConvertFrom => eager!(convert_from),
BinaryFunc::Encode => eager!(encode, temp_storage),
BinaryFunc::Decode => eager!(decode, temp_storage),
BinaryFunc::Left => eager!(left),
BinaryFunc::Position => eager!(position),
BinaryFunc::Right => eager!(right),
BinaryFunc::Trim => Ok(eager!(trim)),
Expand Down Expand Up @@ -3043,8 +3045,8 @@ impl BinaryFunc {
ScalarType::Bool.nullable(true)
}

ToCharTimestamp | ToCharTimestampTz | ConvertFrom | Right | Trim | TrimLeading
| TrimTrailing => ScalarType::String.nullable(in_nullable),
ToCharTimestamp | ToCharTimestampTz | ConvertFrom | Left | Right | Trim
| TrimLeading | TrimTrailing => ScalarType::String.nullable(in_nullable),

AddInt32
| SubInt32
Expand Down Expand Up @@ -3370,6 +3372,7 @@ impl BinaryFunc {
| RoundDecimal(_)
| RoundAPD
| ConvertFrom
| Left
| Position
| Right
| Trim
Expand Down Expand Up @@ -3506,6 +3509,7 @@ impl fmt::Display for BinaryFunc {
BinaryFunc::RoundDecimal(_) => f.write_str("round"),
BinaryFunc::RoundAPD => f.write_str("round"),
BinaryFunc::ConvertFrom => f.write_str("convert_from"),
BinaryFunc::Left => f.write_str("left"),
BinaryFunc::Position => f.write_str("position"),
BinaryFunc::Right => f.write_str("right"),
BinaryFunc::Trim => f.write_str("btrim"),
Expand Down Expand Up @@ -5170,6 +5174,32 @@ fn position<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
}
}

fn left<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
let string: &'a str = a.unwrap_str();
let n = b.unwrap_int32();

let mut byte_indices = string.char_indices().map(|(i, _)| i);

let end_in_bytes = match n.cmp(&0) {
Ordering::Equal => 0,
Ordering::Greater => {
let n = usize::try_from(n).map_err(|_| {
EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n))
})?;
// nth from the back
byte_indices.nth(n).unwrap_or_else(|| string.len())
}
Ordering::Less => {
let n = usize::try_from(n.abs() - 1).map_err(|_| {
EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n))
})?;
byte_indices.rev().nth(n).unwrap_or(0)
}
};

Ok(Datum::String(&string[..end_in_bytes]))
}

fn right<'a>(a: Datum<'a>, b: Datum<'a>) -> Result<Datum<'a>, EvalError> {
let string: &'a str = a.unwrap_str();
let n = b.unwrap_int32();
Expand Down
3 changes: 3 additions & 0 deletions src/sql/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,9 @@ lazy_static! {
"jsonb_typeof" => Scalar {
params!(Jsonb) => UnaryFunc::JsonbTypeof, 3210;
},
"left" => Scalar {
params!(String, Int32) => BinaryFunc::Left, 3060;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OID confirmed!

},
"length" => Scalar {
params!(Bytes) => UnaryFunc::ByteLengthBytes, 2010;
params!(String) => UnaryFunc::CharLength, 1317;
Expand Down
122 changes: 122 additions & 0 deletions test/sqllogictest/string.slt
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,128 @@ SELECT position(e'\u0903\u0951' IN e'\u0930\u0903\u0951')
----
2

### left ###
statement ok
CREATE TABLE lefttest (strcol char(15), vccol varchar(15), smicol smallint, intcol int)

statement ok
INSERT INTO lefttest VALUES ('Mg', 'Mn', 1, -1), ('magnesium', 'manganese', 3, NULL),
(NULL, NULL, 0, 0), ('24.31', '54.94', 3, -3), ('长久不见', '爱不释手', NULL, 3),
('', '', -1, 2)

# invalid input
statement error
SELECT left(42)

statement error
SELECT left('str')

statement error
SELECT left(42, 'str')

statement error
SELECT left('str', 42, 17)

# standard tests

#TODO: materialize#589 select left(strcol, foo) from lefttest

# edge case
query T rowsort
SELECT left(vccol, 0) FROM lefttest
----
(empty)
(empty)
NULL
(empty)
(empty)
(empty)

# from the front
query T rowsort
SELECT left(vccol, 2) FROM lefttest
----
Mn
ma
NULL
54
爱不
(empty)

# from the back
query T rowsort
SELECT left(vccol, -2) FROM lefttest
----
(empty)
mangane
NULL
54.
爱不
(empty)

query TT rowsort
SELECT left(vccol, smicol), left(vccol, intcol) FROM lefttest
----
M M
man NULL
NULL NULL
54. 54
NULL 爱不释
(empty) (empty)


# NULL inputs
query T
SELECT left(NULL, 99)
----
NULL

query T
SELECT left('str', NULL)
----
NULL

# combining characters

query T
SELECT left('रः॑', 0)
----
(empty)

query T
SELECT left('रः॑', 1)
----

query T
SELECT left('रः॑', -1)
----
रः

# this is exactly the same as above, but using unicode escapes
query T
SELECT left(e'\u0930\u0903\u0951', -1)
----
रः

# integer edge cases
Comment thread
antiguru marked this conversation as resolved.

# i32:MIN
query T
SELECT left('hello', -2147483648)
----
(empty)

# i32:MIN + 1
query T
SELECT left('hello', -2147483647)
----
(empty)

# i64
query error arguments cannot be implicitly cast to any implementation's parameters
SELECT left('hello', 2147483648)

### right ###
statement ok
CREATE TABLE righttest (strcol char(15), vccol varchar(15), smicol smallint, intcol int)
Expand Down