Files
turso/core/function.rs
Pekka Enberg e727b8e0dc Merge 'Vector improvements' from Nikita Sivukhin
This PR introduces sparse vectors support and jaccard distance
implementation.
Also, this PR restructure the code to have all vector operations in
separate files (they grow pretty quickly as new vector representations
added to the DB).

Closes #3647
2025-10-10 13:08:46 +03:00

888 lines
31 KiB
Rust

use std::fmt;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use turso_ext::{FinalizeFunction, InitAggFunction, ScalarFunction, StepFunction};
use crate::LimboError;
pub struct ExternalFunc {
pub name: String,
pub func: ExtFunc,
}
impl ExternalFunc {
pub fn is_deterministic(&self) -> bool {
false // external functions can be whatever so let's just default to false
}
}
#[derive(Debug, Clone)]
pub enum ExtFunc {
Scalar(ScalarFunction),
Aggregate {
argc: usize,
init: InitAggFunction,
step: StepFunction,
finalize: FinalizeFunction,
},
}
impl ExtFunc {
pub fn agg_args(&self) -> Result<usize, ()> {
if let ExtFunc::Aggregate { argc, .. } = self {
return Ok(*argc);
}
Err(())
}
}
impl ExternalFunc {
pub fn new_scalar(name: String, func: ScalarFunction) -> Self {
Self {
name,
func: ExtFunc::Scalar(func),
}
}
pub fn new_aggregate(
name: String,
argc: i32,
func: (InitAggFunction, StepFunction, FinalizeFunction),
) -> Self {
Self {
name,
func: ExtFunc::Aggregate {
argc: argc as usize,
init: func.0,
step: func.1,
finalize: func.2,
},
}
}
}
impl Debug for ExternalFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl Display for ExternalFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[cfg(feature = "json")]
#[derive(Debug, Clone, PartialEq)]
pub enum JsonFunc {
Json,
Jsonb,
JsonArray,
JsonbArray,
JsonArrayLength,
JsonArrowExtract,
JsonArrowShiftExtract,
JsonExtract,
JsonbExtract,
JsonObject,
JsonbObject,
JsonType,
JsonErrorPosition,
JsonValid,
JsonPatch,
JsonbPatch,
JsonRemove,
JsonbRemove,
JsonReplace,
JsonbReplace,
JsonInsert,
JsonbInsert,
JsonPretty,
JsonSet,
JsonbSet,
JsonQuote,
}
#[cfg(feature = "json")]
impl JsonFunc {
pub fn is_deterministic(&self) -> bool {
true
}
}
#[cfg(feature = "json")]
impl Display for JsonFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Json => "json".to_string(),
Self::Jsonb => "jsonb".to_string(),
Self::JsonArray => "json_array".to_string(),
Self::JsonbArray => "jsonb_array".to_string(),
Self::JsonExtract => "json_extract".to_string(),
Self::JsonbExtract => "jsonb_extract".to_string(),
Self::JsonArrayLength => "json_array_length".to_string(),
Self::JsonArrowExtract => "->".to_string(),
Self::JsonArrowShiftExtract => "->>".to_string(),
Self::JsonObject => "json_object".to_string(),
Self::JsonbObject => "jsonb_object".to_string(),
Self::JsonType => "json_type".to_string(),
Self::JsonErrorPosition => "json_error_position".to_string(),
Self::JsonValid => "json_valid".to_string(),
Self::JsonPatch => "json_patch".to_string(),
Self::JsonbPatch => "jsonb_patch".to_string(),
Self::JsonRemove => "json_remove".to_string(),
Self::JsonbRemove => "jsonb_remove".to_string(),
Self::JsonReplace => "json_replace".to_string(),
Self::JsonbReplace => "jsonb_replace".to_string(),
Self::JsonInsert => "json_insert".to_string(),
Self::JsonbInsert => "jsonb_insert".to_string(),
Self::JsonPretty => "json_pretty".to_string(),
Self::JsonSet => "json_set".to_string(),
Self::JsonbSet => "jsonb_set".to_string(),
Self::JsonQuote => "json_quote".to_string(),
}
)
}
}
#[derive(Debug, Clone)]
pub enum VectorFunc {
Vector,
Vector32,
Vector32Sparse,
Vector64,
VectorExtract,
VectorDistanceCos,
VectorDistanceL2,
VectorDistanceJaccard,
VectorConcat,
VectorSlice,
}
impl VectorFunc {
pub fn is_deterministic(&self) -> bool {
true
}
}
impl Display for VectorFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Self::Vector => "vector".to_string(),
Self::Vector32 => "vector32".to_string(),
Self::Vector32Sparse => "vector32_sparse".to_string(),
Self::Vector64 => "vector64".to_string(),
Self::VectorExtract => "vector_extract".to_string(),
Self::VectorDistanceCos => "vector_distance_cos".to_string(),
Self::VectorDistanceL2 => "vector_distance_l2".to_string(),
Self::VectorDistanceJaccard => "vector_distance_jaccard".to_string(),
Self::VectorConcat => "vector_concat".to_string(),
Self::VectorSlice => "vector_slice".to_string(),
};
write!(f, "{str}")
}
}
#[derive(Debug, Clone)]
pub enum AggFunc {
Avg,
Count,
Count0,
GroupConcat,
Max,
Min,
StringAgg,
Sum,
Total,
#[cfg(feature = "json")]
JsonbGroupArray,
#[cfg(feature = "json")]
JsonGroupArray,
#[cfg(feature = "json")]
JsonbGroupObject,
#[cfg(feature = "json")]
JsonGroupObject,
External(Arc<ExtFunc>),
}
impl PartialEq for AggFunc {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Avg, Self::Avg)
| (Self::Count, Self::Count)
| (Self::GroupConcat, Self::GroupConcat)
| (Self::Max, Self::Max)
| (Self::Min, Self::Min)
| (Self::StringAgg, Self::StringAgg)
| (Self::Sum, Self::Sum)
| (Self::Total, Self::Total) => true,
(Self::External(a), Self::External(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl AggFunc {
pub fn is_deterministic(&self) -> bool {
false // consider aggregate functions nondeterministic since they depend on the number of rows, not only the input arguments
}
pub fn num_args(&self) -> usize {
match self {
Self::Avg => 1,
Self::Count0 => 0,
Self::Count => 1,
Self::GroupConcat => 1,
Self::Max => 1,
Self::Min => 1,
Self::StringAgg => 2,
Self::Sum => 1,
Self::Total => 1,
#[cfg(feature = "json")]
Self::JsonGroupArray | Self::JsonbGroupArray => 1,
#[cfg(feature = "json")]
Self::JsonGroupObject | Self::JsonbGroupObject => 2,
Self::External(func) => func.agg_args().unwrap_or(0),
}
}
pub fn to_string(&self) -> &str {
match self {
Self::Avg => "avg",
Self::Count0 => "count",
Self::Count => "count",
Self::GroupConcat => "group_concat",
Self::Max => "max",
Self::Min => "min",
Self::StringAgg => "string_agg",
Self::Sum => "sum",
Self::Total => "total",
#[cfg(feature = "json")]
Self::JsonbGroupArray => "jsonb_group_array",
#[cfg(feature = "json")]
Self::JsonGroupArray => "json_group_array",
#[cfg(feature = "json")]
Self::JsonbGroupObject => "jsonb_group_object",
#[cfg(feature = "json")]
Self::JsonGroupObject => "json_group_object",
Self::External(_) => "extension function",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScalarFunc {
Cast,
Changes,
Char,
Coalesce,
Concat,
ConcatWs,
Glob,
IfNull,
Iif,
Instr,
Like,
Abs,
Upper,
Lower,
Random,
RandomBlob,
Trim,
LTrim,
RTrim,
Round,
Length,
OctetLength,
Min,
Max,
Nullif,
Sign,
Substr,
Substring,
Soundex,
Date,
Time,
TotalChanges,
DateTime,
Typeof,
Unicode,
Quote,
SqliteVersion,
TursoVersion,
SqliteSourceId,
UnixEpoch,
JulianDay,
Hex,
Unhex,
ZeroBlob,
LastInsertRowid,
Replace,
#[cfg(feature = "fs")]
#[cfg(not(target_family = "wasm"))]
LoadExtension,
StrfTime,
Printf,
Likely,
TimeDiff,
Likelihood,
TableColumnsJsonArray,
BinRecordJsonObject,
Attach,
Detach,
Unlikely,
}
impl ScalarFunc {
pub fn is_deterministic(&self) -> bool {
match self {
ScalarFunc::Cast => true,
ScalarFunc::Changes => false, // depends on DB state
ScalarFunc::Char => true,
ScalarFunc::Coalesce => true,
ScalarFunc::Concat => true,
ScalarFunc::ConcatWs => true,
ScalarFunc::Glob => true,
ScalarFunc::IfNull => true,
ScalarFunc::Iif => true,
ScalarFunc::Instr => true,
ScalarFunc::Like => true,
ScalarFunc::Abs => true,
ScalarFunc::Upper => true,
ScalarFunc::Lower => true,
ScalarFunc::Random => false, // duh
ScalarFunc::RandomBlob => false, // duh
ScalarFunc::Trim => true,
ScalarFunc::LTrim => true,
ScalarFunc::RTrim => true,
ScalarFunc::Round => true,
ScalarFunc::Length => true,
ScalarFunc::OctetLength => true,
ScalarFunc::Min => true,
ScalarFunc::Max => true,
ScalarFunc::Nullif => true,
ScalarFunc::Sign => true,
ScalarFunc::Substr => true,
ScalarFunc::Substring => true,
ScalarFunc::Soundex => true,
ScalarFunc::Date => false,
ScalarFunc::Time => false,
ScalarFunc::TotalChanges => false,
ScalarFunc::DateTime => false,
ScalarFunc::Typeof => true,
ScalarFunc::Unicode => true,
ScalarFunc::Quote => true,
ScalarFunc::SqliteVersion => true,
ScalarFunc::TursoVersion => true,
ScalarFunc::SqliteSourceId => true,
ScalarFunc::UnixEpoch => false,
ScalarFunc::JulianDay => false,
ScalarFunc::Hex => true,
ScalarFunc::Unhex => true,
ScalarFunc::ZeroBlob => true,
ScalarFunc::LastInsertRowid => false,
ScalarFunc::Replace => true,
#[cfg(feature = "fs")]
#[cfg(not(target_family = "wasm"))]
ScalarFunc::LoadExtension => true,
ScalarFunc::StrfTime => false,
ScalarFunc::Printf => false,
ScalarFunc::Likely => true,
ScalarFunc::TimeDiff => false,
ScalarFunc::Likelihood => true,
ScalarFunc::TableColumnsJsonArray => true, // while columns of the table can change with DDL statements, within single query plan it's static
ScalarFunc::BinRecordJsonObject => true,
ScalarFunc::Attach => false, // changes database state
ScalarFunc::Detach => false, // changes database state
ScalarFunc::Unlikely => true,
}
}
}
impl Display for ScalarFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Self::Cast => "cast".to_string(),
Self::Changes => "changes".to_string(),
Self::Char => "char".to_string(),
Self::Coalesce => "coalesce".to_string(),
Self::Concat => "concat".to_string(),
Self::ConcatWs => "concat_ws".to_string(),
Self::Glob => "glob".to_string(),
Self::IfNull => "ifnull".to_string(),
Self::Iif => "iif".to_string(),
Self::Instr => "instr".to_string(),
Self::Like => "like(2)".to_string(),
Self::Abs => "abs".to_string(),
Self::Upper => "upper".to_string(),
Self::Lower => "lower".to_string(),
Self::Random => "random".to_string(),
Self::RandomBlob => "randomblob".to_string(),
Self::Trim => "trim".to_string(),
Self::LTrim => "ltrim".to_string(),
Self::RTrim => "rtrim".to_string(),
Self::Round => "round".to_string(),
Self::Length => "length".to_string(),
Self::OctetLength => "octet_length".to_string(),
Self::Min => "min".to_string(),
Self::Max => "max".to_string(),
Self::Nullif => "nullif".to_string(),
Self::Sign => "sign".to_string(),
Self::Substr => "substr".to_string(),
Self::Substring => "substring".to_string(),
Self::Soundex => "soundex".to_string(),
Self::Date => "date".to_string(),
Self::Time => "time".to_string(),
Self::TotalChanges => "total_changes".to_string(),
Self::Typeof => "typeof".to_string(),
Self::Unicode => "unicode".to_string(),
Self::Quote => "quote".to_string(),
Self::SqliteVersion => "sqlite_version".to_string(),
Self::TursoVersion => "turso_version".to_string(),
Self::SqliteSourceId => "sqlite_source_id".to_string(),
Self::JulianDay => "julianday".to_string(),
Self::UnixEpoch => "unixepoch".to_string(),
Self::Hex => "hex".to_string(),
Self::Unhex => "unhex".to_string(),
Self::ZeroBlob => "zeroblob".to_string(),
Self::LastInsertRowid => "last_insert_rowid".to_string(),
Self::Replace => "replace".to_string(),
Self::DateTime => "datetime".to_string(),
#[cfg(feature = "fs")]
#[cfg(not(target_family = "wasm"))]
Self::LoadExtension => "load_extension".to_string(),
Self::StrfTime => "strftime".to_string(),
Self::Printf => "printf".to_string(),
Self::Likely => "likely".to_string(),
Self::TimeDiff => "timediff".to_string(),
Self::Likelihood => "likelihood".to_string(),
Self::TableColumnsJsonArray => "table_columns_json_array".to_string(),
Self::BinRecordJsonObject => "bin_record_json_object".to_string(),
Self::Attach => "attach".to_string(),
Self::Detach => "detach".to_string(),
Self::Unlikely => "unlikely".to_string(),
};
write!(f, "{str}")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MathFunc {
Acos,
Acosh,
Asin,
Asinh,
Atan,
Atan2,
Atanh,
Ceil,
Ceiling,
Cos,
Cosh,
Degrees,
Exp,
Floor,
Ln,
Log,
Log10,
Log2,
Mod,
Pi,
Pow,
Power,
Radians,
Sin,
Sinh,
Sqrt,
Tan,
Tanh,
Trunc,
}
pub enum MathFuncArity {
Nullary,
Unary,
Binary,
UnaryOrBinary,
}
impl MathFunc {
pub fn is_deterministic(&self) -> bool {
true
}
pub fn arity(&self) -> MathFuncArity {
match self {
Self::Pi => MathFuncArity::Nullary,
Self::Acos
| Self::Acosh
| Self::Asin
| Self::Asinh
| Self::Atan
| Self::Atanh
| Self::Ceil
| Self::Ceiling
| Self::Cos
| Self::Cosh
| Self::Degrees
| Self::Exp
| Self::Floor
| Self::Ln
| Self::Log10
| Self::Log2
| Self::Radians
| Self::Sin
| Self::Sinh
| Self::Sqrt
| Self::Tan
| Self::Tanh
| Self::Trunc => MathFuncArity::Unary,
Self::Atan2 | Self::Mod | Self::Pow | Self::Power => MathFuncArity::Binary,
Self::Log => MathFuncArity::UnaryOrBinary,
}
}
}
impl Display for MathFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
Self::Acos => "acos".to_string(),
Self::Acosh => "acosh".to_string(),
Self::Asin => "asin".to_string(),
Self::Asinh => "asinh".to_string(),
Self::Atan => "atan".to_string(),
Self::Atan2 => "atan2".to_string(),
Self::Atanh => "atanh".to_string(),
Self::Ceil => "ceil".to_string(),
Self::Ceiling => "ceiling".to_string(),
Self::Cos => "cos".to_string(),
Self::Cosh => "cosh".to_string(),
Self::Degrees => "degrees".to_string(),
Self::Exp => "exp".to_string(),
Self::Floor => "floor".to_string(),
Self::Ln => "ln".to_string(),
Self::Log => "log".to_string(),
Self::Log10 => "log10".to_string(),
Self::Log2 => "log2".to_string(),
Self::Mod => "mod".to_string(),
Self::Pi => "pi".to_string(),
Self::Pow => "pow".to_string(),
Self::Power => "power".to_string(),
Self::Radians => "radians".to_string(),
Self::Sin => "sin".to_string(),
Self::Sinh => "sinh".to_string(),
Self::Sqrt => "sqrt".to_string(),
Self::Tan => "tan".to_string(),
Self::Tanh => "tanh".to_string(),
Self::Trunc => "trunc".to_string(),
};
write!(f, "{str}")
}
}
#[derive(Debug)]
pub enum AlterTableFunc {
RenameTable,
AlterColumn,
RenameColumn,
}
impl Display for AlterTableFunc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AlterTableFunc::RenameTable => write!(f, "limbo_rename_table"),
AlterTableFunc::RenameColumn => write!(f, "limbo_rename_column"),
AlterTableFunc::AlterColumn => write!(f, "limbo_alter_column"),
}
}
}
#[derive(Debug)]
pub enum Func {
Agg(AggFunc),
Scalar(ScalarFunc),
Math(MathFunc),
Vector(VectorFunc),
#[cfg(feature = "json")]
Json(JsonFunc),
AlterTable(AlterTableFunc),
External(Arc<ExternalFunc>),
}
impl Display for Func {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Agg(agg_func) => write!(f, "{}", agg_func.to_string()),
Self::Scalar(scalar_func) => write!(f, "{scalar_func}"),
Self::Math(math_func) => write!(f, "{math_func}"),
Self::Vector(vector_func) => write!(f, "{vector_func}"),
#[cfg(feature = "json")]
Self::Json(json_func) => write!(f, "{json_func}"),
Self::External(generic_func) => write!(f, "{generic_func}"),
Self::AlterTable(alter_func) => write!(f, "{alter_func}"),
}
}
}
#[derive(Debug)]
pub struct FuncCtx {
pub func: Func,
pub arg_count: usize,
}
impl Func {
pub fn is_deterministic(&self) -> bool {
match self {
Self::Agg(agg_func) => agg_func.is_deterministic(),
Self::Scalar(scalar_func) => scalar_func.is_deterministic(),
Self::Math(math_func) => math_func.is_deterministic(),
Self::Vector(vector_func) => vector_func.is_deterministic(),
#[cfg(feature = "json")]
Self::Json(json_func) => json_func.is_deterministic(),
Self::External(external_func) => external_func.is_deterministic(),
Self::AlterTable(_) => true,
}
}
pub fn supports_star_syntax(&self) -> bool {
match self {
Self::Scalar(scalar_func) => {
matches!(
scalar_func,
ScalarFunc::Changes
| ScalarFunc::Random
| ScalarFunc::TotalChanges
| ScalarFunc::SqliteVersion
| ScalarFunc::TursoVersion
| ScalarFunc::SqliteSourceId
| ScalarFunc::LastInsertRowid
)
}
Self::Math(math_func) => {
matches!(math_func.arity(), MathFuncArity::Nullary)
}
// Aggregate functions with (*) syntax are handled separately in the planner
Self::Agg(_) => false,
_ => false,
}
}
pub fn resolve_function(name: &str, arg_count: usize) -> Result<Self, LimboError> {
let normalized_name = crate::util::normalize_ident(name);
match normalized_name.as_str() {
"avg" => {
if arg_count != 1 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::Avg))
}
"count" => {
// Handle both COUNT() and COUNT(expr) cases
if arg_count == 0 {
Ok(Self::Agg(AggFunc::Count0)) // COUNT() case
} else if arg_count == 1 {
Ok(Self::Agg(AggFunc::Count)) // COUNT(expr) case
} else {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
}
"group_concat" => {
if arg_count != 1 && arg_count != 2 {
println!("{arg_count}");
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::GroupConcat))
}
"max" if arg_count > 1 => Ok(Self::Scalar(ScalarFunc::Max)),
"max" => {
if arg_count < 1 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::Max))
}
"min" if arg_count > 1 => Ok(Self::Scalar(ScalarFunc::Min)),
"min" => {
if arg_count < 1 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::Min))
}
"nullif" if arg_count == 2 => Ok(Self::Scalar(ScalarFunc::Nullif)),
"string_agg" => {
if arg_count != 2 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::StringAgg))
}
"sum" => {
if arg_count != 1 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::Sum))
}
"total" => {
if arg_count != 1 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Agg(AggFunc::Total))
}
"timediff" => {
if arg_count != 2 {
crate::bail_parse_error!("wrong number of arguments to function {}()", name)
}
Ok(Self::Scalar(ScalarFunc::TimeDiff))
}
#[cfg(feature = "json")]
"jsonb_group_array" => Ok(Self::Agg(AggFunc::JsonbGroupArray)),
#[cfg(feature = "json")]
"json_group_array" => Ok(Self::Agg(AggFunc::JsonGroupArray)),
#[cfg(feature = "json")]
"jsonb_group_object" => Ok(Self::Agg(AggFunc::JsonbGroupObject)),
#[cfg(feature = "json")]
"json_group_object" => Ok(Self::Agg(AggFunc::JsonGroupObject)),
"char" => Ok(Self::Scalar(ScalarFunc::Char)),
"coalesce" => Ok(Self::Scalar(ScalarFunc::Coalesce)),
"concat" => Ok(Self::Scalar(ScalarFunc::Concat)),
"concat_ws" => Ok(Self::Scalar(ScalarFunc::ConcatWs)),
"changes" => Ok(Self::Scalar(ScalarFunc::Changes)),
"total_changes" => Ok(Self::Scalar(ScalarFunc::TotalChanges)),
"glob" => Ok(Self::Scalar(ScalarFunc::Glob)),
"ifnull" => Ok(Self::Scalar(ScalarFunc::IfNull)),
"iif" => Ok(Self::Scalar(ScalarFunc::Iif)),
"instr" => Ok(Self::Scalar(ScalarFunc::Instr)),
"like" => Ok(Self::Scalar(ScalarFunc::Like)),
"abs" => Ok(Self::Scalar(ScalarFunc::Abs)),
"upper" => Ok(Self::Scalar(ScalarFunc::Upper)),
"lower" => Ok(Self::Scalar(ScalarFunc::Lower)),
"random" => Ok(Self::Scalar(ScalarFunc::Random)),
"randomblob" => Ok(Self::Scalar(ScalarFunc::RandomBlob)),
"trim" => Ok(Self::Scalar(ScalarFunc::Trim)),
"ltrim" => Ok(Self::Scalar(ScalarFunc::LTrim)),
"rtrim" => Ok(Self::Scalar(ScalarFunc::RTrim)),
"round" => Ok(Self::Scalar(ScalarFunc::Round)),
"length" => Ok(Self::Scalar(ScalarFunc::Length)),
"octet_length" => Ok(Self::Scalar(ScalarFunc::OctetLength)),
"sign" => Ok(Self::Scalar(ScalarFunc::Sign)),
"substr" => Ok(Self::Scalar(ScalarFunc::Substr)),
"substring" => Ok(Self::Scalar(ScalarFunc::Substring)),
"date" => Ok(Self::Scalar(ScalarFunc::Date)),
"time" => Ok(Self::Scalar(ScalarFunc::Time)),
"datetime" => Ok(Self::Scalar(ScalarFunc::DateTime)),
"typeof" => Ok(Self::Scalar(ScalarFunc::Typeof)),
"last_insert_rowid" => Ok(Self::Scalar(ScalarFunc::LastInsertRowid)),
"unicode" => Ok(Self::Scalar(ScalarFunc::Unicode)),
"quote" => Ok(Self::Scalar(ScalarFunc::Quote)),
"sqlite_version" => Ok(Self::Scalar(ScalarFunc::SqliteVersion)),
"turso_version" => Ok(Self::Scalar(ScalarFunc::TursoVersion)),
"sqlite_source_id" => Ok(Self::Scalar(ScalarFunc::SqliteSourceId)),
"replace" => Ok(Self::Scalar(ScalarFunc::Replace)),
"likely" => Ok(Self::Scalar(ScalarFunc::Likely)),
"likelihood" => Ok(Self::Scalar(ScalarFunc::Likelihood)),
"unlikely" => Ok(Self::Scalar(ScalarFunc::Unlikely)),
#[cfg(feature = "json")]
"json" => Ok(Self::Json(JsonFunc::Json)),
#[cfg(feature = "json")]
"jsonb" => Ok(Self::Json(JsonFunc::Jsonb)),
#[cfg(feature = "json")]
"json_array_length" => Ok(Self::Json(JsonFunc::JsonArrayLength)),
#[cfg(feature = "json")]
"json_array" => Ok(Self::Json(JsonFunc::JsonArray)),
#[cfg(feature = "json")]
"jsonb_array" => Ok(Self::Json(JsonFunc::JsonbArray)),
#[cfg(feature = "json")]
"json_extract" => Ok(Func::Json(JsonFunc::JsonExtract)),
#[cfg(feature = "json")]
"jsonb_extract" => Ok(Func::Json(JsonFunc::JsonbExtract)),
#[cfg(feature = "json")]
"json_object" => Ok(Func::Json(JsonFunc::JsonObject)),
#[cfg(feature = "json")]
"jsonb_object" => Ok(Func::Json(JsonFunc::JsonbObject)),
#[cfg(feature = "json")]
"json_type" => Ok(Func::Json(JsonFunc::JsonType)),
#[cfg(feature = "json")]
"json_error_position" => Ok(Self::Json(JsonFunc::JsonErrorPosition)),
#[cfg(feature = "json")]
"json_valid" => Ok(Self::Json(JsonFunc::JsonValid)),
#[cfg(feature = "json")]
"json_patch" => Ok(Self::Json(JsonFunc::JsonPatch)),
#[cfg(feature = "json")]
"json_remove" => Ok(Self::Json(JsonFunc::JsonRemove)),
#[cfg(feature = "json")]
"jsonb_remove" => Ok(Self::Json(JsonFunc::JsonbRemove)),
#[cfg(feature = "json")]
"json_replace" => Ok(Self::Json(JsonFunc::JsonReplace)),
#[cfg(feature = "json")]
"json_insert" => Ok(Self::Json(JsonFunc::JsonInsert)),
#[cfg(feature = "json")]
"jsonb_insert" => Ok(Self::Json(JsonFunc::JsonbInsert)),
#[cfg(feature = "json")]
"jsonb_replace" => Ok(Self::Json(JsonFunc::JsonReplace)),
#[cfg(feature = "json")]
"json_pretty" => Ok(Self::Json(JsonFunc::JsonPretty)),
#[cfg(feature = "json")]
"json_set" => Ok(Self::Json(JsonFunc::JsonSet)),
#[cfg(feature = "json")]
"jsonb_set" => Ok(Self::Json(JsonFunc::JsonbSet)),
#[cfg(feature = "json")]
"json_quote" => Ok(Self::Json(JsonFunc::JsonQuote)),
"unixepoch" => Ok(Self::Scalar(ScalarFunc::UnixEpoch)),
"julianday" => Ok(Self::Scalar(ScalarFunc::JulianDay)),
"hex" => Ok(Self::Scalar(ScalarFunc::Hex)),
"unhex" => Ok(Self::Scalar(ScalarFunc::Unhex)),
"zeroblob" => Ok(Self::Scalar(ScalarFunc::ZeroBlob)),
"soundex" => Ok(Self::Scalar(ScalarFunc::Soundex)),
"table_columns_json_array" => Ok(Self::Scalar(ScalarFunc::TableColumnsJsonArray)),
"bin_record_json_object" => Ok(Self::Scalar(ScalarFunc::BinRecordJsonObject)),
"acos" => Ok(Self::Math(MathFunc::Acos)),
"acosh" => Ok(Self::Math(MathFunc::Acosh)),
"asin" => Ok(Self::Math(MathFunc::Asin)),
"asinh" => Ok(Self::Math(MathFunc::Asinh)),
"atan" => Ok(Self::Math(MathFunc::Atan)),
"atan2" => Ok(Self::Math(MathFunc::Atan2)),
"atanh" => Ok(Self::Math(MathFunc::Atanh)),
"ceil" => Ok(Self::Math(MathFunc::Ceil)),
"ceiling" => Ok(Self::Math(MathFunc::Ceiling)),
"cos" => Ok(Self::Math(MathFunc::Cos)),
"cosh" => Ok(Self::Math(MathFunc::Cosh)),
"degrees" => Ok(Self::Math(MathFunc::Degrees)),
"exp" => Ok(Self::Math(MathFunc::Exp)),
"floor" => Ok(Self::Math(MathFunc::Floor)),
"ln" => Ok(Self::Math(MathFunc::Ln)),
"log" => Ok(Self::Math(MathFunc::Log)),
"log10" => Ok(Self::Math(MathFunc::Log10)),
"log2" => Ok(Self::Math(MathFunc::Log2)),
"mod" => Ok(Self::Math(MathFunc::Mod)),
"pi" => Ok(Self::Math(MathFunc::Pi)),
"pow" => Ok(Self::Math(MathFunc::Pow)),
"power" => Ok(Self::Math(MathFunc::Power)),
"radians" => Ok(Self::Math(MathFunc::Radians)),
"sin" => Ok(Self::Math(MathFunc::Sin)),
"sinh" => Ok(Self::Math(MathFunc::Sinh)),
"sqrt" => Ok(Self::Math(MathFunc::Sqrt)),
"tan" => Ok(Self::Math(MathFunc::Tan)),
"tanh" => Ok(Self::Math(MathFunc::Tanh)),
"trunc" => Ok(Self::Math(MathFunc::Trunc)),
#[cfg(feature = "fs")]
#[cfg(not(target_family = "wasm"))]
"load_extension" => Ok(Self::Scalar(ScalarFunc::LoadExtension)),
"strftime" => Ok(Self::Scalar(ScalarFunc::StrfTime)),
"printf" => Ok(Self::Scalar(ScalarFunc::Printf)),
"vector" => Ok(Self::Vector(VectorFunc::Vector)),
"vector32" => Ok(Self::Vector(VectorFunc::Vector32)),
"vector32_sparse" => Ok(Self::Vector(VectorFunc::Vector32Sparse)),
"vector64" => Ok(Self::Vector(VectorFunc::Vector64)),
"vector_extract" => Ok(Self::Vector(VectorFunc::VectorExtract)),
"vector_distance_cos" => Ok(Self::Vector(VectorFunc::VectorDistanceCos)),
"vector_distance_l2" => Ok(Self::Vector(VectorFunc::VectorDistanceL2)),
"vector_distance_jaccard" => Ok(Self::Vector(VectorFunc::VectorDistanceJaccard)),
"vector_concat" => Ok(Self::Vector(VectorFunc::VectorConcat)),
"vector_slice" => Ok(Self::Vector(VectorFunc::VectorSlice)),
_ => crate::bail_parse_error!("no such function: {}", name),
}
}
}