Skip to main content

akar_function/
registry.rs

1//! Function registry — manages lookup and registration of all built-in functions.
2//!
3//! Three categories:
4//! - Scalar functions (1-to-1 mapping of inputs to outputs)
5//! - Aggregate functions (N-to-1 reduction)
6//! - Table functions (produce a table of rows)
7
8use akar_common::types::Value;
9use akar_common::vector::DataChunk;
10use hashbrown::HashMap;
11use std::sync::Arc;
12
13// ==================== Scalar Function Types ====================
14
15/// All built-in scalar function variants.
16#[derive(Clone)]
17#[allow(clippy::type_complexity)]
18pub enum ScalarFunction {
19    Arithmetic {
20        op: ArithmeticOp,
21    },
22    Comparison {
23        op: ComparisonOp,
24    },
25    String {
26        op: StringOp,
27    },
28    Cast {
29        target_type: CastTarget,
30    },
31    Date {
32        op: DateOp,
33    },
34    List {
35        op: ListOp,
36    },
37    Map {
38        op: MapOp,
39    },
40    Struct {
41        op: StructOp,
42    },
43    Boolean {
44        op: BooleanOp,
45    },
46    Utility {
47        op: UtilityOp,
48    },
49    Schema {
50        op: SchemaOp,
51    },
52    Array {
53        op: ArrayOp,
54    },
55    /// Path functions — operate on recursive rel / path values.
56    Path {
57        op: PathOp,
58    },
59    /// UUID function — generates random UUIDs.
60    Uuid,
61    /// Extension-provided scalar function with a callback closure.
62    /// The closure receives input values and returns an output value.
63    CustomScalar {
64        name: String,
65        execute: Arc<dyn Fn(&[Value]) -> Result<Value, String> + Send + Sync>,
66    },
67    /// Sequence operation (nextval/currval) — requires catalog access.
68    /// Resolved at the connection/processor level where the catalog is available.
69    SequenceOp {
70        is_nextval: bool,
71    },
72    /// Hash functions — MD5, SHA256, generic HASH.
73    Hash {
74        op: HashOp,
75    },
76    /// Interval constructor functions.
77    Interval {
78        op: IntervalOp,
79    },
80    /// Blob functions — ENCODE, DECODE, OCTET_LENGTH.
81    Blob {
82        op: BlobOp,
83    },
84    /// Union functions — UNION_VALUE, UNION_TAG, UNION_EXTRACT.
85    Union {
86        op: UnionOp,
87    },
88}
89
90impl std::fmt::Debug for ScalarFunction {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            Self::Arithmetic { op } => f.debug_struct("Arithmetic").field("op", op).finish(),
94            Self::Comparison { op } => f.debug_struct("Comparison").field("op", op).finish(),
95            Self::String { op } => f.debug_struct("String").field("op", op).finish(),
96            Self::Cast { target_type } => f.debug_struct("Cast").field("target_type", target_type).finish(),
97            Self::Date { op } => f.debug_struct("Date").field("op", op).finish(),
98            Self::List { op } => f.debug_struct("List").field("op", op).finish(),
99            Self::Map { op } => f.debug_struct("Map").field("op", op).finish(),
100            Self::Struct { op } => f.debug_struct("Struct").field("op", op).finish(),
101            Self::Boolean { op } => f.debug_struct("Boolean").field("op", op).finish(),
102            Self::Utility { op } => f.debug_struct("Utility").field("op", op).finish(),
103            Self::Schema { op } => f.debug_struct("Schema").field("op", op).finish(),
104            Self::Array { op } => f.debug_struct("Array").field("op", op).finish(),
105            Self::Path { op } => f.debug_struct("Path").field("op", op).finish(),
106            Self::Uuid => f.debug_struct("Uuid").finish(),
107            Self::CustomScalar { name, .. } => f.debug_struct("CustomScalar").field("name", name).finish(),
108            Self::SequenceOp { is_nextval } => f.debug_struct("SequenceOp").field("is_nextval", is_nextval).finish(),
109            Self::Hash { op } => f.debug_struct("Hash").field("op", op).finish(),
110            Self::Interval { op } => f.debug_struct("Interval").field("op", op).finish(),
111            Self::Blob { op } => f.debug_struct("Blob").field("op", op).finish(),
112            Self::Union { op } => f.debug_struct("Union").field("op", op).finish(),
113        }
114    }
115}
116
117#[derive(Debug, Clone, Copy)]
118pub enum ArithmeticOp {
119    Add,
120    Sub,
121    Mul,
122    Div,
123    Mod,
124    Abs,
125    Ceil,
126    Floor,
127    Round,
128    Negate,
129    Power,
130    Sqrt,
131    Log,
132    Exp,
133    Sin,
134    Cos,
135    Tan,
136    Asin,
137    Acos,
138    Atan,
139    Atan2,
140    Sinh,
141    Cosh,
142    Tanh,
143    Degrees,
144    Radians,
145    Sign,
146    Pi,
147    Rand,
148    /// Math functions (f64-based, single argument)
149    Cbrt,
150    Cot,
151    Log2,
152    Even,
153    Gcd,
154    Lcm,
155    /// Heavy math functions (C++ port)
156    Factorial,
157    Gamma,
158    Lgamma,
159    /// Seeding the RNG
160    SetSeed,
161    /// Bitwise operations (int64-only, matching C++ hardcoded int64_t)
162    BitwiseAnd,
163    BitwiseOr,
164    BitwiseXor,
165    BitShiftLeft,
166    BitShiftRight,
167}
168
169#[derive(Debug, Clone, Copy)]
170pub enum ComparisonOp {
171    Eq,
172    NotEq,
173    Lt,
174    Lte,
175    Gt,
176    Gte,
177    IsNull,
178    IsNotNull,
179}
180
181#[derive(Debug, Clone, Copy)]
182pub enum StringOp {
183    Concat,
184    Contains,
185    StartsWith,
186    EndsWith,
187    ToUpper,
188    ToLower,
189    Trim,
190    LTrim,
191    RTrim,
192    Length,
193    Reverse,
194    Repeat,
195    Replace,
196    Substring,
197    RegexMatches,
198    RegexReplace,
199    Split,
200    Head,
201    Tail,
202    Left,
203    Right,
204    Lpad,
205    Rpad,
206    /// String basic functions (C++ port)
207    InitCap,
208    Soundex,
209    ConcatWs,
210    SplitPart,
211    ArrayExtract,
212    /// Regex string functions (C++ port)
213    RegexpFullMatch,
214    RegexpExtract,
215    RegexpExtractAll,
216    RegexpSplitToArray,
217    /// String edit distance
218    Levenshtein,
219    /// LIKE pattern matching
220    Like,
221}
222
223#[derive(Debug, Clone, Copy)]
224pub enum CastTarget {
225    String,
226    Int64,
227    Int32,
228    Double,
229    Float,
230    Bool,
231    Date,
232    Timestamp,
233    Interval,
234}
235
236#[derive(Debug, Clone, Copy)]
237pub enum DateOp {
238    DatePart,
239    DateTrunc,
240    DateDiff,
241    DateAdd,
242    CurrentDate,
243    CurrentTimestamp,
244    Year,
245    Month,
246    Day,
247    Hour,
248    Minute,
249    Second,
250    DayName,
251    MonthName,
252    LastDay,
253    MakeDate,
254    /// Timestamp functions (C++ port)
255    Century,
256    EpochMs,
257    ToTimestamp,
258    ToEpochMs,
259}
260
261#[derive(Debug, Clone, Copy)]
262pub enum ListOp {
263    Creation,
264    Extract,
265    Concat,
266    Len,
267    Sort,
268    Reverse,
269    Contains,
270    Append,
271    Prepend,
272    Slice,
273    /// List functions (C++ port)
274    Range,
275    Distinct,
276    Unique,
277    Sum,
278    Product,
279    AnyValue,
280    ToString,
281    Position,
282    HasAll,
283    ReverseSort,
284    /// List predicate functions (non-lambda versions)
285    Any,
286    All,
287    None,
288    Single,
289    Count,
290    Min,
291    Max,
292    HasAny,
293    /// Lambda-based list functions (evaluated by expression evaluator)
294    Transform,
295    Filter,
296    Reduce,
297}
298
299#[derive(Debug, Clone, Copy)]
300pub enum MapOp {
301    Creation,
302    Extract,
303    MapFromEntries,
304    Keys,
305    Values,
306    Contains,
307}
308
309#[derive(Debug, Clone, Copy)]
310pub enum StructOp {
311    Creation,
312    Extract,
313}
314
315#[derive(Debug, Clone, Copy)]
316pub enum BooleanOp {
317    And,
318    Or,
319    Xor,
320    Not,
321}
322
323/// Utility functions.
324#[derive(Debug, Clone, Copy)]
325pub enum UtilityOp {
326    Coalesce,
327    IfNull,
328    TypeOf,
329    NullIf,
330    Size,
331    Error,
332    PgIsReady,
333    /// Return the largest value among arguments (SQL standard, NULLs ignored).
334    Greatest,
335    /// Return the smallest value among arguments (SQL standard, NULLs ignored).
336    Least,
337    /// ConstantOrNull(a, b) — returns a if both a and b are non-NULL, else NULL.
338    /// Utility function for NULL-aware constant propagation.
339    ConstantOrNull,
340}
341
342/// Schema functions — access metadata about nodes, relationships, and values.
343///
344/// These correspond to the C++ schema functions in `function/schema/`:
345/// - `OFFSET(v)` → returns the internal offset of a node/rel ID (INT64)
346/// - `ID(v)` → returns the internal ID (offset + table_id) as InternalID
347/// - `START_NODE(r)` → returns the source node of a relationship
348/// - `END_NODE(r)` → returns the target node of a relationship
349/// - `LABEL(v)` → returns the table name as a string
350#[derive(Debug, Clone, Copy)]
351pub enum SchemaOp {
352    Offset,
353    Id,
354    StartNode,
355    EndNode,
356    Label,
357    /// COST(pattern) — extract total cost from a weighted path.
358    Cost,
359    /// ROWID(pattern) — extract row offset from InternalID.
360    RowId,
361}
362
363/// Path functions — operate on recursive rel / path values (NODES, RELS, LENGTH).
364///
365/// Ported from C++ `function/path/`:
366/// - `NODES(path)` → returns the list of nodes in the path
367/// - `RELS(path)` / `RELATIONSHIPS(path)` → returns the list of rels in the path
368/// - `LENGTH(path)` → returns the number of rels in the path
369#[derive(Debug, Clone, Copy)]
370pub enum PathOp {
371    Nodes,
372    Rels,
373    Length,
374    /// PROPERTIES(path) — extract all properties from path nodes and rels.
375    Properties,
376    /// IS_TRAIL(path) — check if path has no repeated edges (a trail).
377    IsTrail,
378    /// IS_ACYCLIC(path) — check if path has no repeated nodes (acyclic).
379    IsAcyclic,
380}
381
382/// Array math functions — element-wise operations on numeric lists.
383///
384/// These correspond to the C++ array functions in `function/array/`:
385/// - `array_cosine_similarity(a, b)` → cosine similarity between two arrays
386/// - `array_distance(a, b)` → Euclidean distance between two arrays
387/// - `array_inner_product(a, b)` → dot/inner product of two arrays
388/// - `array_cross_product(a, b)` → 3D cross product of two arrays
389/// - `array_squared_distance(a, b)` → squared Euclidean distance
390#[derive(Debug, Clone, Copy)]
391pub enum ArrayOp {
392    CosineSimilarity,
393    Distance,
394    InnerProduct,
395    DotProduct,
396    CrossProduct,
397    SquaredDistance,
398    Intersect,
399}
400
401/// Hash functions — ported from C++ `function/hash/`.
402///
403/// - `MD5(str)` → 32-char hex string
404/// - `SHA256(str)` → 64-char hex string
405/// - `HASH(val)` → Int64 hash of any value
406#[derive(Debug, Clone, Copy)]
407pub enum HashOp {
408    Md5,
409    Sha256,
410    Hash,
411}
412
413/// Interval constructor functions — ported from C++ `function/interval/`.
414///
415/// Each takes INT64 and returns INTERVAL:
416/// - `TO_YEARS(n)` → months = n * 12
417/// - `TO_MONTHS(n)` → months = n
418/// - `TO_DAYS(n)` → days = n
419/// - `TO_HOURS(n)` → micros = n * 3_600_000_000
420/// - `TO_MINUTES(n)` → micros = n * 60_000_000
421/// - `TO_SECONDS(n)` → micros = n * 1_000_000
422/// - `TO_MILLISECONDS(n)` → micros = n * 1000
423/// - `TO_MICROSECONDS(n)` → micros = n
424#[derive(Debug, Clone, Copy)]
425pub enum IntervalOp {
426    ToYears,
427    ToMonths,
428    ToDays,
429    ToHours,
430    ToMinutes,
431    ToSeconds,
432    ToMilliseconds,
433    ToMicroseconds,
434}
435
436/// Blob functions — ported from C++ `function/blob/`.
437///
438/// - `ENCODE(str)` → Blob: copy string bytes into a blob
439/// - `DECODE(blob)` → String: convert blob to string (UTF-8 validated)
440/// - `OCTET_LENGTH(blob)` → Int64: byte length of blob
441#[derive(Debug, Clone, Copy)]
442pub enum BlobOp {
443    Encode,
444    Decode,
445    OctetLength,
446    ToBase64,
447    FromBase64,
448    BlobFromBytes,
449}
450
451/// Union functions — ported from C++ `function/union/`.
452///
453/// Union is stored internally as Struct with a `"tag"` field (UInt16) prepended.
454/// - `UNION_VALUE(val)` → create a union wrapping a value
455/// - `UNION_TAG(u)` → return active tag name as string
456/// - `UNION_EXTRACT(u, key)` → extract value by field name
457#[derive(Debug, Clone, Copy)]
458pub enum UnionOp {
459    UnionValue,
460    UnionTag,
461    UnionExtract,
462}
463
464// ==================== Aggregate Function Types ====================
465
466/// All built-in aggregate functions.
467#[derive(Debug, Clone)]
468pub enum AggregateFunction {
469    Count,
470    Sum,
471    Avg,
472    Min,
473    Max,
474    Collect,
475    CountStar,
476    StdDev,
477    Variance,
478    /// STRING_AGG(expr, delimiter) — concatenates strings.
479    StringAgg {
480        delimiter: String,
481    },
482    /// PERCENTILE_DISC(expr, percentile) — discrete percentile.
483    PercentileDisc {
484        percentile: f64,
485    },
486    /// PERCENTILE_CONT(expr, percentile) — continuous percentile.
487    PercentileCont {
488        percentile: f64,
489    },
490    /// COUNT_IF(condition) — count rows where condition is TRUE.
491    CountIf,
492}
493
494// ==================== Table Function Types ====================
495
496/// All built-in table functions.
497#[derive(Clone)]
498pub enum TableFunction {
499    ScanCsv {
500        path: String,
501    },
502    ScanParquet {
503        path: String,
504    },
505    ScanJson {
506        path: String,
507    },
508    ListTables,
509    ShowColumns {
510        table_name: String,
511    },
512    CurrentSetting {
513        key: String,
514    },
515    /// Extension-specific custom table function (tag-based, no callback).
516    /// The `name` field identifies which custom function to execute.
517    Custom {
518        name: String,
519    },
520    /// Extension-provided table function with a callback closure.
521    /// The closure receives input args and fills a mutable DataChunk.
522    #[allow(clippy::type_complexity)]
523    CustomTable {
524        name: String,
525        execute: Arc<dyn Fn(&[Value], &mut DataChunk) -> Result<(), String> + Send + Sync>,
526    },
527}
528
529impl std::fmt::Debug for TableFunction {
530    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
531        match self {
532            Self::ScanCsv { path } => f.debug_struct("ScanCsv").field("path", path).finish(),
533            Self::ScanParquet { path } => f.debug_struct("ScanParquet").field("path", path).finish(),
534            Self::ScanJson { path } => f.debug_struct("ScanJson").field("path", path).finish(),
535            Self::ListTables => write!(f, "ListTables"),
536            Self::ShowColumns { table_name } => f.debug_struct("ShowColumns").field("table_name", table_name).finish(),
537            Self::CurrentSetting { key } => f.debug_struct("CurrentSetting").field("key", key).finish(),
538            Self::Custom { name } => f.debug_struct("Custom").field("name", name).finish(),
539            Self::CustomTable { name, .. } => f.debug_struct("CustomTable").field("name", name).finish(),
540        }
541    }
542}
543
544/// A resolved function with its variant.
545#[derive(Debug, Clone)]
546pub enum ResolvedFunction {
547    Scalar(ScalarFunction),
548    Aggregate(AggregateFunction),
549    Table(TableFunction),
550}
551
552// ==================== Registry ====================
553
554/// Registry of all built-in functions (scalar, aggregate, table).
555#[derive(Default)]
556pub struct FunctionRegistry {
557    scalar_functions: HashMap<String, ScalarFunction>,
558    aggregate_functions: HashMap<String, AggregateFunction>,
559    table_functions: HashMap<String, TableFunction>,
560}
561
562impl FunctionRegistry {
563    pub fn new() -> Self {
564        let mut reg = Self::default();
565        reg.register_builtins();
566        reg
567    }
568
569    fn register_builtins(&mut self) {
570        // --- Arithmetic ---
571        self.register_scalar("+", ScalarFunction::Arithmetic { op: ArithmeticOp::Add });
572        self.register_scalar("-", ScalarFunction::Arithmetic { op: ArithmeticOp::Sub });
573        self.register_scalar("*", ScalarFunction::Arithmetic { op: ArithmeticOp::Mul });
574        self.register_scalar("/", ScalarFunction::Arithmetic { op: ArithmeticOp::Div });
575        self.register_scalar("%", ScalarFunction::Arithmetic { op: ArithmeticOp::Mod });
576        self.register_scalar("abs", ScalarFunction::Arithmetic { op: ArithmeticOp::Abs });
577        self.register_scalar("ceil", ScalarFunction::Arithmetic { op: ArithmeticOp::Ceil });
578        // Standard SQL alias
579        self.register_scalar("ceiling", ScalarFunction::Arithmetic { op: ArithmeticOp::Ceil });
580        self.register_scalar(
581            "floor",
582            ScalarFunction::Arithmetic {
583                op: ArithmeticOp::Floor,
584            },
585        );
586        self.register_scalar(
587            "round",
588            ScalarFunction::Arithmetic {
589                op: ArithmeticOp::Round,
590            },
591        );
592        self.register_scalar(
593            "^",
594            ScalarFunction::Arithmetic {
595                op: ArithmeticOp::Power,
596            },
597        );
598        self.register_scalar("sqrt", ScalarFunction::Arithmetic { op: ArithmeticOp::Sqrt });
599        // Standard SQL aliases
600        self.register_scalar(
601            "pow",
602            ScalarFunction::Arithmetic {
603                op: ArithmeticOp::Power,
604            },
605        );
606        self.register_scalar("log10", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
607        // Math aliases
608        self.register_scalar("cbrt", ScalarFunction::Arithmetic { op: ArithmeticOp::Cbrt });
609        self.register_scalar("cot", ScalarFunction::Arithmetic { op: ArithmeticOp::Cot });
610        self.register_scalar("log", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
611        self.register_scalar("ln", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
612        self.register_scalar("log2", ScalarFunction::Arithmetic { op: ArithmeticOp::Log2 });
613        self.register_scalar("even", ScalarFunction::Arithmetic { op: ArithmeticOp::Even });
614        self.register_scalar(
615            "factorial",
616            ScalarFunction::Arithmetic {
617                op: ArithmeticOp::Factorial,
618            },
619        );
620        self.register_scalar(
621            "gamma",
622            ScalarFunction::Arithmetic {
623                op: ArithmeticOp::Gamma,
624            },
625        );
626        self.register_scalar(
627            "lgamma",
628            ScalarFunction::Arithmetic {
629                op: ArithmeticOp::Lgamma,
630            },
631        );
632        self.register_scalar(
633            "set_seed",
634            ScalarFunction::Arithmetic {
635                op: ArithmeticOp::SetSeed,
636            },
637        );
638        self.register_scalar("exp", ScalarFunction::Arithmetic { op: ArithmeticOp::Exp });
639        self.register_scalar("sin", ScalarFunction::Arithmetic { op: ArithmeticOp::Sin });
640        self.register_scalar("cos", ScalarFunction::Arithmetic { op: ArithmeticOp::Cos });
641        self.register_scalar("tan", ScalarFunction::Arithmetic { op: ArithmeticOp::Tan });
642        self.register_scalar("asin", ScalarFunction::Arithmetic { op: ArithmeticOp::Asin });
643        self.register_scalar("acos", ScalarFunction::Arithmetic { op: ArithmeticOp::Acos });
644        self.register_scalar("atan", ScalarFunction::Arithmetic { op: ArithmeticOp::Atan });
645        self.register_scalar(
646            "atan2",
647            ScalarFunction::Arithmetic {
648                op: ArithmeticOp::Atan2,
649            },
650        );
651        self.register_scalar(
652            "degrees",
653            ScalarFunction::Arithmetic {
654                op: ArithmeticOp::Degrees,
655            },
656        );
657        self.register_scalar(
658            "radians",
659            ScalarFunction::Arithmetic {
660                op: ArithmeticOp::Radians,
661            },
662        );
663        self.register_scalar("sign", ScalarFunction::Arithmetic { op: ArithmeticOp::Sign });
664        self.register_scalar("pi", ScalarFunction::Arithmetic { op: ArithmeticOp::Pi });
665        self.register_scalar("rand", ScalarFunction::Arithmetic { op: ArithmeticOp::Rand });
666
667        self.register_scalar("sinh", ScalarFunction::Arithmetic { op: ArithmeticOp::Sinh });
668        self.register_scalar("cosh", ScalarFunction::Arithmetic { op: ArithmeticOp::Cosh });
669        self.register_scalar("tanh", ScalarFunction::Arithmetic { op: ArithmeticOp::Tanh });
670        self.register_scalar("gcd", ScalarFunction::Arithmetic { op: ArithmeticOp::Gcd });
671        self.register_scalar("lcm", ScalarFunction::Arithmetic { op: ArithmeticOp::Lcm });
672
673        // --- Bitwise ---
674        self.register_scalar(
675            "bitwise_and",
676            ScalarFunction::Arithmetic {
677                op: ArithmeticOp::BitwiseAnd,
678            },
679        );
680        self.register_scalar(
681            "&",
682            ScalarFunction::Arithmetic {
683                op: ArithmeticOp::BitwiseAnd,
684            },
685        );
686        self.register_scalar(
687            "bitwise_or",
688            ScalarFunction::Arithmetic {
689                op: ArithmeticOp::BitwiseOr,
690            },
691        );
692        self.register_scalar(
693            "|",
694            ScalarFunction::Arithmetic {
695                op: ArithmeticOp::BitwiseOr,
696            },
697        );
698        self.register_scalar(
699            "bitwise_xor",
700            ScalarFunction::Arithmetic {
701                op: ArithmeticOp::BitwiseXor,
702            },
703        );
704        self.register_scalar(
705            "#",
706            ScalarFunction::Arithmetic {
707                op: ArithmeticOp::BitwiseXor,
708            },
709        );
710        self.register_scalar(
711            "bit_shift_left",
712            ScalarFunction::Arithmetic {
713                op: ArithmeticOp::BitShiftLeft,
714            },
715        );
716        self.register_scalar(
717            "<<",
718            ScalarFunction::Arithmetic {
719                op: ArithmeticOp::BitShiftLeft,
720            },
721        );
722        self.register_scalar(
723            "bit_shift_right",
724            ScalarFunction::Arithmetic {
725                op: ArithmeticOp::BitShiftRight,
726            },
727        );
728        self.register_scalar(
729            ">>",
730            ScalarFunction::Arithmetic {
731                op: ArithmeticOp::BitShiftRight,
732            },
733        );
734
735        // --- Comparison ---
736        self.register_scalar("=", ScalarFunction::Comparison { op: ComparisonOp::Eq });
737        self.register_scalar(
738            "<>",
739            ScalarFunction::Comparison {
740                op: ComparisonOp::NotEq,
741            },
742        );
743        self.register_scalar("<", ScalarFunction::Comparison { op: ComparisonOp::Lt });
744        self.register_scalar("<=", ScalarFunction::Comparison { op: ComparisonOp::Lte });
745        self.register_scalar(">", ScalarFunction::Comparison { op: ComparisonOp::Gt });
746        self.register_scalar(">=", ScalarFunction::Comparison { op: ComparisonOp::Gte });
747        self.register_scalar(
748            "IS NULL",
749            ScalarFunction::Comparison {
750                op: ComparisonOp::IsNull,
751            },
752        );
753        self.register_scalar(
754            "IS NOT NULL",
755            ScalarFunction::Comparison {
756                op: ComparisonOp::IsNotNull,
757            },
758        );
759
760        // --- String ---
761        self.register_scalar("concat", ScalarFunction::String { op: StringOp::Concat });
762        self.register_scalar("contains", ScalarFunction::String { op: StringOp::Contains });
763        self.register_scalar(
764            "starts_with",
765            ScalarFunction::String {
766                op: StringOp::StartsWith,
767            },
768        );
769        self.register_scalar("ends_with", ScalarFunction::String { op: StringOp::EndsWith });
770        // C++ function aliases
771        self.register_scalar(
772            "prefix",
773            ScalarFunction::String {
774                op: StringOp::StartsWith,
775            },
776        );
777        self.register_scalar("suffix", ScalarFunction::String { op: StringOp::EndsWith });
778        self.register_scalar("like", ScalarFunction::String { op: StringOp::Like });
779        self.register_scalar("to_upper", ScalarFunction::String { op: StringOp::ToUpper });
780        self.register_scalar("to_lower", ScalarFunction::String { op: StringOp::ToLower });
781        // Standard SQL aliases
782        self.register_scalar("upper", ScalarFunction::String { op: StringOp::ToUpper });
783        self.register_scalar("lower", ScalarFunction::String { op: StringOp::ToLower });
784        self.register_scalar("ucase", ScalarFunction::String { op: StringOp::ToUpper });
785        self.register_scalar("lcase", ScalarFunction::String { op: StringOp::ToLower });
786        self.register_scalar("trim", ScalarFunction::String { op: StringOp::Trim });
787        self.register_scalar("ltrim", ScalarFunction::String { op: StringOp::LTrim });
788        self.register_scalar("rtrim", ScalarFunction::String { op: StringOp::RTrim });
789        self.register_scalar("length", ScalarFunction::String { op: StringOp::Length });
790        self.register_scalar("reverse", ScalarFunction::String { op: StringOp::Reverse });
791        self.register_scalar("repeat", ScalarFunction::String { op: StringOp::Repeat });
792        self.register_scalar("replace", ScalarFunction::String { op: StringOp::Replace });
793        self.register_scalar(
794            "substring",
795            ScalarFunction::String {
796                op: StringOp::Substring,
797            },
798        );
799        self.register_scalar(
800            "regex_matches",
801            ScalarFunction::String {
802                op: StringOp::RegexMatches,
803            },
804        );
805        self.register_scalar(
806            "regex_replace",
807            ScalarFunction::String {
808                op: StringOp::RegexReplace,
809            },
810        );
811        self.register_scalar("split", ScalarFunction::String { op: StringOp::Split });
812        self.register_scalar("head", ScalarFunction::String { op: StringOp::Head });
813        self.register_scalar("tail", ScalarFunction::String { op: StringOp::Tail });
814        self.register_scalar("left", ScalarFunction::String { op: StringOp::Left });
815        self.register_scalar("right", ScalarFunction::String { op: StringOp::Right });
816        self.register_scalar("lpad", ScalarFunction::String { op: StringOp::Lpad });
817        self.register_scalar("rpad", ScalarFunction::String { op: StringOp::Rpad });
818
819        // --- String basic (C++ port) ---
820        self.register_scalar("initcap", ScalarFunction::String { op: StringOp::InitCap });
821        self.register_scalar("concat_ws", ScalarFunction::String { op: StringOp::ConcatWs });
822        self.register_scalar(
823            "split_part",
824            ScalarFunction::String {
825                op: StringOp::SplitPart,
826            },
827        );
828        self.register_scalar(
829            "array_extract",
830            ScalarFunction::String {
831                op: StringOp::ArrayExtract,
832            },
833        );
834
835        // --- Regex string functions ---
836        self.register_scalar(
837            "regexp_full_match",
838            ScalarFunction::String {
839                op: StringOp::RegexpFullMatch,
840            },
841        );
842        self.register_scalar(
843            "regexp_extract",
844            ScalarFunction::String {
845                op: StringOp::RegexpExtract,
846            },
847        );
848        self.register_scalar(
849            "regexp_extract_all",
850            ScalarFunction::String {
851                op: StringOp::RegexpExtractAll,
852            },
853        );
854        self.register_scalar(
855            "regexp_split_to_array",
856            ScalarFunction::String {
857                op: StringOp::RegexpSplitToArray,
858            },
859        );
860        self.register_scalar(
861            "levenshtein",
862            ScalarFunction::String {
863                op: StringOp::Levenshtein,
864            },
865        );
866        self.register_scalar("soundex", ScalarFunction::String { op: StringOp::Soundex });
867
868        // --- Hash functions ---
869        self.register_scalar("md5", ScalarFunction::Hash { op: HashOp::Md5 });
870        self.register_scalar("sha256", ScalarFunction::Hash { op: HashOp::Sha256 });
871        self.register_scalar("hash", ScalarFunction::Hash { op: HashOp::Hash });
872
873        // --- Interval ---
874        self.register_scalar(
875            "to_years",
876            ScalarFunction::Interval {
877                op: IntervalOp::ToYears,
878            },
879        );
880        self.register_scalar(
881            "to_months",
882            ScalarFunction::Interval {
883                op: IntervalOp::ToMonths,
884            },
885        );
886        self.register_scalar("to_days", ScalarFunction::Interval { op: IntervalOp::ToDays });
887        self.register_scalar(
888            "to_hours",
889            ScalarFunction::Interval {
890                op: IntervalOp::ToHours,
891            },
892        );
893        self.register_scalar(
894            "to_minutes",
895            ScalarFunction::Interval {
896                op: IntervalOp::ToMinutes,
897            },
898        );
899        self.register_scalar(
900            "to_seconds",
901            ScalarFunction::Interval {
902                op: IntervalOp::ToSeconds,
903            },
904        );
905        self.register_scalar(
906            "to_milliseconds",
907            ScalarFunction::Interval {
908                op: IntervalOp::ToMilliseconds,
909            },
910        );
911        self.register_scalar(
912            "to_microseconds",
913            ScalarFunction::Interval {
914                op: IntervalOp::ToMicroseconds,
915            },
916        );
917
918        // --- Date/Time ---
919        self.register_scalar("date_part", ScalarFunction::Date { op: DateOp::DatePart });
920        self.register_scalar("date_trunc", ScalarFunction::Date { op: DateOp::DateTrunc });
921        self.register_scalar("date_diff", ScalarFunction::Date { op: DateOp::DateDiff });
922        self.register_scalar("date_add", ScalarFunction::Date { op: DateOp::DateAdd });
923        self.register_scalar(
924            "current_date",
925            ScalarFunction::Date {
926                op: DateOp::CurrentDate,
927            },
928        );
929        self.register_scalar(
930            "current_timestamp",
931            ScalarFunction::Date {
932                op: DateOp::CurrentTimestamp,
933            },
934        );
935        self.register_scalar("year", ScalarFunction::Date { op: DateOp::Year });
936        self.register_scalar("month", ScalarFunction::Date { op: DateOp::Month });
937
938        // Sequence functions (require catalog access at connection level)
939        self.register_scalar("nextval", ScalarFunction::SequenceOp { is_nextval: true });
940        self.register_scalar("currval", ScalarFunction::SequenceOp { is_nextval: false });
941        self.register_scalar("day", ScalarFunction::Date { op: DateOp::Day });
942        self.register_scalar("hour", ScalarFunction::Date { op: DateOp::Hour });
943        self.register_scalar("minute", ScalarFunction::Date { op: DateOp::Minute });
944        self.register_scalar("second", ScalarFunction::Date { op: DateOp::Second });
945        self.register_scalar("dayname", ScalarFunction::Date { op: DateOp::DayName });
946        self.register_scalar("monthname", ScalarFunction::Date { op: DateOp::MonthName });
947        self.register_scalar("last_day", ScalarFunction::Date { op: DateOp::LastDay });
948        self.register_scalar("make_date", ScalarFunction::Date { op: DateOp::MakeDate });
949        self.register_scalar("century", ScalarFunction::Date { op: DateOp::Century });
950        self.register_scalar("epoch_ms", ScalarFunction::Date { op: DateOp::EpochMs });
951        self.register_scalar(
952            "to_timestamp",
953            ScalarFunction::Date {
954                op: DateOp::ToTimestamp,
955            },
956        );
957        self.register_scalar("to_epoch_ms", ScalarFunction::Date { op: DateOp::ToEpochMs });
958
959        // --- Cast ---
960        self.register_scalar(
961            "CAST",
962            ScalarFunction::Cast {
963                target_type: CastTarget::String,
964            },
965        );
966        self.register_scalar(
967            "cast_string",
968            ScalarFunction::Cast {
969                target_type: CastTarget::String,
970            },
971        );
972        self.register_scalar(
973            "cast_int64",
974            ScalarFunction::Cast {
975                target_type: CastTarget::Int64,
976            },
977        );
978        self.register_scalar(
979            "cast_double",
980            ScalarFunction::Cast {
981                target_type: CastTarget::Double,
982            },
983        );
984        self.register_scalar(
985            "cast_bool",
986            ScalarFunction::Cast {
987                target_type: CastTarget::Bool,
988            },
989        );
990        // Cast function name aliases (Cypher/SQL standard)
991        self.register_scalar(
992            "date",
993            ScalarFunction::Cast {
994                target_type: CastTarget::Date,
995            },
996        );
997        self.register_scalar(
998            "timestamp",
999            ScalarFunction::Cast {
1000                target_type: CastTarget::Timestamp,
1001            },
1002        );
1003        self.register_scalar(
1004            "float",
1005            ScalarFunction::Cast {
1006                target_type: CastTarget::Double,
1007            },
1008        );
1009        self.register_scalar(
1010            "double",
1011            ScalarFunction::Cast {
1012                target_type: CastTarget::Double,
1013            },
1014        );
1015        self.register_scalar(
1016            "int64",
1017            ScalarFunction::Cast {
1018                target_type: CastTarget::Int64,
1019            },
1020        );
1021        self.register_scalar(
1022            "int",
1023            ScalarFunction::Cast {
1024                target_type: CastTarget::Int64,
1025            },
1026        );
1027        self.register_scalar(
1028            "bool",
1029            ScalarFunction::Cast {
1030                target_type: CastTarget::Bool,
1031            },
1032        );
1033        self.register_scalar(
1034            "boolean",
1035            ScalarFunction::Cast {
1036                target_type: CastTarget::Bool,
1037            },
1038        );
1039        self.register_scalar(
1040            "string",
1041            ScalarFunction::Cast {
1042                target_type: CastTarget::String,
1043            },
1044        );
1045        self.register_scalar(
1046            "blob",
1047            ScalarFunction::Cast {
1048                target_type: CastTarget::String,
1049            },
1050        );
1051
1052        // --- Blob ---
1053        self.register_scalar("encode", ScalarFunction::Blob { op: BlobOp::Encode });
1054        self.register_scalar("decode", ScalarFunction::Blob { op: BlobOp::Decode });
1055        self.register_scalar(
1056            "octet_length",
1057            ScalarFunction::Blob {
1058                op: BlobOp::OctetLength,
1059            },
1060        );
1061
1062        // --- List ---
1063        self.register_scalar("list_creation", ScalarFunction::List { op: ListOp::Creation });
1064        self.register_scalar("list_extract", ScalarFunction::List { op: ListOp::Extract });
1065        self.register_scalar("list_concat", ScalarFunction::List { op: ListOp::Concat });
1066        self.register_scalar("list_cat", ScalarFunction::List { op: ListOp::Concat });
1067        self.register_scalar("list_len", ScalarFunction::List { op: ListOp::Len });
1068        self.register_scalar("list_sort", ScalarFunction::List { op: ListOp::Sort });
1069        self.register_scalar("list_reverse", ScalarFunction::List { op: ListOp::Reverse });
1070        self.register_scalar("list_contains", ScalarFunction::List { op: ListOp::Contains });
1071        self.register_scalar("list_append", ScalarFunction::List { op: ListOp::Append });
1072        self.register_scalar("list_prepend", ScalarFunction::List { op: ListOp::Prepend });
1073        self.register_scalar("list_slice", ScalarFunction::List { op: ListOp::Slice });
1074
1075        // --- List functions (C++ port) ---
1076        self.register_scalar("range", ScalarFunction::List { op: ListOp::Range });
1077        self.register_scalar("list_distinct", ScalarFunction::List { op: ListOp::Distinct });
1078        self.register_scalar("list_unique", ScalarFunction::List { op: ListOp::Unique });
1079        self.register_scalar("list_sum", ScalarFunction::List { op: ListOp::Sum });
1080        self.register_scalar("list_product", ScalarFunction::List { op: ListOp::Product });
1081        self.register_scalar("list_any_value", ScalarFunction::List { op: ListOp::AnyValue });
1082        self.register_scalar("list_to_string", ScalarFunction::List { op: ListOp::ToString });
1083        self.register_scalar("list_position", ScalarFunction::List { op: ListOp::Position });
1084        self.register_scalar("list_indexof", ScalarFunction::List { op: ListOp::Position });
1085        self.register_scalar("list_has_all", ScalarFunction::List { op: ListOp::HasAll });
1086        self.register_scalar("list_has_any", ScalarFunction::List { op: ListOp::HasAny });
1087        self.register_scalar("list_count", ScalarFunction::List { op: ListOp::Count });
1088        self.register_scalar("list_min", ScalarFunction::List { op: ListOp::Min });
1089        self.register_scalar("list_max", ScalarFunction::List { op: ListOp::Max });
1090        self.register_scalar(
1091            "list_reverse_sort",
1092            ScalarFunction::List {
1093                op: ListOp::ReverseSort,
1094            },
1095        );
1096
1097        // --- Lambda-based list functions (evaluated by expression evaluator) ---
1098        self.register_scalar("list_transform", ScalarFunction::List { op: ListOp::Transform });
1099        self.register_scalar("list_filter", ScalarFunction::List { op: ListOp::Filter });
1100        self.register_scalar("list_reduce", ScalarFunction::List { op: ListOp::Reduce });
1101
1102        // --- List predicate functions ---
1103        self.register_scalar("any", ScalarFunction::List { op: ListOp::Any });
1104        self.register_scalar("all", ScalarFunction::List { op: ListOp::All });
1105        self.register_scalar("none", ScalarFunction::List { op: ListOp::None });
1106        self.register_scalar("single", ScalarFunction::List { op: ListOp::Single });
1107
1108        // --- Map ---
1109        self.register_scalar("map_creation", ScalarFunction::Map { op: MapOp::Creation });
1110        self.register_scalar("map_extract", ScalarFunction::Map { op: MapOp::Extract });
1111        self.register_scalar("element_at", ScalarFunction::Map { op: MapOp::Extract });
1112        self.register_scalar("map_keys", ScalarFunction::Map { op: MapOp::Keys });
1113        self.register_scalar("map_values", ScalarFunction::Map { op: MapOp::Values });
1114
1115        // --- Struct ---
1116        self.register_scalar("struct_creation", ScalarFunction::Struct { op: StructOp::Creation });
1117        self.register_scalar("struct_extract", ScalarFunction::Struct { op: StructOp::Extract });
1118
1119        // --- Union ---
1120        self.register_scalar(
1121            "union_value",
1122            ScalarFunction::Union {
1123                op: UnionOp::UnionValue,
1124            },
1125        );
1126        self.register_scalar(
1127            "union_extract",
1128            ScalarFunction::Union {
1129                op: UnionOp::UnionExtract,
1130            },
1131        );
1132        self.register_scalar("union_tag", ScalarFunction::Union { op: UnionOp::UnionTag });
1133
1134        // --- Boolean ---
1135        self.register_scalar("AND", ScalarFunction::Boolean { op: BooleanOp::And });
1136        self.register_scalar("OR", ScalarFunction::Boolean { op: BooleanOp::Or });
1137        self.register_scalar("XOR", ScalarFunction::Boolean { op: BooleanOp::Xor });
1138        self.register_scalar("NOT", ScalarFunction::Boolean { op: BooleanOp::Not });
1139
1140        // --- Utility ---
1141        self.register_scalar(
1142            "coalesce",
1143            ScalarFunction::Utility {
1144                op: UtilityOp::Coalesce,
1145            },
1146        );
1147        self.register_scalar("ifnull", ScalarFunction::Utility { op: UtilityOp::IfNull });
1148        self.register_scalar("nullif", ScalarFunction::Utility { op: UtilityOp::NullIf });
1149        self.register_scalar("size", ScalarFunction::Utility { op: UtilityOp::Size });
1150        self.register_scalar("cardinality", ScalarFunction::Utility { op: UtilityOp::Size });
1151        self.register_scalar("typeof", ScalarFunction::Utility { op: UtilityOp::TypeOf });
1152        self.register_scalar("error", ScalarFunction::Utility { op: UtilityOp::Error });
1153        self.register_scalar(
1154            "pg_isready",
1155            ScalarFunction::Utility {
1156                op: UtilityOp::PgIsReady,
1157            },
1158        );
1159
1160        // --- Greatest/Least (SQL extremum functions) ---
1161        self.register_scalar(
1162            "greatest",
1163            ScalarFunction::Utility {
1164                op: UtilityOp::Greatest,
1165            },
1166        );
1167        self.register_scalar("least", ScalarFunction::Utility { op: UtilityOp::Least });
1168        self.register_scalar(
1169            "constant_or_null",
1170            ScalarFunction::Utility {
1171                op: UtilityOp::ConstantOrNull,
1172            },
1173        );
1174
1175        // --- Schema ---
1176        self.register_scalar("OFFSET", ScalarFunction::Schema { op: SchemaOp::Offset });
1177        self.register_scalar("ID", ScalarFunction::Schema { op: SchemaOp::Id });
1178        self.register_scalar(
1179            "START_NODE",
1180            ScalarFunction::Schema {
1181                op: SchemaOp::StartNode,
1182            },
1183        );
1184        self.register_scalar("END_NODE", ScalarFunction::Schema { op: SchemaOp::EndNode });
1185        self.register_scalar("LABEL", ScalarFunction::Schema { op: SchemaOp::Label });
1186        self.register_scalar("COST", ScalarFunction::Schema { op: SchemaOp::Cost });
1187        self.register_scalar("ROWID", ScalarFunction::Schema { op: SchemaOp::RowId });
1188
1189        // --- Array ---
1190        self.register_scalar(
1191            "array_cosine_similarity",
1192            ScalarFunction::Array {
1193                op: ArrayOp::CosineSimilarity,
1194            },
1195        );
1196        self.register_scalar("array_distance", ScalarFunction::Array { op: ArrayOp::Distance });
1197        self.register_scalar(
1198            "array_inner_product",
1199            ScalarFunction::Array {
1200                op: ArrayOp::InnerProduct,
1201            },
1202        );
1203        self.register_scalar(
1204            "array_dot_product",
1205            ScalarFunction::Array {
1206                op: ArrayOp::DotProduct,
1207            },
1208        );
1209        self.register_scalar(
1210            "array_cross_product",
1211            ScalarFunction::Array {
1212                op: ArrayOp::CrossProduct,
1213            },
1214        );
1215        self.register_scalar(
1216            "array_squared_distance",
1217            ScalarFunction::Array {
1218                op: ArrayOp::SquaredDistance,
1219            },
1220        );
1221        self.register_scalar("array_intersect", ScalarFunction::Array { op: ArrayOp::Intersect });
1222
1223        // --- Path ---
1224        self.register_scalar("nodes", ScalarFunction::Path { op: PathOp::Nodes });
1225        self.register_scalar("rels", ScalarFunction::Path { op: PathOp::Rels });
1226        self.register_scalar("relationships", ScalarFunction::Path { op: PathOp::Rels });
1227        self.register_scalar("properties", ScalarFunction::Path { op: PathOp::Properties });
1228        self.register_scalar("is_trail", ScalarFunction::Path { op: PathOp::IsTrail });
1229        self.register_scalar("is_acyclic", ScalarFunction::Path { op: PathOp::IsAcyclic });
1230
1231        // --- UUID ---
1232        self.register_scalar("gen_random_uuid", ScalarFunction::Uuid);
1233
1234        // --- Map ---
1235        self.register_scalar(
1236            "map_from_entries",
1237            ScalarFunction::Map {
1238                op: MapOp::MapFromEntries,
1239            },
1240        );
1241
1242        // --- Blob ---
1243        self.register_scalar(
1244            "blob_from_bytes",
1245            ScalarFunction::Blob {
1246                op: BlobOp::BlobFromBytes,
1247            },
1248        );
1249        self.register_scalar("to_base64", ScalarFunction::Blob { op: BlobOp::ToBase64 });
1250        self.register_scalar("from_base64", ScalarFunction::Blob { op: BlobOp::FromBase64 });
1251
1252        // --- Array utility aliases (delegate to list functions) ---
1253        self.register_scalar("array_concat", ScalarFunction::List { op: ListOp::Concat });
1254        self.register_scalar("array_cat", ScalarFunction::List { op: ListOp::Concat });
1255        self.register_scalar("array_append", ScalarFunction::List { op: ListOp::Append });
1256        self.register_scalar("array_push_back", ScalarFunction::List { op: ListOp::Append });
1257        self.register_scalar("array_prepend", ScalarFunction::List { op: ListOp::Prepend });
1258        self.register_scalar("array_push_front", ScalarFunction::List { op: ListOp::Prepend });
1259        self.register_scalar("array_contains", ScalarFunction::List { op: ListOp::Contains });
1260        self.register_scalar("array_has", ScalarFunction::List { op: ListOp::Contains });
1261        self.register_scalar("array_slice", ScalarFunction::List { op: ListOp::Slice });
1262        self.register_scalar("array_value", ScalarFunction::List { op: ListOp::Creation });
1263
1264        // --- Aggregate ---
1265        self.register_aggregate("COUNT", AggregateFunction::Count);
1266        self.register_aggregate("COUNT(*)", AggregateFunction::CountStar);
1267        self.register_aggregate("COUNT_IF", AggregateFunction::CountIf);
1268        self.register_aggregate("SUM", AggregateFunction::Sum);
1269        self.register_aggregate("AVG", AggregateFunction::Avg);
1270        self.register_aggregate("MIN", AggregateFunction::Min);
1271        self.register_aggregate("MAX", AggregateFunction::Max);
1272        self.register_aggregate("COLLECT", AggregateFunction::Collect);
1273        self.register_aggregate("STDDEV", AggregateFunction::StdDev);
1274        self.register_aggregate("VARIANCE", AggregateFunction::Variance);
1275        self.register_aggregate(
1276            "STRING_AGG",
1277            AggregateFunction::StringAgg {
1278                delimiter: ",".to_string(),
1279            },
1280        );
1281        self.register_aggregate(
1282            "GROUP_CONCAT",
1283            AggregateFunction::StringAgg {
1284                delimiter: ",".to_string(),
1285            },
1286        );
1287        self.register_aggregate("PERCENTILE_DISC", AggregateFunction::PercentileDisc { percentile: 0.5 });
1288        self.register_aggregate("PERCENTILE_CONT", AggregateFunction::PercentileCont { percentile: 0.5 });
1289
1290        // --- Table ---
1291        self.register_table("list_tables", TableFunction::ListTables);
1292    }
1293
1294    // --- Registration ---
1295
1296    pub fn register_scalar(&mut self, name: &str, func: ScalarFunction) {
1297        self.scalar_functions.insert(name.to_lowercase(), func);
1298    }
1299
1300    pub fn register_aggregate(&mut self, name: &str, func: AggregateFunction) {
1301        self.aggregate_functions.insert(name.to_lowercase(), func);
1302    }
1303
1304    pub fn register_table(&mut self, name: &str, func: TableFunction) {
1305        self.table_functions.insert(name.to_lowercase(), func);
1306    }
1307
1308    // --- Lookup ---
1309
1310    pub fn resolve(&self, name: &str) -> Option<ResolvedFunction> {
1311        let lower = name.to_lowercase();
1312        if let Some(f) = self.scalar_functions.get(&lower) {
1313            return Some(ResolvedFunction::Scalar(f.clone()));
1314        }
1315        if let Some(f) = self.aggregate_functions.get(&lower) {
1316            return Some(ResolvedFunction::Aggregate(f.clone()));
1317        }
1318        if let Some(f) = self.table_functions.get(&lower) {
1319            return Some(ResolvedFunction::Table(f.clone()));
1320        }
1321        None
1322    }
1323
1324    pub fn get_scalar(&self, name: &str) -> Option<&ScalarFunction> {
1325        self.scalar_functions.get(&name.to_lowercase())
1326    }
1327
1328    pub fn get_aggregate(&self, name: &str) -> Option<&AggregateFunction> {
1329        self.aggregate_functions.get(&name.to_lowercase())
1330    }
1331
1332    pub fn get_table(&self, name: &str) -> Option<&TableFunction> {
1333        self.table_functions.get(&name.to_lowercase())
1334    }
1335
1336    /// List all registered functions as (name, kind) pairs.
1337    /// Kind is one of: "SCALAR", "AGGREGATE", "TABLE".
1338    pub fn list_all(&self) -> Vec<(String, String)> {
1339        let mut result = Vec::new();
1340        for name in self.scalar_functions.keys() {
1341            result.push((name.clone(), "SCALAR".to_string()));
1342        }
1343        for name in self.aggregate_functions.keys() {
1344            result.push((name.clone(), "AGGREGATE".to_string()));
1345        }
1346        for name in self.table_functions.keys() {
1347            result.push((name.clone(), "TABLE".to_string()));
1348        }
1349        result.sort_by(|a, b| a.0.cmp(&b.0));
1350        result
1351    }
1352
1353    pub fn contains(&self, name: &str) -> bool {
1354        let lower = name.to_lowercase();
1355        self.scalar_functions.contains_key(&lower)
1356            || self.aggregate_functions.contains_key(&lower)
1357            || self.table_functions.contains_key(&lower)
1358    }
1359
1360    /// Number of registered scalar functions.
1361    pub fn scalar_count(&self) -> usize {
1362        self.scalar_functions.len()
1363    }
1364
1365    /// Number of registered aggregate functions.
1366    pub fn aggregate_count(&self) -> usize {
1367        self.aggregate_functions.len()
1368    }
1369
1370    /// Number of registered table functions.
1371    pub fn table_count(&self) -> usize {
1372        self.table_functions.len()
1373    }
1374
1375    /// Total number of registered functions.
1376    pub fn total_count(&self) -> usize {
1377        self.scalar_count() + self.aggregate_count() + self.table_count()
1378    }
1379
1380    /// Execute a table function by name with the given pre-evaluated arguments.
1381    ///
1382    /// Returns a `Vec<Vec<Value>>` representing rows of results.
1383    /// Each inner vec is one row with one or more column values.
1384    pub fn execute_table_function(&self, name: &str, args: &[Value]) -> Result<Vec<Vec<Value>>, String> {
1385        use akar_common::vector::DataChunk;
1386
1387        let func = self
1388            .get_table(name)
1389            .ok_or_else(|| format!("Table function '{}' not found", name))?;
1390
1391        match func {
1392            TableFunction::ListTables => Err("ListTables requires catalog access — handled at connection level".into()),
1393            TableFunction::ShowColumns { .. } => {
1394                Err("ShowColumns requires catalog access — handled at connection level".into())
1395            }
1396            TableFunction::Custom { name: custom_name } => Err(format!(
1397                "Table function '{}' requires an extension or external context to be loaded. \
1398                 Use LOAD EXTENSION or CALL with the appropriate handler.",
1399                custom_name
1400            )),
1401            TableFunction::CustomTable { name: _, execute } => {
1402                let mut chunk = DataChunk {
1403                    fields: Vec::new(),
1404                    field_types: Vec::new(),
1405                    size: 0,
1406                    field_names: vec![],
1407                    sel_vector: None,
1408                };
1409                execute(args, &mut chunk).map(|_| {
1410                    let mut rows = Vec::new();
1411                    for row in 0..chunk.size {
1412                        let mut row_vals = Vec::new();
1413                        for field_idx in 0..chunk.fields.len() {
1414                            row_vals.push(chunk.get_value(field_idx, row).unwrap_or(Value::Null));
1415                        }
1416                        rows.push(row_vals);
1417                    }
1418                    rows
1419                })
1420            }
1421            TableFunction::ScanCsv { .. }
1422            | TableFunction::ScanParquet { .. }
1423            | TableFunction::ScanJson { .. }
1424            | TableFunction::CurrentSetting { .. } => Err(format!(
1425                "Table function '{}' cannot be executed via CALL — it requires file path or catalog context. \
1426                 Use COPY FROM 'file' FORMAT CSV/PARQUET/JSON or CALL current_setting('key') via the connection layer.",
1427                name
1428            )),
1429        }
1430    }
1431}