Skip to main content

clt_database/
function.rs

1use crate::sync::Arc;
2use std::fmt;
3use std::fmt::{Debug, Display};
4use strum::IntoEnumIterator;
5use turso_ext::{
6    ContextDestructor, FinalizeFunction, InitAggFunction, ScalarFunction, StepFunction,
7    ValueDestructor,
8};
9
10use crate::LimboError;
11
12pub type ContextCollationFunction = unsafe extern "C" fn(
13    context: usize,
14    left_ptr: *const u8,
15    left_len: usize,
16    right_ptr: *const u8,
17    right_len: usize,
18) -> i32;
19
20pub trait Deterministic: std::fmt::Display {
21    fn is_deterministic(&self) -> bool;
22}
23
24pub struct ExternalFunc {
25    pub name: String,
26    pub func: ExtFunc,
27}
28
29pub struct ExternalCollation {
30    pub name: String,
31    pub context: usize,
32    pub callback: ContextCollationFunction,
33    pub context_destructor: Option<ContextDestructor>,
34}
35
36impl ExternalCollation {
37    pub fn new(
38        name: String,
39        context: usize,
40        callback: ContextCollationFunction,
41        context_destructor: Option<ContextDestructor>,
42    ) -> Self {
43        Self {
44            name,
45            context,
46            callback,
47            context_destructor,
48        }
49    }
50}
51
52impl Drop for ExternalCollation {
53    fn drop(&mut self) {
54        if let Some(destructor) = self.context_destructor {
55            unsafe { destructor(self.context) };
56        }
57    }
58}
59
60impl Debug for ExternalCollation {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.debug_struct("ExternalCollation")
63            .field("name", &self.name)
64            .finish()
65    }
66}
67
68impl Deterministic for ExternalFunc {
69    fn is_deterministic(&self) -> bool {
70        match self.func {
71            ExtFunc::Scalar { deterministic, .. } => deterministic,
72            _ => false,
73        }
74    }
75}
76
77#[derive(Debug, Clone)]
78pub enum ExtFunc {
79    Scalar {
80        context: usize,
81        argc: i32,
82        deterministic: bool,
83        callback: ScalarFunction,
84        context_destructor: Option<ContextDestructor>,
85        value_destructor: Option<ValueDestructor>,
86    },
87    Aggregate {
88        context: usize,
89        argc: i32,
90        init: InitAggFunction,
91        step: StepFunction,
92        finalize: FinalizeFunction,
93        context_destructor: Option<ContextDestructor>,
94        aggregate_destructor: Option<ContextDestructor>,
95        value_destructor: Option<ValueDestructor>,
96    },
97}
98
99impl ExtFunc {
100    pub fn agg_args(&self) -> Result<i32, ()> {
101        if let ExtFunc::Aggregate { argc, .. } = self {
102            return Ok(*argc);
103        }
104        Err(())
105    }
106
107    pub fn matches_arg_count(&self, arg_count: usize) -> bool {
108        match self {
109            Self::Scalar { argc, .. } => *argc < 0 || *argc as usize == arg_count,
110            Self::Aggregate { argc, .. } => *argc < 0 || *argc as usize == arg_count,
111        }
112    }
113
114    pub fn is_aggregate(&self) -> bool {
115        matches!(self, Self::Aggregate { .. })
116    }
117
118    pub fn with_aggregate_arg_count(&self, arg_count: usize) -> Self {
119        match self {
120            Self::Aggregate {
121                context,
122                init,
123                step,
124                finalize,
125                aggregate_destructor,
126                value_destructor,
127                ..
128            } => Self::Aggregate {
129                context: *context,
130                argc: arg_count as i32,
131                init: *init,
132                step: *step,
133                finalize: *finalize,
134                context_destructor: None,
135                aggregate_destructor: *aggregate_destructor,
136                value_destructor: *value_destructor,
137            },
138            _ => self.clone(),
139        }
140    }
141}
142
143impl ExternalFunc {
144    pub fn new_scalar(
145        name: String,
146        argc: i32,
147        deterministic: bool,
148        context: usize,
149        callback: ScalarFunction,
150        context_destructor: Option<ContextDestructor>,
151        value_destructor: Option<ValueDestructor>,
152    ) -> Self {
153        Self {
154            name,
155            func: ExtFunc::Scalar {
156                context,
157                argc,
158                deterministic,
159                callback,
160                context_destructor,
161                value_destructor,
162            },
163        }
164    }
165
166    pub fn new_aggregate(
167        name: String,
168        argc: i32,
169        context: usize,
170        func: (InitAggFunction, StepFunction, FinalizeFunction),
171        context_destructor: Option<ContextDestructor>,
172        aggregate_destructor: Option<ContextDestructor>,
173        value_destructor: Option<ValueDestructor>,
174    ) -> Self {
175        Self {
176            name,
177            func: ExtFunc::Aggregate {
178                context,
179                argc,
180                init: func.0,
181                step: func.1,
182                finalize: func.2,
183                context_destructor,
184                aggregate_destructor,
185                value_destructor,
186            },
187        }
188    }
189}
190
191impl Drop for ExternalFunc {
192    fn drop(&mut self) {
193        match self.func {
194            ExtFunc::Scalar {
195                context,
196                context_destructor: Some(context_destructor),
197                ..
198            }
199            | ExtFunc::Aggregate {
200                context,
201                context_destructor: Some(context_destructor),
202                ..
203            } => unsafe { context_destructor(context) },
204            _ => {}
205        }
206    }
207}
208
209impl Debug for ExternalFunc {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        write!(f, "{}", self.name)
212    }
213}
214
215impl Display for ExternalFunc {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(f, "{}", self.name)
218    }
219}
220
221#[cfg(clt_turso_feature = "json")]
222#[derive(Debug, Clone, PartialEq, strum::EnumIter)]
223pub enum JsonFunc {
224    Json,
225    Jsonb,
226    JsonArray,
227    JsonbArray,
228    JsonArrayLength,
229    JsonArrowExtract,
230    JsonArrowShiftExtract,
231    JsonExtract,
232    JsonbExtract,
233    JsonObject,
234    JsonbObject,
235    JsonType,
236    JsonErrorPosition,
237    JsonValid,
238    JsonPatch,
239    JsonbPatch,
240    JsonRemove,
241    JsonbRemove,
242    JsonReplace,
243    JsonbReplace,
244    JsonInsert,
245    JsonbInsert,
246    JsonPretty,
247    JsonSet,
248    JsonbSet,
249    JsonQuote,
250}
251
252#[cfg(clt_turso_feature = "json")]
253impl Deterministic for JsonFunc {
254    fn is_deterministic(&self) -> bool {
255        true
256    }
257}
258
259#[cfg(clt_turso_feature = "json")]
260impl Display for JsonFunc {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        write!(
263            f,
264            "{}",
265            match self {
266                Self::Json => "json",
267                Self::Jsonb => "jsonb",
268                Self::JsonArray => "json_array",
269                Self::JsonbArray => "jsonb_array",
270                Self::JsonExtract => "json_extract",
271                Self::JsonbExtract => "jsonb_extract",
272                Self::JsonArrayLength => "json_array_length",
273                Self::JsonArrowExtract => "->",
274                Self::JsonArrowShiftExtract => "->>",
275                Self::JsonObject => "json_object",
276                Self::JsonbObject => "jsonb_object",
277                Self::JsonType => "json_type",
278                Self::JsonErrorPosition => "json_error_position",
279                Self::JsonValid => "json_valid",
280                Self::JsonPatch => "json_patch",
281                Self::JsonbPatch => "jsonb_patch",
282                Self::JsonRemove => "json_remove",
283                Self::JsonbRemove => "jsonb_remove",
284                Self::JsonReplace => "json_replace",
285                Self::JsonbReplace => "jsonb_replace",
286                Self::JsonInsert => "json_insert",
287                Self::JsonbInsert => "jsonb_insert",
288                Self::JsonPretty => "json_pretty",
289                Self::JsonSet => "json_set",
290                Self::JsonbSet => "jsonb_set",
291                Self::JsonQuote => "json_quote",
292            }
293        )
294    }
295}
296
297#[cfg(clt_turso_feature = "json")]
298impl JsonFunc {
299    /// Returns true for operator-style entries that should not appear in PRAGMA function_list.
300    pub fn is_internal(&self) -> bool {
301        matches!(self, Self::JsonArrowExtract | Self::JsonArrowShiftExtract)
302    }
303
304    pub fn arities(&self) -> &'static [i32] {
305        match self {
306            Self::Json
307            | Self::Jsonb
308            | Self::JsonQuote
309            | Self::JsonErrorPosition
310            | Self::JsonValid => &[1],
311            Self::JsonPatch | Self::JsonbPatch => &[2],
312            Self::JsonArrayLength | Self::JsonType => &[1, 2],
313            // Operators — filtered out, arity doesn't matter
314            Self::JsonArrowExtract | Self::JsonArrowShiftExtract => &[2],
315            // Variable-arg
316            _ => &[-1],
317        }
318    }
319}
320
321#[derive(Debug, Clone, strum::EnumIter)]
322pub enum VectorFunc {
323    Vector,
324    Vector32,
325    Vector32Sparse,
326    Vector64,
327    Vector8,
328    Vector1Bit,
329    VectorExtract,
330    VectorDistanceCos,
331    VectorDistanceL2,
332    VectorDistanceJaccard,
333    VectorDistanceDot,
334    VectorConcat,
335    VectorSlice,
336}
337
338impl Deterministic for VectorFunc {
339    fn is_deterministic(&self) -> bool {
340        true
341    }
342}
343
344impl Display for VectorFunc {
345    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        let str = match self {
347            Self::Vector => "vector",
348            Self::Vector32 => "vector32",
349            Self::Vector32Sparse => "vector32_sparse",
350            Self::Vector64 => "vector64",
351            Self::Vector8 => "vector8",
352            Self::Vector1Bit => "vector1bit",
353            Self::VectorExtract => "vector_extract",
354            Self::VectorDistanceCos => "vector_distance_cos",
355            Self::VectorDistanceL2 => "vector_distance_l2",
356            Self::VectorDistanceJaccard => "vector_distance_jaccard",
357            Self::VectorDistanceDot => "vector_distance_dot",
358            Self::VectorConcat => "vector_concat",
359            Self::VectorSlice => "vector_slice",
360        };
361        write!(f, "{str}")
362    }
363}
364
365impl VectorFunc {
366    pub fn arities(&self) -> &'static [i32] {
367        match self {
368            Self::Vector
369            | Self::Vector32
370            | Self::Vector32Sparse
371            | Self::Vector64
372            | Self::Vector8
373            | Self::Vector1Bit
374            | Self::VectorExtract => &[1],
375            Self::VectorDistanceCos
376            | Self::VectorDistanceL2
377            | Self::VectorDistanceJaccard
378            | Self::VectorDistanceDot => &[2],
379            Self::VectorSlice => &[3],
380            Self::VectorConcat => &[-1],
381        }
382    }
383}
384
385/// Full-text search functions
386#[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
387#[derive(Debug, Clone, PartialEq, strum::EnumIter)]
388pub enum FtsFunc {
389    /// fts_score(col1, col2, ..., query): computes FTS relevance score
390    /// When used with an FTS index, the optimizer routes through the index method
391    Score,
392    /// fts_match(col1, col2, ..., query): returns true if document matches query
393    /// Used in WHERE clause for filtering rows by FTS match
394    Match,
395    /// fts_highlight(text, query, before_tag, after_tag): returns text with matching terms highlighted
396    /// Wraps matching query terms in the text with before_tag and after_tag markers
397    Highlight,
398}
399
400#[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
401impl FtsFunc {
402    pub fn is_deterministic(&self) -> bool {
403        true
404    }
405
406    pub fn arities(&self) -> &'static [i32] {
407        match self {
408            Self::Highlight => &[4],
409            // Score and Match take variable columns + query
410            Self::Score | Self::Match => &[-1],
411        }
412    }
413}
414
415#[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
416impl Display for FtsFunc {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        let str = match self {
419            Self::Score => "fts_score",
420            Self::Match => "fts_match",
421            Self::Highlight => "fts_highlight",
422        };
423        write!(f, "{str}")
424    }
425}
426
427#[derive(Debug, Clone, strum::EnumIter)]
428pub enum AggFunc {
429    Avg,
430    Count,
431    Count0,
432    GroupConcat,
433    Max,
434    Min,
435    StringAgg,
436    Sum,
437    Total,
438    #[cfg(clt_turso_feature = "json")]
439    JsonbGroupArray,
440    #[cfg(clt_turso_feature = "json")]
441    JsonGroupArray,
442    #[cfg(clt_turso_feature = "json")]
443    JsonbGroupObject,
444    #[cfg(clt_turso_feature = "json")]
445    JsonGroupObject,
446    ArrayAgg,
447    /// `mode() WITHIN GROUP (ORDER BY x)` — most frequent value of `x`.
448    /// Stored args (post-planning): `[value]`.
449    #[strum(disabled)]
450    Mode,
451    /// `percentile_cont(fraction) WITHIN GROUP (ORDER BY x)` — interpolated percentile.
452    /// Stored args (post-planning): `[value, fraction]`.
453    #[strum(disabled)]
454    PercentileCont,
455    /// `percentile_disc(fraction) WITHIN GROUP (ORDER BY x)` — discrete percentile.
456    /// Stored args (post-planning): `[value, fraction]`.
457    #[strum(disabled)]
458    PercentileDisc,
459    #[strum(disabled)]
460    External(Arc<ExtFunc>),
461}
462
463#[derive(Debug, Clone, strum::EnumIter)]
464pub enum WindowFunc {
465    RowNumber,
466    Rank,
467    DenseRank,
468    PercentRank,
469    CumeDist,
470    Ntile,
471    Lag,
472    Lead,
473    FirstValue,
474    LastValue,
475    NthValue,
476    #[strum(disabled)]
477    External(Arc<ExtFunc>),
478}
479
480impl WindowFunc {
481    /// SQL name of this window function. Matches the strings used by
482    /// `Display` so EXPLAIN output and error messages agree.
483    pub fn as_str(&self) -> &'static str {
484        match self {
485            Self::RowNumber => "row_number",
486            Self::Rank => "rank",
487            Self::DenseRank => "dense_rank",
488            Self::PercentRank => "percent_rank",
489            Self::CumeDist => "cume_dist",
490            Self::Ntile => "ntile",
491            Self::Lag => "lag",
492            Self::Lead => "lead",
493            Self::FirstValue => "first_value",
494            Self::LastValue => "last_value",
495            Self::NthValue => "nth_value",
496            Self::External(_) => unreachable!(
497                "WindowFunc::External is not constructible: ExtFunc has no Window variant"
498            ),
499        }
500    }
501
502    pub fn arities(&self) -> &'static [i32] {
503        match self {
504            Self::RowNumber | Self::Rank | Self::DenseRank | Self::PercentRank | Self::CumeDist => {
505                &[0]
506            }
507            Self::Ntile | Self::FirstValue | Self::LastValue => &[1],
508            Self::NthValue => &[2],
509            Self::Lag | Self::Lead => &[1, 2, 3],
510            Self::External(_) => unreachable!(
511                "WindowFunc::External is not constructible: ExtFunc has no Window variant"
512            ),
513        }
514    }
515
516    /// Whether name resolution + runtime dispatch are wired up. Stub variants
517    /// must not be advertised via `pragma_function_list`, or introspection
518    /// drifts ahead of the resolver and users get "no such function" when
519    /// they try to call them.
520    pub fn is_implemented(&self) -> bool {
521        matches!(self, Self::RowNumber)
522    }
523}
524
525impl PartialEq for WindowFunc {
526    fn eq(&self, other: &Self) -> bool {
527        match (self, other) {
528            (Self::RowNumber, Self::RowNumber)
529            | (Self::Rank, Self::Rank)
530            | (Self::DenseRank, Self::DenseRank)
531            | (Self::PercentRank, Self::PercentRank)
532            | (Self::CumeDist, Self::CumeDist)
533            | (Self::Ntile, Self::Ntile)
534            | (Self::Lag, Self::Lag)
535            | (Self::Lead, Self::Lead)
536            | (Self::FirstValue, Self::FirstValue)
537            | (Self::LastValue, Self::LastValue)
538            | (Self::NthValue, Self::NthValue) => true,
539            (Self::External(a), Self::External(b)) => Arc::ptr_eq(a, b),
540            _ => false,
541        }
542    }
543}
544
545impl Eq for WindowFunc {}
546
547impl Deterministic for WindowFunc {
548    fn is_deterministic(&self) -> bool {
549        match self {
550            Self::RowNumber
551            | Self::Rank
552            | Self::DenseRank
553            | Self::PercentRank
554            | Self::CumeDist
555            | Self::Ntile
556            | Self::Lag
557            | Self::Lead
558            | Self::FirstValue
559            | Self::LastValue
560            | Self::NthValue => true,
561            Self::External(_) => unreachable!(
562                "WindowFunc::External is not constructible: ExtFunc has no Window variant"
563            ),
564        }
565    }
566}
567
568impl std::fmt::Display for WindowFunc {
569    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
570        f.write_str(self.as_str())
571    }
572}
573
574/// Function reference used by AggStep / AggValue / AggFinal opcodes.
575/// Aggregates used in window context and pure window functions share the same
576/// step/value dispatch path; this enum carries which side of that split a
577/// particular call belongs to.
578#[derive(Debug, Clone)]
579pub enum AccumulatorFunc {
580    Agg(AggFunc),
581    Window(WindowFunc),
582}
583
584impl AccumulatorFunc {
585    /// Extract the inner `AggFunc` when this kind is known to be an
586    /// aggregate. `unreachable!`s on `Window(...)` — the only opcodes
587    /// that carry an `AccumulatorFunc` are the AggStep / AggValue /
588    /// AggFinal trio, and the call sites that emit those wrap aggregates
589    /// only. A `Window` value reaching here is a planner bug.
590    pub fn expect_agg(&self) -> &AggFunc {
591        match self {
592            Self::Agg(f) => f,
593            Self::Window(f) => {
594                unreachable!("window function {f} reached an aggregate-only dispatch path")
595            }
596        }
597    }
598
599    pub fn as_str(&self) -> &'static str {
600        match self {
601            Self::Agg(f) => f.as_str(),
602            Self::Window(f) => f.as_str(),
603        }
604    }
605}
606
607impl PartialEq for AggFunc {
608    fn eq(&self, other: &Self) -> bool {
609        match (self, other) {
610            (Self::Avg, Self::Avg)
611            | (Self::Count, Self::Count)
612            | (Self::GroupConcat, Self::GroupConcat)
613            | (Self::Max, Self::Max)
614            | (Self::Min, Self::Min)
615            | (Self::StringAgg, Self::StringAgg)
616            | (Self::Sum, Self::Sum)
617            | (Self::Total, Self::Total)
618            | (Self::ArrayAgg, Self::ArrayAgg)
619            | (Self::Mode, Self::Mode)
620            | (Self::PercentileCont, Self::PercentileCont)
621            | (Self::PercentileDisc, Self::PercentileDisc) => true,
622            (Self::External(a), Self::External(b)) => Arc::ptr_eq(a, b),
623            _ => false,
624        }
625    }
626}
627
628impl Deterministic for AggFunc {
629    fn is_deterministic(&self) -> bool {
630        false // consider aggregate functions nondeterministic since they depend on the number of rows, not only the input arguments
631    }
632}
633impl std::fmt::Display for AggFunc {
634    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635        write!(f, "{}", self.as_str())
636    }
637}
638
639impl AggFunc {
640    pub fn num_args(&self) -> usize {
641        match self {
642            Self::Avg => 1,
643            Self::Count0 => 0,
644            Self::Count => 1,
645            Self::GroupConcat => 1,
646            Self::Max => 1,
647            Self::Min => 1,
648            Self::StringAgg => 2,
649            Self::Sum => 1,
650            Self::Total => 1,
651            Self::ArrayAgg => 1,
652            // Ordered-set aggregates: args are rewritten by the planner to
653            // `[value]` (mode) or `[value, fraction]` (percentiles).
654            Self::Mode => 1,
655            Self::PercentileCont | Self::PercentileDisc => 2,
656            #[cfg(clt_turso_feature = "json")]
657            Self::JsonGroupArray | Self::JsonbGroupArray => 1,
658            #[cfg(clt_turso_feature = "json")]
659            Self::JsonGroupObject | Self::JsonbGroupObject => 2,
660            Self::External(func) => func
661                .agg_args()
662                .map(|argc| argc.max(0) as usize)
663                .unwrap_or(0),
664        }
665    }
666
667    /// Returns all valid arities for this aggregate function.
668    /// Most aggregates have a single arity, but group_concat accepts 1 or 2 args.
669    pub fn arities(&self) -> &'static [i32] {
670        match self {
671            Self::Avg => &[1],
672            Self::Count0 => &[0],
673            Self::Count => &[1],
674            Self::GroupConcat => &[1, 2],
675            Self::Max => &[1],
676            Self::Min => &[1],
677            Self::StringAgg => &[2],
678            Self::Sum => &[1],
679            Self::Total => &[1],
680            Self::ArrayAgg => &[1],
681            Self::Mode => &[1],
682            Self::PercentileCont | Self::PercentileDisc => &[2],
683            #[cfg(clt_turso_feature = "json")]
684            Self::JsonGroupArray | Self::JsonbGroupArray => &[1],
685            #[cfg(clt_turso_feature = "json")]
686            Self::JsonGroupObject | Self::JsonbGroupObject => &[2],
687            Self::External(_) => &[-1],
688        }
689    }
690
691    pub fn as_str(&self) -> &'static str {
692        match self {
693            Self::Avg => "avg",
694            Self::Count0 => "count",
695            Self::Count => "count",
696            Self::GroupConcat => "group_concat",
697            Self::Max => "max",
698            Self::Min => "min",
699            Self::StringAgg => "string_agg",
700            Self::Sum => "sum",
701            Self::Total => "total",
702            Self::ArrayAgg => "array_agg",
703            Self::Mode => "mode",
704            Self::PercentileCont => "percentile_cont",
705            Self::PercentileDisc => "percentile_disc",
706            #[cfg(clt_turso_feature = "json")]
707            Self::JsonbGroupArray => "jsonb_group_array",
708            #[cfg(clt_turso_feature = "json")]
709            Self::JsonGroupArray => "json_group_array",
710            #[cfg(clt_turso_feature = "json")]
711            Self::JsonbGroupObject => "jsonb_group_object",
712            #[cfg(clt_turso_feature = "json")]
713            Self::JsonGroupObject => "json_group_object",
714            Self::External(_) => "extension function",
715        }
716    }
717}
718
719#[derive(Debug, Clone, PartialEq, strum::EnumIter)]
720pub enum ScalarFunc {
721    Cast,
722    Changes,
723    Char,
724    Coalesce,
725    Concat,
726    ConcatWs,
727    Glob,
728    IfNull,
729    Iif,
730    Instr,
731    Like,
732    Abs,
733    Upper,
734    Lower,
735    Random,
736    RandomBlob,
737    Trim,
738    LTrim,
739    RTrim,
740    Round,
741    Length,
742    OctetLength,
743    Min,
744    Max,
745    Nullif,
746    Sign,
747    Substr,
748    Substring,
749    Soundex,
750    Date,
751    Time,
752    TotalChanges,
753    DateTime,
754    Typeof,
755    Unicode,
756    Unistr,
757    UnistrQuote,
758    Quote,
759    SqliteVersion,
760    TursoVersion,
761    SqliteSourceId,
762    UnixEpoch,
763    JulianDay,
764    Hex,
765    Unhex,
766    ZeroBlob,
767    LastInsertRowid,
768    Replace,
769    #[cfg(clt_turso_feature = "fs")]
770    #[cfg(not(target_family = "wasm"))]
771    LoadExtension,
772    StrfTime,
773    Printf,
774    Likely,
775    TimeDiff,
776    Likelihood,
777    TableColumnsJsonArray,
778    BinRecordJsonObject,
779    Attach,
780    Detach,
781    Unlikely,
782    StatInit,
783    StatPush,
784    StatGet,
785    ConnTxnId,
786    IsAutocommit,
787    SequenceWatermark,
788    // Test type functions (for custom type system testing)
789    TestUintEncode,
790    TestUintDecode,
791    TestUintAdd,
792    TestUintSub,
793    TestUintMul,
794    TestUintDiv,
795    TestUintLt,
796    TestUintEq,
797    /// Test-only: returns a monotonically increasing 64-bit integer on every
798    /// evaluation. Used to verify that the planner does not deduplicate
799    /// equivalent SQL calls that contain nondeterministic functions.
800    #[cfg(clt_turso_feature = "test_helper")]
801    TestNondetCounter,
802    StringReverse,
803    // SQL-standard string and math extensions (PG/MySQL/Oracle compatible)
804    Gcd,
805    Lcm,
806    Repeat,
807    Lpad,
808    Rpad,
809    // Built-in type support functions
810    BooleanToInt,
811    IntToBoolean,
812    ValidateIpAddr,
813    // Numeric type functions
814    NumericEncode,
815    NumericDecode,
816    NumericAdd,
817    NumericSub,
818    NumericMul,
819    NumericDiv,
820    NumericLt,
821    NumericEq,
822    // Array construction / element access (desugared from ARRAY[…] and expr[n] syntax)
823    Array,
824    ArrayElement,
825    ArraySetElement,
826    // Array utility functions
827    ArrayLength,
828    ArrayAppend,
829    ArrayPrepend,
830    ArrayCat,
831    ArrayRemove,
832    ArrayContains,
833    ArrayPosition,
834    ArraySlice,
835    StringToArray,
836    ArrayToString,
837    ArrayOverlap,
838    ArrayContainsAll,
839    // Struct/Union construction and access
840    StructPack,
841    StructExtractFunc,
842    UnionValueFunc,
843    UnionTagFunc,
844    UnionExtractFunc,
845    // Sequence functions
846    NextVal,
847    CurrVal,
848    SetVal,
849}
850
851impl Deterministic for ScalarFunc {
852    fn is_deterministic(&self) -> bool {
853        match self {
854            ScalarFunc::Cast => true,
855            ScalarFunc::Changes => false, // depends on DB state
856            ScalarFunc::Char => true,
857            ScalarFunc::Coalesce => true,
858            ScalarFunc::Concat => true,
859            ScalarFunc::ConcatWs => true,
860            ScalarFunc::Glob => true,
861            ScalarFunc::IfNull => true,
862            ScalarFunc::Iif => true,
863            ScalarFunc::Instr => true,
864            ScalarFunc::Like => true,
865            ScalarFunc::Abs => true,
866            ScalarFunc::Upper => true,
867            ScalarFunc::Lower => true,
868            ScalarFunc::Random => false,     // duh
869            ScalarFunc::RandomBlob => false, // duh
870            ScalarFunc::Trim => true,
871            ScalarFunc::LTrim => true,
872            ScalarFunc::RTrim => true,
873            ScalarFunc::Round => true,
874            ScalarFunc::Length => true,
875            ScalarFunc::OctetLength => true,
876            ScalarFunc::Min => true,
877            ScalarFunc::Max => true,
878            ScalarFunc::Nullif => true,
879            ScalarFunc::Sign => true,
880            ScalarFunc::Substr => true,
881            ScalarFunc::Substring => true,
882            ScalarFunc::Soundex => true,
883            ScalarFunc::Date => false,
884            ScalarFunc::Time => false,
885            ScalarFunc::TotalChanges => false,
886            ScalarFunc::DateTime => false,
887            ScalarFunc::Typeof => true,
888            ScalarFunc::Unicode => true,
889            ScalarFunc::Unistr => true,
890            ScalarFunc::UnistrQuote => true,
891            ScalarFunc::Quote => true,
892            ScalarFunc::SqliteVersion => false,
893            ScalarFunc::TursoVersion => false,
894            ScalarFunc::SqliteSourceId => false,
895            ScalarFunc::UnixEpoch => false,
896            ScalarFunc::JulianDay => false,
897            ScalarFunc::Hex => true,
898            ScalarFunc::Unhex => true,
899            ScalarFunc::ZeroBlob => true,
900            ScalarFunc::LastInsertRowid => false,
901            ScalarFunc::Replace => true,
902            #[cfg(clt_turso_feature = "fs")]
903            #[cfg(not(target_family = "wasm"))]
904            ScalarFunc::LoadExtension => false,
905            ScalarFunc::StrfTime => false,
906            ScalarFunc::Printf => true,
907            ScalarFunc::Likely => true,
908            ScalarFunc::TimeDiff => false,
909            ScalarFunc::Likelihood => true,
910            ScalarFunc::TableColumnsJsonArray => true, // while columns of the table can change with DDL statements, within single query plan it's static
911            ScalarFunc::BinRecordJsonObject => true,
912            ScalarFunc::Attach => false, // changes database state
913            ScalarFunc::Detach => false, // changes database state
914            ScalarFunc::Unlikely => true,
915            ScalarFunc::StatInit => false, // internal ANALYZE function
916            ScalarFunc::StatPush => false, // internal ANALYZE function
917            ScalarFunc::StatGet => false,  // internal ANALYZE function
918            ScalarFunc::ConnTxnId => false, // depends on connection state
919            ScalarFunc::IsAutocommit => false, // depends on connection state
920            ScalarFunc::SequenceWatermark => false, // depends on active MVCC transactions
921            ScalarFunc::TestUintEncode
922            | ScalarFunc::TestUintDecode
923            | ScalarFunc::TestUintAdd
924            | ScalarFunc::TestUintSub
925            | ScalarFunc::TestUintMul
926            | ScalarFunc::TestUintDiv
927            | ScalarFunc::TestUintLt
928            | ScalarFunc::TestUintEq
929            | ScalarFunc::StringReverse => true,
930            ScalarFunc::Gcd
931            | ScalarFunc::Lcm
932            | ScalarFunc::Repeat
933            | ScalarFunc::Lpad
934            | ScalarFunc::Rpad => true,
935            #[cfg(clt_turso_feature = "test_helper")]
936            ScalarFunc::TestNondetCounter => false,
937            ScalarFunc::BooleanToInt
938            | ScalarFunc::IntToBoolean
939            | ScalarFunc::ValidateIpAddr
940            | ScalarFunc::NumericEncode
941            | ScalarFunc::NumericDecode
942            | ScalarFunc::NumericAdd
943            | ScalarFunc::NumericSub
944            | ScalarFunc::NumericMul
945            | ScalarFunc::NumericDiv
946            | ScalarFunc::NumericLt
947            | ScalarFunc::NumericEq => true,
948            ScalarFunc::Array
949            | ScalarFunc::ArrayElement
950            | ScalarFunc::ArraySetElement
951            | ScalarFunc::ArrayLength
952            | ScalarFunc::ArrayAppend
953            | ScalarFunc::ArrayPrepend
954            | ScalarFunc::ArrayCat
955            | ScalarFunc::ArrayRemove
956            | ScalarFunc::ArrayContains
957            | ScalarFunc::ArrayPosition
958            | ScalarFunc::ArraySlice
959            | ScalarFunc::StringToArray
960            | ScalarFunc::ArrayToString
961            | ScalarFunc::ArrayOverlap
962            | ScalarFunc::ArrayContainsAll => true,
963            ScalarFunc::StructPack
964            | ScalarFunc::StructExtractFunc
965            | ScalarFunc::UnionValueFunc
966            | ScalarFunc::UnionTagFunc
967            | ScalarFunc::UnionExtractFunc => true,
968            ScalarFunc::NextVal | ScalarFunc::CurrVal | ScalarFunc::SetVal => false,
969        }
970    }
971}
972
973impl ScalarFunc {
974    /// Returns true if this function returns a record-format array blob
975    /// that needs ArrayDecode for display.
976    ///
977    /// FIXME: ideally every function would declare its return type via a
978    /// `return_type()` method, and this whitelist would be replaced by a
979    /// generic check. Postponed for now — the set of array-returning
980    /// functions is small and controlled by us.
981    pub fn returns_array_blob(&self) -> bool {
982        matches!(
983            self,
984            Self::Array
985                | Self::ArraySetElement
986                | Self::ArrayAppend
987                | Self::ArrayPrepend
988                | Self::ArrayCat
989                | Self::ArrayRemove
990                | Self::ArraySlice
991                | Self::StringToArray
992        )
993    }
994}
995
996impl Display for ScalarFunc {
997    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
998        let str = match self {
999            Self::Cast => "cast",
1000            Self::Changes => "changes",
1001            Self::Char => "char",
1002            Self::Coalesce => "coalesce",
1003            Self::Concat => "concat",
1004            Self::ConcatWs => "concat_ws",
1005            Self::Glob => "glob",
1006            Self::IfNull => "ifnull",
1007            Self::Iif => "iif",
1008            Self::Instr => "instr",
1009            Self::Like => "like",
1010            Self::Abs => "abs",
1011            Self::Upper => "upper",
1012            Self::Lower => "lower",
1013            Self::Random => "random",
1014            Self::RandomBlob => "randomblob",
1015            Self::Trim => "trim",
1016            Self::LTrim => "ltrim",
1017            Self::RTrim => "rtrim",
1018            Self::Round => "round",
1019            Self::Length => "length",
1020            Self::OctetLength => "octet_length",
1021            Self::Min => "min",
1022            Self::Max => "max",
1023            Self::Nullif => "nullif",
1024            Self::Sign => "sign",
1025            Self::Substr => "substr",
1026            Self::Substring => "substring",
1027            Self::Soundex => "soundex",
1028            Self::Date => "date",
1029            Self::Time => "time",
1030            Self::TotalChanges => "total_changes",
1031            Self::Typeof => "typeof",
1032            Self::Unicode => "unicode",
1033            Self::Unistr => "unistr",
1034            Self::UnistrQuote => "unistr_quote",
1035            Self::Quote => "quote",
1036            Self::SqliteVersion => "sqlite_version",
1037            Self::TursoVersion => "turso_version",
1038            Self::SqliteSourceId => "sqlite_source_id",
1039            Self::JulianDay => "julianday",
1040            Self::UnixEpoch => "unixepoch",
1041            Self::Hex => "hex",
1042            Self::Unhex => "unhex",
1043            Self::ZeroBlob => "zeroblob",
1044            Self::LastInsertRowid => "last_insert_rowid",
1045            Self::Replace => "replace",
1046            Self::DateTime => "datetime",
1047            #[cfg(clt_turso_feature = "fs")]
1048            #[cfg(not(target_family = "wasm"))]
1049            Self::LoadExtension => "load_extension",
1050            Self::StrfTime => "strftime",
1051            Self::Printf => "printf",
1052            Self::Likely => "likely",
1053            Self::TimeDiff => "timediff",
1054            Self::Likelihood => "likelihood",
1055            Self::TableColumnsJsonArray => "table_columns_json_array",
1056            Self::BinRecordJsonObject => "bin_record_json_object",
1057            Self::Attach => "attach",
1058            Self::Detach => "detach",
1059            Self::Unlikely => "unlikely",
1060            Self::StatInit => "stat_init",
1061            Self::StatPush => "stat_push",
1062            Self::StatGet => "stat_get",
1063            Self::ConnTxnId => "conn_txn_id",
1064            Self::IsAutocommit => "is_autocommit",
1065            Self::SequenceWatermark => "sequence_watermark_experimental",
1066            Self::TestUintEncode => "test_uint_encode",
1067            Self::TestUintDecode => "test_uint_decode",
1068            Self::TestUintAdd => "test_uint_add",
1069            Self::TestUintSub => "test_uint_sub",
1070            Self::TestUintMul => "test_uint_mul",
1071            Self::TestUintDiv => "test_uint_div",
1072            Self::TestUintLt => "test_uint_lt",
1073            Self::TestUintEq => "test_uint_eq",
1074            #[cfg(clt_turso_feature = "test_helper")]
1075            Self::TestNondetCounter => "test_nondet_counter",
1076            Self::StringReverse => "string_reverse",
1077            Self::Gcd => "gcd",
1078            Self::Lcm => "lcm",
1079            Self::Repeat => "repeat",
1080            Self::Lpad => "lpad",
1081            Self::Rpad => "rpad",
1082            Self::BooleanToInt => "boolean_to_int",
1083            Self::IntToBoolean => "int_to_boolean",
1084            Self::ValidateIpAddr => "validate_ipaddr",
1085            Self::NumericEncode => "numeric_encode",
1086            Self::NumericDecode => "numeric_decode",
1087            Self::NumericAdd => "numeric_add",
1088            Self::NumericSub => "numeric_sub",
1089            Self::NumericMul => "numeric_mul",
1090            Self::NumericDiv => "numeric_div",
1091            Self::NumericLt => "numeric_lt",
1092            Self::NumericEq => "numeric_eq",
1093            Self::Array => "array",
1094            Self::ArrayElement => "array_element",
1095            Self::ArraySetElement => "array_set_element",
1096            Self::ArrayLength => "array_length",
1097            Self::ArrayAppend => "array_append",
1098            Self::ArrayPrepend => "array_prepend",
1099            Self::ArrayCat => "array_cat",
1100            Self::ArrayRemove => "array_remove",
1101            Self::ArrayContains => "array_contains",
1102            Self::ArrayPosition => "array_position",
1103            Self::ArraySlice => "array_slice",
1104            Self::StringToArray => "string_to_array",
1105            Self::ArrayToString => "array_to_string",
1106            Self::ArrayOverlap => "array_overlap",
1107            Self::ArrayContainsAll => "array_contains_all",
1108            Self::StructPack => "struct_pack",
1109            Self::StructExtractFunc => "struct_extract",
1110            Self::UnionValueFunc => "union_value",
1111            Self::UnionTagFunc => "union_tag",
1112            Self::UnionExtractFunc => "union_extract",
1113            Self::NextVal => "nextval",
1114            Self::CurrVal => "currval",
1115            Self::SetVal => "setval",
1116        };
1117        write!(f, "{str}")
1118    }
1119}
1120
1121impl ScalarFunc {
1122    /// Returns true for internal functions that should not appear in PRAGMA function_list.
1123    pub fn is_internal(&self) -> bool {
1124        matches!(
1125            self,
1126            Self::Cast
1127                | Self::Array
1128                | Self::ArrayElement
1129                | Self::ArraySetElement
1130                | Self::StatInit
1131                | Self::StatPush
1132                | Self::StatGet
1133                | Self::Attach
1134                | Self::Detach
1135                | Self::TableColumnsJsonArray
1136                | Self::BinRecordJsonObject
1137                | Self::ConnTxnId
1138                | Self::IsAutocommit
1139        )
1140    }
1141
1142    /// Returns the valid arities for this function.
1143    /// Each value becomes a separate row in PRAGMA function_list.
1144    /// -1 means truly variable arguments (e.g. coalesce, printf).
1145    pub fn arities(&self) -> &'static [i32] {
1146        match self {
1147            // 0-arg
1148            Self::Changes
1149            | Self::LastInsertRowid
1150            | Self::Random
1151            | Self::SqliteVersion
1152            | Self::TursoVersion
1153            | Self::SqliteSourceId
1154            | Self::TotalChanges => &[0],
1155            #[cfg(clt_turso_feature = "test_helper")]
1156            Self::TestNondetCounter => &[0],
1157            // 1-arg
1158            Self::Abs
1159            | Self::Hex
1160            | Self::Length
1161            | Self::Lower
1162            | Self::OctetLength
1163            | Self::Quote
1164            | Self::UnistrQuote
1165            | Self::RandomBlob
1166            | Self::Sign
1167            | Self::Soundex
1168            | Self::Typeof
1169            | Self::Unicode
1170            | Self::Unistr
1171            | Self::Upper
1172            | Self::ZeroBlob
1173            | Self::Likely
1174            | Self::Unlikely
1175            | Self::SequenceWatermark => &[1],
1176            // 2-arg
1177            Self::Glob
1178            | Self::Instr
1179            | Self::Nullif
1180            | Self::IfNull
1181            | Self::Likelihood
1182            | Self::TimeDiff => &[2],
1183            // 3-arg
1184            Self::Iif | Self::Replace => &[3],
1185            // Multi-arity (one row per valid arity)
1186            Self::Like => &[2, 3],
1187            Self::Trim | Self::LTrim | Self::RTrim | Self::Round | Self::Unhex => &[1, 2],
1188            Self::Substr | Self::Substring => &[2, 3],
1189            // Truly variable-arg
1190            Self::Char
1191            | Self::Coalesce
1192            | Self::Concat
1193            | Self::ConcatWs
1194            | Self::Date
1195            | Self::Time
1196            | Self::DateTime
1197            | Self::UnixEpoch
1198            | Self::JulianDay
1199            | Self::StrfTime
1200            | Self::Printf => &[-1],
1201            #[cfg(clt_turso_feature = "fs")]
1202            #[cfg(not(target_family = "wasm"))]
1203            Self::LoadExtension => &[-1],
1204            // Internal functions — arity doesn't matter since they're filtered out
1205            Self::Cast
1206            | Self::StatInit
1207            | Self::StatPush
1208            | Self::StatGet
1209            | Self::Attach
1210            | Self::Detach
1211            | Self::TableColumnsJsonArray
1212            | Self::BinRecordJsonObject
1213            | Self::ConnTxnId
1214            | Self::IsAutocommit => &[0],
1215            // Scalar max/min (multi-arg)
1216            Self::Max | Self::Min => &[-1],
1217            // SQL-standard string and math extensions
1218            Self::Gcd | Self::Lcm | Self::Repeat => &[2],
1219            Self::Lpad | Self::Rpad => &[2, 3],
1220            // Test functions for custom types (1-arg encode/decode, 2-arg operators)
1221            Self::TestUintEncode | Self::TestUintDecode | Self::StringReverse => &[1],
1222            Self::TestUintAdd
1223            | Self::TestUintSub
1224            | Self::TestUintMul
1225            | Self::TestUintDiv
1226            | Self::TestUintLt
1227            | Self::TestUintEq => &[2],
1228            // Built-in type functions
1229            Self::BooleanToInt
1230            | Self::IntToBoolean
1231            | Self::ValidateIpAddr
1232            | Self::NumericDecode => &[1],
1233            Self::NumericAdd
1234            | Self::NumericSub
1235            | Self::NumericMul
1236            | Self::NumericDiv
1237            | Self::NumericLt
1238            | Self::NumericEq => &[2],
1239            Self::NumericEncode => &[3],
1240            // Array construction / element access
1241            Self::Array => &[-1], // variable arity
1242            Self::ArrayElement => &[2],
1243            Self::ArraySetElement => &[3],
1244            // Array functions
1245            Self::ArrayLength => &[1, 2],
1246            Self::ArrayAppend
1247            | Self::ArrayPrepend
1248            | Self::ArrayCat
1249            | Self::ArrayRemove
1250            | Self::ArrayContains
1251            | Self::ArrayPosition
1252            | Self::ArrayOverlap
1253            | Self::ArrayContainsAll => &[2],
1254            Self::ArraySlice => &[3],
1255            Self::StringToArray => &[2, 3],
1256            Self::ArrayToString => &[2, 3],
1257            // Struct/Union functions
1258            // struct_pack is intentionally variable-arity: field count validation
1259            // happens at INSERT time when the value is stored into a typed column.
1260            // Standalone calls produce a generic record blob.
1261            Self::StructPack => &[-1],
1262            Self::StructExtractFunc => &[2], // struct_extract(col, 'field')
1263            Self::UnionValueFunc => &[2],    // union_value('tag', value)
1264            Self::UnionTagFunc => &[1],      // union_tag(col)
1265            Self::UnionExtractFunc => &[2],  // union_extract(col, 'tag')
1266            // Sequence functions
1267            Self::NextVal | Self::CurrVal => &[1],
1268            Self::SetVal => &[2, 3],
1269        }
1270    }
1271
1272    /// Returns true for functions that can turn NULL arguments into a non-NULL result.
1273    ///
1274    /// This is used by planner/optimizer logic that needs to reason about whether
1275    /// predicates are null-rejecting for outer-join simplification.
1276    pub fn can_mask_nulls(&self) -> bool {
1277        matches!(self, Self::Coalesce | Self::IfNull)
1278    }
1279}
1280
1281#[derive(Debug, Clone, PartialEq, strum::EnumIter)]
1282pub enum MathFunc {
1283    Acos,
1284    Acosh,
1285    Asin,
1286    Asinh,
1287    Atan,
1288    Atan2,
1289    Atanh,
1290    Ceil,
1291    Ceiling,
1292    Cos,
1293    Cosh,
1294    Degrees,
1295    Exp,
1296    Floor,
1297    Ln,
1298    Log,
1299    Log10,
1300    Log2,
1301    Mod,
1302    Pi,
1303    Pow,
1304    Power,
1305    Radians,
1306    Sin,
1307    Sinh,
1308    Sqrt,
1309    Tan,
1310    Tanh,
1311    Trunc,
1312}
1313
1314pub enum MathFuncArity {
1315    Nullary,
1316    Unary,
1317    Binary,
1318    UnaryOrBinary,
1319}
1320
1321impl Deterministic for MathFunc {
1322    fn is_deterministic(&self) -> bool {
1323        true
1324    }
1325}
1326
1327impl MathFunc {
1328    pub fn arity(&self) -> MathFuncArity {
1329        match self {
1330            Self::Pi => MathFuncArity::Nullary,
1331            Self::Acos
1332            | Self::Acosh
1333            | Self::Asin
1334            | Self::Asinh
1335            | Self::Atan
1336            | Self::Atanh
1337            | Self::Ceil
1338            | Self::Ceiling
1339            | Self::Cos
1340            | Self::Cosh
1341            | Self::Degrees
1342            | Self::Exp
1343            | Self::Floor
1344            | Self::Ln
1345            | Self::Log10
1346            | Self::Log2
1347            | Self::Radians
1348            | Self::Sin
1349            | Self::Sinh
1350            | Self::Sqrt
1351            | Self::Tan
1352            | Self::Tanh
1353            | Self::Trunc => MathFuncArity::Unary,
1354
1355            Self::Atan2 | Self::Mod | Self::Pow | Self::Power => MathFuncArity::Binary,
1356
1357            Self::Log => MathFuncArity::UnaryOrBinary,
1358        }
1359    }
1360
1361    pub fn arities(&self) -> &'static [i32] {
1362        match self.arity() {
1363            MathFuncArity::Nullary => &[0],
1364            MathFuncArity::Unary => &[1],
1365            MathFuncArity::Binary => &[2],
1366            MathFuncArity::UnaryOrBinary => &[1, 2],
1367        }
1368    }
1369}
1370
1371impl Display for MathFunc {
1372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1373        let str = match self {
1374            Self::Acos => "acos",
1375            Self::Acosh => "acosh",
1376            Self::Asin => "asin",
1377            Self::Asinh => "asinh",
1378            Self::Atan => "atan",
1379            Self::Atan2 => "atan2",
1380            Self::Atanh => "atanh",
1381            Self::Ceil => "ceil",
1382            Self::Ceiling => "ceiling",
1383            Self::Cos => "cos",
1384            Self::Cosh => "cosh",
1385            Self::Degrees => "degrees",
1386            Self::Exp => "exp",
1387            Self::Floor => "floor",
1388            Self::Ln => "ln",
1389            Self::Log => "log",
1390            Self::Log10 => "log10",
1391            Self::Log2 => "log2",
1392            Self::Mod => "mod",
1393            Self::Pi => "pi",
1394            Self::Pow => "pow",
1395            Self::Power => "power",
1396            Self::Radians => "radians",
1397            Self::Sin => "sin",
1398            Self::Sinh => "sinh",
1399            Self::Sqrt => "sqrt",
1400            Self::Tan => "tan",
1401            Self::Tanh => "tanh",
1402            Self::Trunc => "trunc",
1403        };
1404        write!(f, "{str}")
1405    }
1406}
1407
1408#[derive(Debug, Clone)]
1409pub enum AlterTableFunc {
1410    RenameTable,
1411    AlterColumn,
1412    RenameColumn,
1413}
1414
1415impl Display for AlterTableFunc {
1416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1417        match self {
1418            AlterTableFunc::RenameTable => write!(f, "limbo_rename_table"),
1419            AlterTableFunc::RenameColumn => write!(f, "limbo_rename_column"),
1420            AlterTableFunc::AlterColumn => write!(f, "limbo_alter_column"),
1421        }
1422    }
1423}
1424
1425#[derive(Debug, Clone)]
1426pub enum Func {
1427    Agg(AggFunc),
1428    Window(WindowFunc),
1429    Scalar(ScalarFunc),
1430    Math(MathFunc),
1431    Vector(VectorFunc),
1432    #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1433    Fts(FtsFunc),
1434    #[cfg(clt_turso_feature = "json")]
1435    Json(JsonFunc),
1436    AlterTable(AlterTableFunc),
1437    External(Arc<ExternalFunc>),
1438}
1439
1440impl Display for Func {
1441    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1442        match self {
1443            Self::Agg(agg_func) => write!(f, "{}", agg_func.as_str()),
1444            Self::Window(window_func) => write!(f, "{window_func}"),
1445            Self::Scalar(scalar_func) => write!(f, "{scalar_func}"),
1446            Self::Math(math_func) => write!(f, "{math_func}"),
1447            Self::Vector(vector_func) => write!(f, "{vector_func}"),
1448            #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1449            Self::Fts(fts_func) => write!(f, "{fts_func}"),
1450            #[cfg(clt_turso_feature = "json")]
1451            Self::Json(json_func) => write!(f, "{json_func}"),
1452            Self::External(generic_func) => write!(f, "{generic_func}"),
1453            Self::AlterTable(alter_func) => write!(f, "{alter_func}"),
1454        }
1455    }
1456}
1457
1458#[derive(Debug, Clone)]
1459pub struct FuncCtx {
1460    pub func: Func,
1461    pub arg_count: usize,
1462}
1463
1464impl Deterministic for Func {
1465    fn is_deterministic(&self) -> bool {
1466        match self {
1467            Self::Agg(agg_func) => agg_func.is_deterministic(),
1468            Self::Window(window_func) => window_func.is_deterministic(),
1469            Self::Scalar(scalar_func) => scalar_func.is_deterministic(),
1470            Self::Math(math_func) => math_func.is_deterministic(),
1471            Self::Vector(vector_func) => vector_func.is_deterministic(),
1472            #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1473            Self::Fts(fts_func) => fts_func.is_deterministic(),
1474            #[cfg(clt_turso_feature = "json")]
1475            Self::Json(json_func) => json_func.is_deterministic(),
1476            Self::External(external_func) => external_func.is_deterministic(),
1477            Self::AlterTable(_) => true,
1478        }
1479    }
1480}
1481
1482impl Func {
1483    pub fn supports_star_syntax(&self) -> bool {
1484        // Functions that need star expansion also support star syntax
1485        if self.needs_star_expansion() {
1486            return true;
1487        }
1488        match self {
1489            Self::Scalar(scalar_func) => {
1490                let basic = matches!(
1491                    scalar_func,
1492                    ScalarFunc::Changes
1493                        | ScalarFunc::Random
1494                        | ScalarFunc::TotalChanges
1495                        | ScalarFunc::SqliteVersion
1496                        | ScalarFunc::TursoVersion
1497                        | ScalarFunc::SqliteSourceId
1498                        | ScalarFunc::LastInsertRowid
1499                );
1500                #[cfg(clt_turso_feature = "test_helper")]
1501                let basic = basic || matches!(scalar_func, ScalarFunc::TestNondetCounter);
1502                basic
1503            }
1504            Self::Math(math_func) => {
1505                matches!(math_func.arity(), MathFuncArity::Nullary)
1506            }
1507            // Aggregate functions with (*) syntax are handled separately in the planner
1508            Self::Agg(_) => false,
1509            Self::Window(_) => false,
1510            _ => false,
1511        }
1512    }
1513
1514    /// Returns true for functions that can turn NULL arguments into a non-NULL result.
1515    ///
1516    /// This metadata is currently used by optimizer null-rejection analysis.
1517    pub fn can_mask_nulls(&self) -> bool {
1518        match self {
1519            Self::Scalar(scalar_func) => scalar_func.can_mask_nulls(),
1520            _ => false,
1521        }
1522    }
1523
1524    /// Returns true if the function needs the `*` to be expanded to all columns
1525    /// from the referenced tables. This is used for functions like `json_object(*)`
1526    /// and `jsonb_object(*)` which create a JSON object with column names as keys
1527    /// and column values as values.
1528    #[cfg(clt_turso_feature = "json")]
1529    pub fn needs_star_expansion(&self) -> bool {
1530        matches!(
1531            self,
1532            Self::Json(JsonFunc::JsonObject) | Self::Json(JsonFunc::JsonbObject)
1533        )
1534    }
1535
1536    #[cfg(not(clt_turso_feature = "json"))]
1537    pub fn needs_star_expansion(&self) -> bool {
1538        false
1539    }
1540    pub fn resolve_function(name: &str, arg_count: usize) -> Result<Option<Self>, LimboError> {
1541        let normalized_name = crate::util::normalize_ident(name);
1542        match normalized_name.as_str() {
1543            "avg" => {
1544                if arg_count != 1 {
1545                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1546                }
1547                Ok(Some(Self::Agg(AggFunc::Avg)))
1548            }
1549            "count" => {
1550                // Handle both COUNT() and COUNT(expr) cases
1551                if arg_count == 0 {
1552                    Ok(Some(Self::Agg(AggFunc::Count0))) // COUNT() case
1553                } else if arg_count == 1 {
1554                    Ok(Some(Self::Agg(AggFunc::Count))) // COUNT(expr) case
1555                } else {
1556                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1557                }
1558            }
1559            "group_concat" => {
1560                if arg_count != 1 && arg_count != 2 {
1561                    println!("{arg_count}");
1562                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1563                }
1564                Ok(Some(Self::Agg(AggFunc::GroupConcat)))
1565            }
1566            "max" if arg_count > 1 => Ok(Some(Self::Scalar(ScalarFunc::Max))),
1567            "max" => {
1568                if arg_count < 1 {
1569                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1570                }
1571                Ok(Some(Self::Agg(AggFunc::Max)))
1572            }
1573            "min" if arg_count > 1 => Ok(Some(Self::Scalar(ScalarFunc::Min))),
1574            "min" => {
1575                if arg_count < 1 {
1576                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1577                }
1578                Ok(Some(Self::Agg(AggFunc::Min)))
1579            }
1580            "nullif" if arg_count == 2 => Ok(Some(Self::Scalar(ScalarFunc::Nullif))),
1581            "string_agg" => {
1582                if arg_count != 2 {
1583                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1584                }
1585                Ok(Some(Self::Agg(AggFunc::StringAgg)))
1586            }
1587            "sum" => {
1588                if arg_count != 1 {
1589                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1590                }
1591                Ok(Some(Self::Agg(AggFunc::Sum)))
1592            }
1593            "total" => {
1594                if arg_count != 1 {
1595                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1596                }
1597                Ok(Some(Self::Agg(AggFunc::Total)))
1598            }
1599            "row_number" => {
1600                if arg_count != 0 {
1601                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1602                }
1603                Ok(Some(Self::Window(WindowFunc::RowNumber)))
1604            }
1605            "timediff" => {
1606                if arg_count != 2 {
1607                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1608                }
1609                Ok(Some(Self::Scalar(ScalarFunc::TimeDiff)))
1610            }
1611            "array_agg" => Ok(Some(Self::Agg(AggFunc::ArrayAgg))),
1612            #[cfg(clt_turso_feature = "json")]
1613            "jsonb_group_array" => Ok(Some(Self::Agg(AggFunc::JsonbGroupArray))),
1614            #[cfg(clt_turso_feature = "json")]
1615            "json_group_array" => Ok(Some(Self::Agg(AggFunc::JsonGroupArray))),
1616            #[cfg(clt_turso_feature = "json")]
1617            "jsonb_group_object" => Ok(Some(Self::Agg(AggFunc::JsonbGroupObject))),
1618            #[cfg(clt_turso_feature = "json")]
1619            "json_group_object" => Ok(Some(Self::Agg(AggFunc::JsonGroupObject))),
1620            "char" | "chr" => Ok(Some(Self::Scalar(ScalarFunc::Char))),
1621            "coalesce" => Ok(Some(Self::Scalar(ScalarFunc::Coalesce))),
1622            "concat" => {
1623                if arg_count == 0 {
1624                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1625                }
1626                Ok(Some(Self::Scalar(ScalarFunc::Concat)))
1627            }
1628            "concat_ws" => {
1629                if arg_count < 2 {
1630                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1631                }
1632                Ok(Some(Self::Scalar(ScalarFunc::ConcatWs)))
1633            }
1634            "changes" => Ok(Some(Self::Scalar(ScalarFunc::Changes))),
1635            "total_changes" => Ok(Some(Self::Scalar(ScalarFunc::TotalChanges))),
1636            "glob" => Ok(Some(Self::Scalar(ScalarFunc::Glob))),
1637            "ifnull" => Ok(Some(Self::Scalar(ScalarFunc::IfNull))),
1638            "if" | "iif" => Ok(Some(Self::Scalar(ScalarFunc::Iif))),
1639            "instr" | "strpos" => Ok(Some(Self::Scalar(ScalarFunc::Instr))),
1640            "like" => Ok(Some(Self::Scalar(ScalarFunc::Like))),
1641            "abs" => Ok(Some(Self::Scalar(ScalarFunc::Abs))),
1642            "upper" => Ok(Some(Self::Scalar(ScalarFunc::Upper))),
1643            "lower" => Ok(Some(Self::Scalar(ScalarFunc::Lower))),
1644            "random" => Ok(Some(Self::Scalar(ScalarFunc::Random))),
1645            "randomblob" => Ok(Some(Self::Scalar(ScalarFunc::RandomBlob))),
1646            "trim" | "btrim" => Ok(Some(Self::Scalar(ScalarFunc::Trim))),
1647            "ltrim" => Ok(Some(Self::Scalar(ScalarFunc::LTrim))),
1648            "rtrim" => Ok(Some(Self::Scalar(ScalarFunc::RTrim))),
1649            "round" => Ok(Some(Self::Scalar(ScalarFunc::Round))),
1650            "length" | "char_length" | "character_length" => {
1651                Ok(Some(Self::Scalar(ScalarFunc::Length)))
1652            }
1653            "octet_length" => Ok(Some(Self::Scalar(ScalarFunc::OctetLength))),
1654            "sign" => Ok(Some(Self::Scalar(ScalarFunc::Sign))),
1655            "substr" => {
1656                if arg_count != 2 && arg_count != 3 {
1657                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1658                }
1659                Ok(Some(Self::Scalar(ScalarFunc::Substr)))
1660            }
1661            "substring" => {
1662                if arg_count != 2 && arg_count != 3 {
1663                    crate::bail_parse_error!("wrong number of arguments to function {}()", name)
1664                }
1665                Ok(Some(Self::Scalar(ScalarFunc::Substring)))
1666            }
1667            "date" => Ok(Some(Self::Scalar(ScalarFunc::Date))),
1668            "time" => Ok(Some(Self::Scalar(ScalarFunc::Time))),
1669            "datetime" => Ok(Some(Self::Scalar(ScalarFunc::DateTime))),
1670            "typeof" => Ok(Some(Self::Scalar(ScalarFunc::Typeof))),
1671            "last_insert_rowid" => Ok(Some(Self::Scalar(ScalarFunc::LastInsertRowid))),
1672            "unicode" => Ok(Some(Self::Scalar(ScalarFunc::Unicode))),
1673            "unistr" => Ok(Some(Self::Scalar(ScalarFunc::Unistr))),
1674            "unistr_quote" => Ok(Some(Self::Scalar(ScalarFunc::UnistrQuote))),
1675            "quote" => Ok(Some(Self::Scalar(ScalarFunc::Quote))),
1676            "sqlite_version" => Ok(Some(Self::Scalar(ScalarFunc::SqliteVersion))),
1677            "turso_version" => Ok(Some(Self::Scalar(ScalarFunc::TursoVersion))),
1678            "sqlite_source_id" => Ok(Some(Self::Scalar(ScalarFunc::SqliteSourceId))),
1679            "replace" => Ok(Some(Self::Scalar(ScalarFunc::Replace))),
1680            "likely" => Ok(Some(Self::Scalar(ScalarFunc::Likely))),
1681            "likelihood" => Ok(Some(Self::Scalar(ScalarFunc::Likelihood))),
1682            "unlikely" => Ok(Some(Self::Scalar(ScalarFunc::Unlikely))),
1683            #[cfg(clt_turso_feature = "json")]
1684            "json" => Ok(Some(Self::Json(JsonFunc::Json))),
1685            #[cfg(clt_turso_feature = "json")]
1686            "jsonb" => Ok(Some(Self::Json(JsonFunc::Jsonb))),
1687            #[cfg(clt_turso_feature = "json")]
1688            "json_array_length" => Ok(Some(Self::Json(JsonFunc::JsonArrayLength))),
1689            #[cfg(clt_turso_feature = "json")]
1690            "json_array" => Ok(Some(Self::Json(JsonFunc::JsonArray))),
1691            #[cfg(clt_turso_feature = "json")]
1692            "jsonb_array" => Ok(Some(Self::Json(JsonFunc::JsonbArray))),
1693            #[cfg(clt_turso_feature = "json")]
1694            "json_extract" => Ok(Some(Func::Json(JsonFunc::JsonExtract))),
1695            #[cfg(clt_turso_feature = "json")]
1696            "jsonb_extract" => Ok(Some(Func::Json(JsonFunc::JsonbExtract))),
1697            #[cfg(clt_turso_feature = "json")]
1698            "json_object" => Ok(Some(Func::Json(JsonFunc::JsonObject))),
1699            #[cfg(clt_turso_feature = "json")]
1700            "jsonb_object" => Ok(Some(Func::Json(JsonFunc::JsonbObject))),
1701            #[cfg(clt_turso_feature = "json")]
1702            "json_type" => Ok(Some(Func::Json(JsonFunc::JsonType))),
1703            #[cfg(clt_turso_feature = "json")]
1704            "json_error_position" => Ok(Some(Self::Json(JsonFunc::JsonErrorPosition))),
1705            #[cfg(clt_turso_feature = "json")]
1706            "json_valid" => Ok(Some(Self::Json(JsonFunc::JsonValid))),
1707            #[cfg(clt_turso_feature = "json")]
1708            "json_patch" => Ok(Some(Self::Json(JsonFunc::JsonPatch))),
1709            #[cfg(clt_turso_feature = "json")]
1710            "jsonb_patch" => Ok(Some(Self::Json(JsonFunc::JsonbPatch))),
1711            #[cfg(clt_turso_feature = "json")]
1712            "json_remove" => Ok(Some(Self::Json(JsonFunc::JsonRemove))),
1713            #[cfg(clt_turso_feature = "json")]
1714            "jsonb_remove" => Ok(Some(Self::Json(JsonFunc::JsonbRemove))),
1715            #[cfg(clt_turso_feature = "json")]
1716            "json_replace" => Ok(Some(Self::Json(JsonFunc::JsonReplace))),
1717            #[cfg(clt_turso_feature = "json")]
1718            "json_insert" => Ok(Some(Self::Json(JsonFunc::JsonInsert))),
1719            #[cfg(clt_turso_feature = "json")]
1720            "jsonb_insert" => Ok(Some(Self::Json(JsonFunc::JsonbInsert))),
1721            #[cfg(clt_turso_feature = "json")]
1722            "jsonb_replace" => Ok(Some(Self::Json(JsonFunc::JsonbReplace))),
1723            #[cfg(clt_turso_feature = "json")]
1724            "json_pretty" => Ok(Some(Self::Json(JsonFunc::JsonPretty))),
1725            #[cfg(clt_turso_feature = "json")]
1726            "json_set" => Ok(Some(Self::Json(JsonFunc::JsonSet))),
1727            #[cfg(clt_turso_feature = "json")]
1728            "jsonb_set" => Ok(Some(Self::Json(JsonFunc::JsonbSet))),
1729            #[cfg(clt_turso_feature = "json")]
1730            "json_quote" => Ok(Some(Self::Json(JsonFunc::JsonQuote))),
1731            "unixepoch" => Ok(Some(Self::Scalar(ScalarFunc::UnixEpoch))),
1732            "julianday" => Ok(Some(Self::Scalar(ScalarFunc::JulianDay))),
1733            "hex" => Ok(Some(Self::Scalar(ScalarFunc::Hex))),
1734            "unhex" => Ok(Some(Self::Scalar(ScalarFunc::Unhex))),
1735            "zeroblob" => Ok(Some(Self::Scalar(ScalarFunc::ZeroBlob))),
1736            "soundex" => Ok(Some(Self::Scalar(ScalarFunc::Soundex))),
1737            "table_columns_json_array" => Ok(Some(Self::Scalar(ScalarFunc::TableColumnsJsonArray))),
1738            "bin_record_json_object" => Ok(Some(Self::Scalar(ScalarFunc::BinRecordJsonObject))),
1739            "conn_txn_id" => Ok(Some(Self::Scalar(ScalarFunc::ConnTxnId))),
1740            "is_autocommit" => Ok(Some(Self::Scalar(ScalarFunc::IsAutocommit))),
1741            "sequence_watermark_experimental" => {
1742                Ok(Some(Self::Scalar(ScalarFunc::SequenceWatermark)))
1743            }
1744            "acos" => Ok(Some(Self::Math(MathFunc::Acos))),
1745            "acosh" => Ok(Some(Self::Math(MathFunc::Acosh))),
1746            "asin" => Ok(Some(Self::Math(MathFunc::Asin))),
1747            "asinh" => Ok(Some(Self::Math(MathFunc::Asinh))),
1748            "atan" => Ok(Some(Self::Math(MathFunc::Atan))),
1749            "atan2" => Ok(Some(Self::Math(MathFunc::Atan2))),
1750            "atanh" => Ok(Some(Self::Math(MathFunc::Atanh))),
1751            "ceil" => Ok(Some(Self::Math(MathFunc::Ceil))),
1752            "ceiling" => Ok(Some(Self::Math(MathFunc::Ceiling))),
1753            "cos" => Ok(Some(Self::Math(MathFunc::Cos))),
1754            "cosh" => Ok(Some(Self::Math(MathFunc::Cosh))),
1755            "degrees" => Ok(Some(Self::Math(MathFunc::Degrees))),
1756            "exp" => Ok(Some(Self::Math(MathFunc::Exp))),
1757            "floor" => Ok(Some(Self::Math(MathFunc::Floor))),
1758            "ln" => Ok(Some(Self::Math(MathFunc::Ln))),
1759            "log" => Ok(Some(Self::Math(MathFunc::Log))),
1760            "log10" => Ok(Some(Self::Math(MathFunc::Log10))),
1761            "log2" => Ok(Some(Self::Math(MathFunc::Log2))),
1762            "mod" => Ok(Some(Self::Math(MathFunc::Mod))),
1763            "pi" => Ok(Some(Self::Math(MathFunc::Pi))),
1764            "pow" => Ok(Some(Self::Math(MathFunc::Pow))),
1765            "power" => Ok(Some(Self::Math(MathFunc::Power))),
1766            "radians" => Ok(Some(Self::Math(MathFunc::Radians))),
1767            "sin" => Ok(Some(Self::Math(MathFunc::Sin))),
1768            "sinh" => Ok(Some(Self::Math(MathFunc::Sinh))),
1769            "sqrt" => Ok(Some(Self::Math(MathFunc::Sqrt))),
1770            "tan" => Ok(Some(Self::Math(MathFunc::Tan))),
1771            "tanh" => Ok(Some(Self::Math(MathFunc::Tanh))),
1772            "trunc" => Ok(Some(Self::Math(MathFunc::Trunc))),
1773            #[cfg(clt_turso_feature = "fs")]
1774            #[cfg(not(target_family = "wasm"))]
1775            "load_extension" => Ok(Some(Self::Scalar(ScalarFunc::LoadExtension))),
1776            "strftime" => Ok(Some(Self::Scalar(ScalarFunc::StrfTime))),
1777            "printf" | "format" => Ok(Some(Self::Scalar(ScalarFunc::Printf))),
1778            "vector" => Ok(Some(Self::Vector(VectorFunc::Vector))),
1779            "vector32" => Ok(Some(Self::Vector(VectorFunc::Vector32))),
1780            "vector32_sparse" => Ok(Some(Self::Vector(VectorFunc::Vector32Sparse))),
1781            "vector64" => Ok(Some(Self::Vector(VectorFunc::Vector64))),
1782            "vector8" => Ok(Some(Self::Vector(VectorFunc::Vector8))),
1783            "vector1bit" => Ok(Some(Self::Vector(VectorFunc::Vector1Bit))),
1784            "vector_extract" => Ok(Some(Self::Vector(VectorFunc::VectorExtract))),
1785            "vector_distance_cos" => Ok(Some(Self::Vector(VectorFunc::VectorDistanceCos))),
1786            "vector_distance_l2" => Ok(Some(Self::Vector(VectorFunc::VectorDistanceL2))),
1787            "vector_distance_jaccard" => Ok(Some(Self::Vector(VectorFunc::VectorDistanceJaccard))),
1788            "vector_distance_dot" => Ok(Some(Self::Vector(VectorFunc::VectorDistanceDot))),
1789            "vector_concat" => Ok(Some(Self::Vector(VectorFunc::VectorConcat))),
1790            "vector_slice" => Ok(Some(Self::Vector(VectorFunc::VectorSlice))),
1791            // FTS functions
1792            #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1793            "fts_score" => Ok(Some(Self::Fts(FtsFunc::Score))),
1794            #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1795            "fts_match" => Ok(Some(Self::Fts(FtsFunc::Match))),
1796            #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1797            "fts_highlight" => Ok(Some(Self::Fts(FtsFunc::Highlight))),
1798            // Test type functions (for custom type system testing)
1799            "test_uint_encode" => Ok(Some(Self::Scalar(ScalarFunc::TestUintEncode))),
1800            "test_uint_decode" => Ok(Some(Self::Scalar(ScalarFunc::TestUintDecode))),
1801            "test_uint_add" => Ok(Some(Self::Scalar(ScalarFunc::TestUintAdd))),
1802            "test_uint_sub" => Ok(Some(Self::Scalar(ScalarFunc::TestUintSub))),
1803            "test_uint_mul" => Ok(Some(Self::Scalar(ScalarFunc::TestUintMul))),
1804            "test_uint_div" => Ok(Some(Self::Scalar(ScalarFunc::TestUintDiv))),
1805            "test_uint_lt" => Ok(Some(Self::Scalar(ScalarFunc::TestUintLt))),
1806            "test_uint_eq" => Ok(Some(Self::Scalar(ScalarFunc::TestUintEq))),
1807            #[cfg(clt_turso_feature = "test_helper")]
1808            "test_nondet_counter" => Ok(Some(Self::Scalar(ScalarFunc::TestNondetCounter))),
1809            "string_reverse" | "reverse" => Ok(Some(Self::Scalar(ScalarFunc::StringReverse))),
1810            "gcd" => Ok(Some(Self::Scalar(ScalarFunc::Gcd))),
1811            "lcm" => Ok(Some(Self::Scalar(ScalarFunc::Lcm))),
1812            "repeat" => Ok(Some(Self::Scalar(ScalarFunc::Repeat))),
1813            "lpad" => Ok(Some(Self::Scalar(ScalarFunc::Lpad))),
1814            "rpad" => Ok(Some(Self::Scalar(ScalarFunc::Rpad))),
1815            // Built-in type support functions
1816            "boolean_to_int" => Ok(Some(Self::Scalar(ScalarFunc::BooleanToInt))),
1817            "int_to_boolean" => Ok(Some(Self::Scalar(ScalarFunc::IntToBoolean))),
1818            "validate_ipaddr" => Ok(Some(Self::Scalar(ScalarFunc::ValidateIpAddr))),
1819            "numeric_encode" => Ok(Some(Self::Scalar(ScalarFunc::NumericEncode))),
1820            "numeric_decode" => Ok(Some(Self::Scalar(ScalarFunc::NumericDecode))),
1821            "numeric_add" => Ok(Some(Self::Scalar(ScalarFunc::NumericAdd))),
1822            "numeric_sub" => Ok(Some(Self::Scalar(ScalarFunc::NumericSub))),
1823            "numeric_mul" => Ok(Some(Self::Scalar(ScalarFunc::NumericMul))),
1824            "numeric_div" => Ok(Some(Self::Scalar(ScalarFunc::NumericDiv))),
1825            "numeric_lt" => Ok(Some(Self::Scalar(ScalarFunc::NumericLt))),
1826            "numeric_eq" => Ok(Some(Self::Scalar(ScalarFunc::NumericEq))),
1827            // Array construction / element access (desugared from syntax)
1828            "array" => Ok(Some(Self::Scalar(ScalarFunc::Array))),
1829            "array_element" => Ok(Some(Self::Scalar(ScalarFunc::ArrayElement))),
1830            "array_set_element" => Ok(Some(Self::Scalar(ScalarFunc::ArraySetElement))),
1831            // Array functions
1832            "array_length" | "array_upper" => Ok(Some(Self::Scalar(ScalarFunc::ArrayLength))),
1833            "array_append" => Ok(Some(Self::Scalar(ScalarFunc::ArrayAppend))),
1834            "array_prepend" => Ok(Some(Self::Scalar(ScalarFunc::ArrayPrepend))),
1835            "array_cat" => Ok(Some(Self::Scalar(ScalarFunc::ArrayCat))),
1836            "array_remove" => Ok(Some(Self::Scalar(ScalarFunc::ArrayRemove))),
1837            "array_contains" => Ok(Some(Self::Scalar(ScalarFunc::ArrayContains))),
1838            "array_position" => Ok(Some(Self::Scalar(ScalarFunc::ArrayPosition))),
1839            "array_slice" => Ok(Some(Self::Scalar(ScalarFunc::ArraySlice))),
1840            "string_to_array" => Ok(Some(Self::Scalar(ScalarFunc::StringToArray))),
1841            "array_to_string" => Ok(Some(Self::Scalar(ScalarFunc::ArrayToString))),
1842            "array_overlap" | "array_overlaps" => Ok(Some(Self::Scalar(ScalarFunc::ArrayOverlap))),
1843            "array_contains_all" => Ok(Some(Self::Scalar(ScalarFunc::ArrayContainsAll))),
1844            // Struct/Union functions
1845            "struct_pack" => Ok(Some(Self::Scalar(ScalarFunc::StructPack))),
1846            "struct_extract" => Ok(Some(Self::Scalar(ScalarFunc::StructExtractFunc))),
1847            "union_value" => Ok(Some(Self::Scalar(ScalarFunc::UnionValueFunc))),
1848            "union_tag" => Ok(Some(Self::Scalar(ScalarFunc::UnionTagFunc))),
1849            "union_extract" => Ok(Some(Self::Scalar(ScalarFunc::UnionExtractFunc))),
1850            // Sequence functions
1851            "nextval" => Ok(Some(Self::Scalar(ScalarFunc::NextVal))),
1852            "currval" => Ok(Some(Self::Scalar(ScalarFunc::CurrVal))),
1853            "setval" => Ok(Some(Self::Scalar(ScalarFunc::SetVal))),
1854            _ => Ok(None),
1855        }
1856    }
1857
1858    /// Returns a list of all built-in functions for PRAGMA function_list.
1859    /// Derives the list from enum iteration so it stays in sync automatically.
1860    /// Functions with multiple valid arities get one row per arity.
1861    pub fn builtin_function_list() -> Vec<FunctionListEntry> {
1862        let mut funcs = Vec::new();
1863
1864        // Helper: push one entry per arity for a function
1865        let mut push = |name: String, func_type: &'static str, arities: &[i32], det: bool| {
1866            for &narg in arities {
1867                funcs.push(FunctionListEntry {
1868                    name: name.clone(),
1869                    func_type,
1870                    narg,
1871                    deterministic: det,
1872                });
1873            }
1874        };
1875
1876        // Scalar functions (filter out internal-only variants)
1877        for f in ScalarFunc::iter() {
1878            if f.is_internal() {
1879                continue;
1880            }
1881            push(f.to_string(), "s", f.arities(), f.is_deterministic());
1882        }
1883
1884        // Aggregate functions (External is #[strum(disabled)], skipped automatically).
1885        // SQLite reports built-in aggregates as "w" (window-capable) since they
1886        // can all be used with OVER clauses.
1887        for f in AggFunc::iter() {
1888            push(f.to_string(), "w", f.arities(), f.is_deterministic());
1889        }
1890
1891        // Window functions (skip stub variants until they're wired up).
1892        for f in WindowFunc::iter() {
1893            if !f.is_implemented() {
1894                continue;
1895            }
1896            push(f.to_string(), "w", f.arities(), f.is_deterministic());
1897        }
1898
1899        // Math functions (all scalar)
1900        for f in MathFunc::iter() {
1901            push(f.to_string(), "s", f.arities(), f.is_deterministic());
1902        }
1903
1904        // Vector functions (all scalar)
1905        for f in VectorFunc::iter() {
1906            push(f.to_string(), "s", f.arities(), f.is_deterministic());
1907        }
1908
1909        // JSON functions (feature-gated, filter out operator-style entries)
1910        #[cfg(clt_turso_feature = "json")]
1911        for f in JsonFunc::iter() {
1912            if f.is_internal() {
1913                continue;
1914            }
1915            push(f.to_string(), "s", f.arities(), f.is_deterministic());
1916        }
1917
1918        // FTS functions (feature-gated)
1919        #[cfg(all(clt_turso_feature = "fts", not(target_family = "wasm")))]
1920        for f in FtsFunc::iter() {
1921            push(f.to_string(), "s", f.arities(), f.is_deterministic());
1922        }
1923
1924        // Aliases: functions callable under multiple names.
1925        // These are additional names that resolve_function() accepts
1926        // but that map to existing enum variants.
1927        funcs.push(FunctionListEntry {
1928            name: "format".into(),
1929            func_type: "s",
1930            narg: -1,
1931            deterministic: true,
1932        });
1933        funcs.push(FunctionListEntry {
1934            name: "if".into(),
1935            func_type: "s",
1936            narg: 3,
1937            deterministic: true,
1938        });
1939
1940        funcs
1941    }
1942}
1943
1944pub struct FunctionListEntry {
1945    pub name: String,
1946    pub func_type: &'static str, // "s" = scalar, "a" = aggregate, "w" = window
1947    pub narg: i32,               // -1 = variable
1948    pub deterministic: bool,
1949}