diff --git a/core/types.rs b/core/types.rs index a5ddaa906..dc4b2162e 100644 --- a/core/types.rs +++ b/core/types.rs @@ -387,6 +387,7 @@ impl Value { }; } + /// Cast Value to String, if Value is NULL returns None pub fn cast_text(&self) -> Option { Some(match self { Value::Null => return None, diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index af75bd83b..ab645ef38 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -8352,12 +8352,12 @@ impl Value { } pub fn exec_substring( - str_value: &Value, + value: &Value, start_value: &Value, length_value: Option<&Value>, ) -> Value { - if let (Value::Text(str), Value::Integer(start)) = (str_value, start_value) { - let str_len = str.as_str().len() as i64; + if let (Some(str), Value::Integer(start)) = (value.cast_text(), start_value) { + let str_len = str.len() as i64; // The left-most character of X is number 1. // If Y is negative then the first character of the substring is found by counting from the right rather than the left. diff --git a/testing/scalar-functions.test b/testing/scalar-functions.test index 46133cd14..07892540e 100755 --- a/testing/scalar-functions.test +++ b/testing/scalar-functions.test @@ -622,6 +622,27 @@ do_execsql_test substring-2-args-exceed-length { SELECT substring('limbo', 10); } {} +do_execsql_test substring-with-blob { + select substr(x'446F6E7450616E696321', 0); +} {DontPanic!} + +do_execsql_test substring-with-blob-size { + select substr(x'446F6E7450616E696321', 5, 6); +} {Panic!} + +# https://github.com/tursodatabase/turso/issues/3306 +do_execsql_test substring-with-blob-sanity { + with t as (select hex(substr(x'414243',2,1)) as slice) select slice, typeof(slice), length(slice) from t; +} {42|text|2} + +do_execsql_test substring-with-numbers { + select substr(12345678987654, 5, 6); +} {567898} + +do_execsql_test substring-with-numbers { + select substr(12345678987654, 0); +} {12345678987654} + do_execsql_test typeof-null { SELECT typeof(null); } {null}