use crate::errors::ExecutorError;
use crate::evaluator::functions::coercion::{coerce_to_integer, coerce_to_string};
pub(in crate::evaluator::functions) fn position(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"POSITION requires exactly 2 arguments, got {}",
args.len()
)));
}
let needle = match coerce_to_string(&args[0]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let haystack = match coerce_to_string(&args[1]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let position = find_char_position(&haystack, &needle);
Ok(vibesql_types::SqlValue::Integer(position))
}
fn find_char_position(haystack: &str, needle: &str) -> i64 {
if needle.is_empty() {
return 1; }
if let Some(byte_pos) = haystack.find(needle) {
let char_pos = haystack[..byte_pos].chars().count();
(char_pos + 1) as i64 } else {
0 }
}
pub(in crate::evaluator::functions) fn instr(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"INSTR requires exactly 2 arguments, got {}",
args.len()
)));
}
if let (vibesql_types::SqlValue::Blob(haystack), vibesql_types::SqlValue::Blob(needle)) =
(&args[0], &args[1])
{
let position = find_byte_position(haystack, needle);
return Ok(vibesql_types::SqlValue::Integer(position));
}
let haystack = match coerce_to_string(&args[0]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let needle = match coerce_to_string(&args[1]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let position = find_char_position(&haystack, &needle);
Ok(vibesql_types::SqlValue::Integer(position))
}
fn find_byte_position(haystack: &[u8], needle: &[u8]) -> i64 {
if needle.is_empty() {
return 1; }
haystack
.windows(needle.len())
.position(|window| window == needle)
.map(|pos| (pos + 1) as i64) .unwrap_or(0) }
pub(in crate::evaluator::functions) fn locate(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() < 2 || args.len() > 3 {
return Err(ExecutorError::UnsupportedFeature(format!(
"LOCATE requires 2 or 3 arguments, got {}",
args.len()
)));
}
let needle = match coerce_to_string(&args[0]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let haystack = match coerce_to_string(&args[1]) {
Some(s) => s,
None => return Ok(vibesql_types::SqlValue::Null),
};
let start_pos = if args.len() == 3 {
match coerce_to_integer(&args[2]) {
Some(s) => {
((s - 1).max(0) as usize).min(haystack.len())
}
None => return Ok(vibesql_types::SqlValue::Null),
}
} else {
0
};
let position = if start_pos >= haystack.len() {
0 } else {
haystack[start_pos..]
.find(&needle)
.map(|pos| (pos + start_pos + 1) as i64) .unwrap_or(0)
};
Ok(vibesql_types::SqlValue::Integer(position))
}