const NEEDS_A_COMPONENT: &[(&[u8], &str)] = &[(
b"embed",
"embed(TEXT): this build has no embedding support compiled in",
)];
pub fn needs_a_component(name: &[u8]) -> Option<&'static str> {
NEEDS_A_COMPONENT
.iter()
.find(|(known, _)| *known == name)
.map(|(_, said)| *said)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ScalarFunc {
Abs,
Char,
Coalesce,
Concat,
ConcatWs,
Glob,
Hex,
IfNull,
Iif,
Instr,
Length,
Like,
Likelihood,
Lower,
LTrim,
Max,
Min,
NullIf,
Quote,
Replace,
Round,
RTrim,
Sign,
Substr,
Trim,
TypeOf,
Unhex,
Unicode,
Upper,
ZeroBlob,
Printf,
OctetLength,
Random,
RandomBlob,
Changes,
TotalChanges,
LastInsertRowid,
SourceId,
Fts5SourceId,
Version,
VectorDistanceCos,
VectorDistanceL2,
VectorDot,
VectorDistanceL1,
VectorDistanceHamming,
VectorDistanceJaccard,
VectorDims,
VectorNorm,
VectorNormalize,
VectorQuantize,
VectorSlice,
VectorAdd,
VectorSubtract,
VectorMultiply,
VectorConcat,
GeopolyArea,
GeopolyBlob,
GeopolyJson,
GeopolySvg,
GeopolyWithin,
GeopolyContainsPoint,
GeopolyOverlap,
GeopolyDebug,
GeopolyBbox,
GeopolyXform,
GeopolyRegular,
GeopolyCcw,
Unknown,
Subtype,
Unistr,
UnistrQuote,
CompileOptionUsed,
CompileOptionGet,
Log,
LoadExtension,
Regexp,
SqlarCompress,
SqlarUncompress,
Offset,
RTreeDepth,
RTreeNode,
RTreeCheck,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AggregateFunc {
Count,
Sum,
Total,
Avg,
Min,
Max,
GroupConcat,
JsonGroupArray,
JsonbGroupArray,
JsonGroupObject,
JsonbGroupObject,
Median,
GeopolyGroupBbox,
VectorSum,
VectorAvg,
Percentile,
PercentileCont,
PercentileDisc,
External,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FunctionFlags {
pub direct_only: bool,
pub innocuous: bool,
pub deterministic: bool,
}
impl FunctionFlags {
pub fn builtin() -> FunctionFlags {
FunctionFlags {
direct_only: false,
innocuous: true,
deterministic: true,
}
}
pub fn external() -> FunctionFlags {
FunctionFlags {
direct_only: true,
innocuous: false,
deterministic: false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallSite {
Statement,
Schema,
}
pub fn schema_refusal(
flags: FunctionFlags,
site: CallSite,
trusted_schema: bool,
) -> Option<&'static str> {
if site == CallSite::Statement {
return None;
}
if flags.direct_only {
return Some("may only be used from top-level SQL");
}
if trusted_schema || flags.innocuous {
return None;
}
Some("is not allowed in a schema")
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExternalFunction {
pub name: Vec<u8>,
pub arity: i32,
pub aggregate: bool,
pub flags: FunctionFlags,
}
impl ExternalFunction {
pub fn accepts(&self, argc: usize) -> bool {
self.arity < 0 || self.arity as usize == argc
}
}
pub fn lookup_external<'a>(
functions: &'a [ExternalFunction],
name: &[u8],
argc: usize,
) -> Option<&'a ExternalFunction> {
let folded = name.to_ascii_lowercase();
functions
.iter()
.find(|function| function.name == folded && function.arity as usize == argc)
.or_else(|| {
functions
.iter()
.find(|function| function.name == folded && function.arity < 0)
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TimeFunc {
Date,
Time,
DateTime,
JulianDay,
UnixEpoch,
StrfTime,
TimeDiff,
}
pub fn lookup_time(folded: &[u8]) -> Option<TimeFunc> {
let func = match folded {
b"date" => TimeFunc::Date,
b"time" => TimeFunc::Time,
b"datetime" => TimeFunc::DateTime,
b"julianday" => TimeFunc::JulianDay,
b"unixepoch" => TimeFunc::UnixEpoch,
b"strftime" => TimeFunc::StrfTime,
b"timediff" => TimeFunc::TimeDiff,
_ => return None,
};
Some(func)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MathFunc {
Acos,
Acosh,
Asin,
Asinh,
Atan,
Atan2,
Atanh,
Ceil,
Cos,
Cosh,
Degrees,
Exp,
Floor,
Ln,
Log,
Log10,
Log2,
Mod,
Pi,
Pow,
Radians,
Sin,
Sinh,
Sqrt,
Tan,
Tanh,
Trunc,
}
impl MathFunc {
pub fn arity(self) -> (usize, usize) {
match self {
MathFunc::Pi => (0, 0),
MathFunc::Atan2 | MathFunc::Mod | MathFunc::Pow => (2, 2),
MathFunc::Log => (1, 2),
_ => (1, 1),
}
}
}
pub fn lookup_math(folded: &[u8]) -> Option<MathFunc> {
let func = match folded {
b"acos" => MathFunc::Acos,
b"acosh" => MathFunc::Acosh,
b"asin" => MathFunc::Asin,
b"asinh" => MathFunc::Asinh,
b"atan" => MathFunc::Atan,
b"atan2" => MathFunc::Atan2,
b"atanh" => MathFunc::Atanh,
b"ceil" | b"ceiling" => MathFunc::Ceil,
b"cos" => MathFunc::Cos,
b"cosh" => MathFunc::Cosh,
b"degrees" => MathFunc::Degrees,
b"exp" => MathFunc::Exp,
b"floor" => MathFunc::Floor,
b"ln" => MathFunc::Ln,
b"log" => MathFunc::Log,
b"log10" => MathFunc::Log10,
b"log2" => MathFunc::Log2,
b"mod" => MathFunc::Mod,
b"pi" => MathFunc::Pi,
b"pow" | b"power" => MathFunc::Pow,
b"radians" => MathFunc::Radians,
b"sin" => MathFunc::Sin,
b"sinh" => MathFunc::Sinh,
b"sqrt" => MathFunc::Sqrt,
b"tan" => MathFunc::Tan,
b"tanh" => MathFunc::Tanh,
b"trunc" => MathFunc::Trunc,
_ => return None,
};
Some(func)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WindowFunc {
RowNumber,
Rank,
DenseRank,
PercentRank,
CumeDist,
Ntile,
Lag,
Lead,
FirstValue,
LastValue,
NthValue,
}
impl WindowFunc {
pub fn arity(self) -> (usize, usize) {
match self {
WindowFunc::RowNumber
| WindowFunc::Rank
| WindowFunc::DenseRank
| WindowFunc::PercentRank
| WindowFunc::CumeDist => (0, 0),
WindowFunc::Ntile | WindowFunc::FirstValue | WindowFunc::LastValue => (1, 1),
WindowFunc::NthValue => (2, 2),
WindowFunc::Lag | WindowFunc::Lead => (1, 3),
}
}
}
pub fn lookup_window(folded: &[u8]) -> Option<WindowFunc> {
let func = match folded {
b"row_number" => WindowFunc::RowNumber,
b"rank" => WindowFunc::Rank,
b"dense_rank" => WindowFunc::DenseRank,
b"percent_rank" => WindowFunc::PercentRank,
b"cume_dist" => WindowFunc::CumeDist,
b"ntile" => WindowFunc::Ntile,
b"lag" => WindowFunc::Lag,
b"lead" => WindowFunc::Lead,
b"first_value" => WindowFunc::FirstValue,
b"last_value" => WindowFunc::LastValue,
b"nth_value" => WindowFunc::NthValue,
_ => return None,
};
Some(func)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum JsonFunc {
Json,
Jsonb,
Array,
ArrayB,
ArrayLength,
ErrorPosition,
Extract,
ExtractB,
Arrow,
ArrowShift,
Insert,
InsertB,
Object,
ObjectB,
Patch,
PatchB,
Pretty,
Remove,
RemoveB,
Replace,
ReplaceB,
Set,
SetB,
Type,
Valid,
Quote,
ArrayInsert,
ArrayInsertB,
}
impl JsonFunc {
pub fn arity(self) -> (usize, usize) {
match self {
JsonFunc::Json | JsonFunc::Jsonb | JsonFunc::ErrorPosition | JsonFunc::Quote => (1, 1),
JsonFunc::Array | JsonFunc::ArrayB | JsonFunc::Object | JsonFunc::ObjectB => {
(0, usize::MAX)
}
JsonFunc::ArrayLength | JsonFunc::Type | JsonFunc::Valid | JsonFunc::Pretty => (1, 2),
JsonFunc::Patch | JsonFunc::PatchB | JsonFunc::Arrow | JsonFunc::ArrowShift => (2, 2),
JsonFunc::Extract | JsonFunc::ExtractB | JsonFunc::Remove | JsonFunc::RemoveB => {
(2, usize::MAX)
}
JsonFunc::Insert
| JsonFunc::InsertB
| JsonFunc::Replace
| JsonFunc::ReplaceB
| JsonFunc::Set
| JsonFunc::SetB
| JsonFunc::ArrayInsert
| JsonFunc::ArrayInsertB => (3, usize::MAX),
}
}
pub fn arity_ok(self, count: usize) -> bool {
let (least, most) = self.arity();
if count < least || count > most {
return false;
}
match self {
JsonFunc::Insert
| JsonFunc::InsertB
| JsonFunc::Replace
| JsonFunc::ReplaceB
| JsonFunc::Set
| JsonFunc::SetB
| JsonFunc::ArrayInsert
| JsonFunc::ArrayInsertB => count % 2 == 1,
JsonFunc::Object | JsonFunc::ObjectB => count.is_multiple_of(2),
_ => true,
}
}
pub fn is_binary(self) -> bool {
matches!(
self,
JsonFunc::Jsonb
| JsonFunc::ArrayB
| JsonFunc::ExtractB
| JsonFunc::InsertB
| JsonFunc::ObjectB
| JsonFunc::PatchB
| JsonFunc::RemoveB
| JsonFunc::ReplaceB
| JsonFunc::SetB
)
}
pub fn first_argument_is_a_document(self) -> bool {
!matches!(
self,
JsonFunc::Array
| JsonFunc::ArrayB
| JsonFunc::Object
| JsonFunc::ObjectB
| JsonFunc::Quote
)
}
}
pub fn lookup_json(folded: &[u8]) -> Option<JsonFunc> {
let func = match folded {
b"json" => JsonFunc::Json,
b"jsonb" => JsonFunc::Jsonb,
b"json_array" => JsonFunc::Array,
b"jsonb_array" => JsonFunc::ArrayB,
b"json_array_length" => JsonFunc::ArrayLength,
b"json_error_position" => JsonFunc::ErrorPosition,
b"json_extract" => JsonFunc::Extract,
b"->" => JsonFunc::Arrow,
b"->>" => JsonFunc::ArrowShift,
b"jsonb_extract" => JsonFunc::ExtractB,
b"json_array_insert" => JsonFunc::ArrayInsert,
b"jsonb_array_insert" => JsonFunc::ArrayInsertB,
b"json_insert" => JsonFunc::Insert,
b"jsonb_insert" => JsonFunc::InsertB,
b"json_object" => JsonFunc::Object,
b"jsonb_object" => JsonFunc::ObjectB,
b"json_patch" => JsonFunc::Patch,
b"jsonb_patch" => JsonFunc::PatchB,
b"json_pretty" => JsonFunc::Pretty,
b"json_remove" => JsonFunc::Remove,
b"jsonb_remove" => JsonFunc::RemoveB,
b"json_replace" => JsonFunc::Replace,
b"jsonb_replace" => JsonFunc::ReplaceB,
b"json_set" => JsonFunc::Set,
b"jsonb_set" => JsonFunc::SetB,
b"json_type" => JsonFunc::Type,
b"json_valid" => JsonFunc::Valid,
b"json_quote" => JsonFunc::Quote,
_ => return None,
};
Some(func)
}
pub fn lookup_scalar(folded: &[u8]) -> Option<ScalarFunc> {
let func = match folded {
b"abs" => ScalarFunc::Abs,
b"char" => ScalarFunc::Char,
b"coalesce" => ScalarFunc::Coalesce,
b"concat" => ScalarFunc::Concat,
b"concat_ws" => ScalarFunc::ConcatWs,
b"glob" => ScalarFunc::Glob,
b"hex" => ScalarFunc::Hex,
b"ifnull" => ScalarFunc::IfNull,
b"iif" | b"if" => ScalarFunc::Iif,
b"instr" => ScalarFunc::Instr,
b"length" => ScalarFunc::Length,
b"like" => ScalarFunc::Like,
b"likelihood" | b"likely" | b"unlikely" => ScalarFunc::Likelihood,
b"lower" => ScalarFunc::Lower,
b"ltrim" => ScalarFunc::LTrim,
b"max" => ScalarFunc::Max,
b"min" => ScalarFunc::Min,
b"nullif" => ScalarFunc::NullIf,
b"quote" => ScalarFunc::Quote,
b"replace" => ScalarFunc::Replace,
b"round" => ScalarFunc::Round,
b"rtrim" => ScalarFunc::RTrim,
b"sign" => ScalarFunc::Sign,
b"substr" | b"substring" => ScalarFunc::Substr,
b"printf" | b"format" => ScalarFunc::Printf,
b"octet_length" => ScalarFunc::OctetLength,
b"random" => ScalarFunc::Random,
b"randomblob" => ScalarFunc::RandomBlob,
b"changes" => ScalarFunc::Changes,
b"total_changes" => ScalarFunc::TotalChanges,
b"last_insert_rowid" => ScalarFunc::LastInsertRowid,
b"sqlite_source_id" => ScalarFunc::SourceId,
b"fts5_source_id" => ScalarFunc::Fts5SourceId,
b"trim" => ScalarFunc::Trim,
b"typeof" => ScalarFunc::TypeOf,
b"unhex" => ScalarFunc::Unhex,
b"unicode" => ScalarFunc::Unicode,
b"upper" => ScalarFunc::Upper,
b"zeroblob" => ScalarFunc::ZeroBlob,
b"sqlite_version" => ScalarFunc::Version,
b"vector_distance_cos" | b"cosine_distance" => ScalarFunc::VectorDistanceCos,
b"vector_distance_l2" | b"l2_distance" => ScalarFunc::VectorDistanceL2,
b"vector_dot" | b"inner_product" => ScalarFunc::VectorDot,
b"l1_distance" | b"vector_distance_l1" => ScalarFunc::VectorDistanceL1,
b"hamming_distance" | b"vector_distance_hamming" => ScalarFunc::VectorDistanceHamming,
b"jaccard_distance" | b"vector_distance_jaccard" => ScalarFunc::VectorDistanceJaccard,
b"vector_dims" => ScalarFunc::VectorDims,
b"vector_norm" => ScalarFunc::VectorNorm,
b"l2_normalize" => ScalarFunc::VectorNormalize,
b"binary_quantize" => ScalarFunc::VectorQuantize,
b"subvector" => ScalarFunc::VectorSlice,
b"vector_add" => ScalarFunc::VectorAdd,
b"vector_sub" => ScalarFunc::VectorSubtract,
b"vector_mul" => ScalarFunc::VectorMultiply,
b"vector_concat" => ScalarFunc::VectorConcat,
b"geopoly_area" => ScalarFunc::GeopolyArea,
b"geopoly_blob" => ScalarFunc::GeopolyBlob,
b"geopoly_json" => ScalarFunc::GeopolyJson,
b"geopoly_svg" => ScalarFunc::GeopolySvg,
b"geopoly_within" => ScalarFunc::GeopolyWithin,
b"geopoly_contains_point" => ScalarFunc::GeopolyContainsPoint,
b"geopoly_overlap" => ScalarFunc::GeopolyOverlap,
b"geopoly_debug" => ScalarFunc::GeopolyDebug,
b"geopoly_bbox" => ScalarFunc::GeopolyBbox,
b"geopoly_xform" => ScalarFunc::GeopolyXform,
b"geopoly_regular" => ScalarFunc::GeopolyRegular,
b"geopoly_ccw" => ScalarFunc::GeopolyCcw,
b"unknown" => ScalarFunc::Unknown,
b"subtype" => ScalarFunc::Subtype,
b"unistr" => ScalarFunc::Unistr,
b"unistr_quote" => ScalarFunc::UnistrQuote,
b"sqlite_compileoption_used" => ScalarFunc::CompileOptionUsed,
b"sqlite_compileoption_get" => ScalarFunc::CompileOptionGet,
b"sqlite_log" => ScalarFunc::Log,
b"load_extension" => ScalarFunc::LoadExtension,
b"regexp" => ScalarFunc::Regexp,
b"sqlite_offset" => ScalarFunc::Offset,
b"sqlar_compress" => ScalarFunc::SqlarCompress,
b"sqlar_uncompress" => ScalarFunc::SqlarUncompress,
b"rtreedepth" => ScalarFunc::RTreeDepth,
b"rtreenode" => ScalarFunc::RTreeNode,
b"rtreecheck" => ScalarFunc::RTreeCheck,
_ => return None,
};
Some(func)
}
pub fn lookup_aggregate(folded: &[u8]) -> Option<AggregateFunc> {
let func = match folded {
b"count" => AggregateFunc::Count,
b"sum" => AggregateFunc::Sum,
b"total" => AggregateFunc::Total,
b"avg" => AggregateFunc::Avg,
b"group_concat" | b"string_agg" => AggregateFunc::GroupConcat,
b"json_group_array" => AggregateFunc::JsonGroupArray,
b"jsonb_group_array" => AggregateFunc::JsonbGroupArray,
b"json_group_object" => AggregateFunc::JsonGroupObject,
b"geopoly_group_bbox" => AggregateFunc::GeopolyGroupBbox,
b"median" => AggregateFunc::Median,
b"percentile" => AggregateFunc::Percentile,
b"percentile_cont" => AggregateFunc::PercentileCont,
b"percentile_disc" => AggregateFunc::PercentileDisc,
b"jsonb_group_object" => AggregateFunc::JsonbGroupObject,
_ => return None,
};
Some(func)
}
pub fn scalar_arity_ok(func: ScalarFunc, count: usize) -> bool {
match func {
ScalarFunc::Abs
| ScalarFunc::Hex
| ScalarFunc::Length
| ScalarFunc::Lower
| ScalarFunc::Quote
| ScalarFunc::Sign
| ScalarFunc::TypeOf
| ScalarFunc::Unicode
| ScalarFunc::Upper
| ScalarFunc::ZeroBlob => count == 1,
ScalarFunc::IfNull | ScalarFunc::NullIf | ScalarFunc::Glob => count == 2,
ScalarFunc::VectorDistanceCos
| ScalarFunc::VectorDistanceL2
| ScalarFunc::VectorDot
| ScalarFunc::VectorDistanceL1
| ScalarFunc::VectorDistanceHamming
| ScalarFunc::VectorDistanceJaccard
| ScalarFunc::VectorAdd
| ScalarFunc::VectorSubtract
| ScalarFunc::VectorMultiply
| ScalarFunc::VectorConcat => count == 2,
ScalarFunc::VectorDims
| ScalarFunc::VectorNorm
| ScalarFunc::VectorNormalize
| ScalarFunc::VectorQuantize => count == 1,
ScalarFunc::VectorSlice => count == 3,
ScalarFunc::RTreeDepth | ScalarFunc::Offset | ScalarFunc::SqlarCompress => count == 1,
ScalarFunc::SqlarUncompress => count == 2,
ScalarFunc::RTreeNode => count == 2,
ScalarFunc::RTreeCheck => count == 1 || count == 2,
ScalarFunc::GeopolyArea
| ScalarFunc::GeopolyBlob
| ScalarFunc::GeopolyJson
| ScalarFunc::GeopolyDebug
| ScalarFunc::GeopolyBbox
| ScalarFunc::GeopolyCcw => count == 1,
ScalarFunc::GeopolyWithin | ScalarFunc::GeopolyOverlap => count == 2,
ScalarFunc::GeopolyContainsPoint => count == 3,
ScalarFunc::GeopolyRegular => count == 4,
ScalarFunc::GeopolyXform => count == 7,
ScalarFunc::GeopolySvg => count >= 1,
ScalarFunc::Replace => count == 3,
ScalarFunc::Iif => count >= 2,
ScalarFunc::Unknown => true,
ScalarFunc::Subtype
| ScalarFunc::Unistr
| ScalarFunc::UnistrQuote
| ScalarFunc::CompileOptionUsed
| ScalarFunc::CompileOptionGet => count == 1,
ScalarFunc::Log | ScalarFunc::Regexp => count == 2,
ScalarFunc::LoadExtension => count == 1 || count == 2,
ScalarFunc::Instr => count == 2,
ScalarFunc::Like => count == 2 || count == 3,
ScalarFunc::Likelihood => count == 1 || count == 2,
ScalarFunc::LTrim | ScalarFunc::RTrim | ScalarFunc::Trim | ScalarFunc::Unhex => {
count == 1 || count == 2
}
ScalarFunc::Round => count == 1 || count == 2,
ScalarFunc::Substr => count == 2 || count == 3,
ScalarFunc::Coalesce | ScalarFunc::Max | ScalarFunc::Min => count >= 2,
ScalarFunc::Char => true,
ScalarFunc::Concat => count >= 1,
ScalarFunc::ConcatWs => count >= 2,
ScalarFunc::Version => count == 0,
ScalarFunc::Printf => count >= 1,
ScalarFunc::OctetLength | ScalarFunc::RandomBlob => count == 1,
ScalarFunc::Random
| ScalarFunc::Changes
| ScalarFunc::TotalChanges
| ScalarFunc::LastInsertRowid
| ScalarFunc::SourceId
| ScalarFunc::Fts5SourceId => count == 0,
}
}
pub fn aggregate_arity_ok(func: AggregateFunc, count: usize, star: bool) -> bool {
match func {
AggregateFunc::Count => star || count == 1,
AggregateFunc::Sum | AggregateFunc::Total | AggregateFunc::Avg => !star && count == 1,
AggregateFunc::Min | AggregateFunc::Max => !star && count == 1,
AggregateFunc::GroupConcat => !star && (count == 1 || count == 2),
AggregateFunc::JsonGroupArray | AggregateFunc::JsonbGroupArray => !star && count == 1,
AggregateFunc::JsonGroupObject | AggregateFunc::JsonbGroupObject => !star && count == 2,
AggregateFunc::Median
| AggregateFunc::GeopolyGroupBbox
| AggregateFunc::VectorSum
| AggregateFunc::VectorAvg => !star && count == 1,
AggregateFunc::Percentile
| AggregateFunc::PercentileCont
| AggregateFunc::PercentileDisc => !star && count == 2,
AggregateFunc::External => !star,
}
}
pub fn is_aggregate_call(folded: &[u8], count: usize, star: bool) -> bool {
if folded == b"min" || folded == b"max" {
return !star && count == 1;
}
lookup_aggregate(folded).is_some()
}
pub fn minmax_aggregate(folded: &[u8]) -> Option<AggregateFunc> {
match folded {
b"min" => Some(AggregateFunc::Min),
b"max" => Some(AggregateFunc::Max),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_matches_folded_names() {
assert_eq!(lookup_scalar(b"abs"), Some(ScalarFunc::Abs));
assert_eq!(lookup_scalar(b"substring"), Some(ScalarFunc::Substr));
assert_eq!(lookup_scalar(b"nope"), None);
assert_eq!(lookup_aggregate(b"count"), Some(AggregateFunc::Count));
assert_eq!(
lookup_aggregate(b"string_agg"),
Some(AggregateFunc::GroupConcat)
);
}
#[test]
fn min_and_max_are_aggregates_only_at_one_argument() {
assert!(is_aggregate_call(b"min", 1, false));
assert!(!is_aggregate_call(b"min", 2, false));
assert!(!is_aggregate_call(b"min", 0, true));
assert_eq!(minmax_aggregate(b"max"), Some(AggregateFunc::Max));
}
#[test]
fn arity_is_checked_per_function() {
assert!(scalar_arity_ok(ScalarFunc::Abs, 1));
assert!(!scalar_arity_ok(ScalarFunc::Abs, 2));
assert!(scalar_arity_ok(ScalarFunc::Substr, 2));
assert!(scalar_arity_ok(ScalarFunc::Substr, 3));
assert!(!scalar_arity_ok(ScalarFunc::Substr, 4));
assert!(scalar_arity_ok(ScalarFunc::Coalesce, 5));
assert!(!scalar_arity_ok(ScalarFunc::Coalesce, 1));
assert!(aggregate_arity_ok(AggregateFunc::Count, 0, true));
assert!(!aggregate_arity_ok(AggregateFunc::Sum, 0, true));
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FunctionEntry {
pub name: &'static str,
pub kind: &'static str,
pub arity: i64,
pub flags: i64,
}
pub const INNOCUOUS_FLAG: i64 = 2048;
pub const DETERMINISTIC_FLAG: i64 = 524288;
const BUILTIN_FLAGS: i64 = INNOCUOUS_FLAG | DETERMINISTIC_FLAG;
const VOLATILE_FLAGS: i64 = INNOCUOUS_FLAG;
pub fn every_function() -> Vec<FunctionEntry> {
let mut out = Vec::new();
let mut scalar = |name: &'static str, arity: i64| {
out.push(FunctionEntry {
name,
kind: "s",
arity,
flags: BUILTIN_FLAGS,
});
};
for (name, arity) in SCALARS {
scalar(name, *arity);
}
for (name, arity) in VOLATILE {
out.push(FunctionEntry {
name,
kind: "s",
arity: *arity,
flags: VOLATILE_FLAGS,
});
}
for (name, arity) in AGGREGATES {
out.push(FunctionEntry {
name,
kind: "a",
arity: *arity,
flags: BUILTIN_FLAGS,
});
}
for (name, arity) in WINDOWS {
out.push(FunctionEntry {
name,
kind: "w",
arity: *arity,
flags: BUILTIN_FLAGS,
});
}
out.sort_by(|left, right| left.name.cmp(right.name).then(left.arity.cmp(&right.arity)));
out
}
const SCALARS: &[(&str, i64)] = &[
("abs", 1),
("acos", 1),
("acosh", 1),
("asin", 1),
("asinh", 1),
("atan", 1),
("atan2", 2),
("atanh", 1),
("ceil", 1),
("ceiling", 1),
("char", -1),
("coalesce", -4),
("concat", -3),
("concat_ws", -4),
("cos", 1),
("cosh", 1),
("date", -1),
("datetime", -1),
("degrees", 1),
("exp", 1),
("floor", 1),
("format", -1),
("glob", 2),
("hex", 1),
("ifnull", 2),
("iif", -4),
("instr", 2),
("json", 1),
("json_array", -1),
("json_array_length", 1),
("json_array_length", 2),
("json_error_position", 1),
("json_extract", -1),
("json_insert", -1),
("json_object", -1),
("json_patch", 2),
("json_pretty", 1),
("json_pretty", 2),
("json_quote", 1),
("json_remove", -1),
("json_replace", -1),
("json_set", -1),
("json_type", 1),
("json_type", 2),
("json_valid", 1),
("json_valid", 2),
("jsonb", 1),
("jsonb_array", -1),
("jsonb_extract", -1),
("jsonb_insert", -1),
("jsonb_object", -1),
("jsonb_patch", 2),
("jsonb_remove", -1),
("jsonb_replace", -1),
("jsonb_set", -1),
("julianday", -1),
("length", 1),
("like", 2),
("like", 3),
("likelihood", 2),
("likely", 1),
("ln", 1),
("log", 1),
("log", 2),
("log10", 1),
("log2", 1),
("lower", 1),
("ltrim", 1),
("ltrim", 2),
("max", -3),
("min", -3),
("mod", 2),
("nullif", 2),
("octet_length", 1),
("pi", 0),
("pow", 2),
("power", 2),
("printf", -1),
("quote", 1),
("radians", 1),
("replace", 3),
("round", 1),
("round", 2),
("rtrim", 1),
("rtrim", 2),
("sign", 1),
("sin", 1),
("sinh", 1),
("fts5_source_id", 0),
("optimize", 1),
("sqlite_source_id", 0),
("sqlite_version", 0),
("sqrt", 1),
("strftime", -1),
("substr", 2),
("substr", 3),
("substring", 2),
("substring", 3),
("tan", 1),
("tanh", 1),
("time", -1),
("timediff", 2),
("trim", 1),
("trim", 2),
("trunc", 1),
("typeof", 1),
("unhex", 1),
("unhex", 2),
("unicode", 1),
("unixepoch", -1),
("unlikely", 1),
("upper", 1),
("binary_quantize", 1),
("rtreecheck", -1),
("sqlar_compress", 1),
("sqlar_uncompress", 2),
("sqlite_offset", 1),
("rtreedepth", 1),
("rtreenode", 2),
("geopoly_area", 1),
("geopoly_bbox", 1),
("geopoly_blob", 1),
("geopoly_ccw", 1),
("geopoly_contains_point", 3),
("geopoly_debug", 1),
("geopoly_group_bbox", 1),
("geopoly_json", 1),
("geopoly_overlap", 2),
("geopoly_regular", 4),
("geopoly_svg", -1),
("geopoly_within", 2),
("geopoly_xform", 7),
("cosine_distance", 2),
("hamming_distance", 2),
("inner_product", 2),
("jaccard_distance", 2),
("l1_distance", 2),
("l2_distance", 2),
("l2_normalize", 1),
("subvector", 3),
("vector_add", 2),
("vector_concat", 2),
("vector_dims", 1),
("vector_distance_cos", 2),
("vector_distance_l2", 2),
("vector_dot", 2),
("vector_mul", 2),
("vector_norm", 1),
("vector_sub", 2),
("zeroblob", 1),
("->", 2),
("->>", 2),
("bm25", -1),
("highlight", -1),
("if", -4),
("json_array_insert", -1),
("jsonb_array_insert", -1),
("match", 2),
("matchinfo", 1),
("matchinfo", 2),
("offsets", 1),
("regexp", 2),
("snippet", -1),
("sqlite_compileoption_get", 1),
("sqlite_compileoption_used", 1),
("subtype", 1),
("unistr", 1),
("unistr_quote", 1),
("unknown", -1),
];
const VOLATILE: &[(&str, i64)] = &[
("changes", 0),
("current_date", 0),
("current_time", 0),
("current_timestamp", 0),
("last_insert_rowid", 0),
("load_extension", 1),
("load_extension", 2),
("random", 0),
("randomblob", 1),
("sqlite_log", 2),
("total_changes", 0),
];
const AGGREGATES: &[(&str, i64)] = &[
("avg", 1),
("count", 0),
("count", 1),
("group_concat", 1),
("group_concat", 2),
("json_group_array", 1),
("json_group_object", 2),
("jsonb_group_array", 1),
("jsonb_group_object", 2),
("max", 1),
("min", 1),
("string_agg", 2),
("sum", 1),
("total", 1),
];
const WINDOWS: &[(&str, i64)] = &[
("cume_dist", 0),
("dense_rank", 0),
("first_value", 1),
("lag", 1),
("lag", 2),
("lag", 3),
("last_value", 1),
("lead", 1),
("lead", 2),
("lead", 3),
("nth_value", 2),
("ntile", 1),
("percent_rank", 0),
("rank", 0),
("row_number", 0),
("median", 1),
("percentile", 2),
("percentile_cont", 2),
("percentile_disc", 2),
];