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    /// Extension-provided table function with graph access.
528    /// The closure receives input args, an optional graph data source
529    /// (present when the caller owns a storage catalog), and fills a
530    /// mutable DataChunk.
531    #[allow(clippy::type_complexity)]
532    CustomTableWithGraph {
533        name: String,
534        execute: Arc<
535            dyn Fn(&[Value], Option<&dyn crate::graph::GraphDataSource>, &mut DataChunk) -> Result<(), String>
536                + Send
537                + Sync,
538        >,
539    },
540}
541
542impl std::fmt::Debug for TableFunction {
543    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544        match self {
545            Self::ScanCsv { path } => f.debug_struct("ScanCsv").field("path", path).finish(),
546            Self::ScanParquet { path } => f.debug_struct("ScanParquet").field("path", path).finish(),
547            Self::ScanJson { path } => f.debug_struct("ScanJson").field("path", path).finish(),
548            Self::ListTables => write!(f, "ListTables"),
549            Self::ShowColumns { table_name } => f.debug_struct("ShowColumns").field("table_name", table_name).finish(),
550            Self::CurrentSetting { key } => f.debug_struct("CurrentSetting").field("key", key).finish(),
551            Self::Custom { name } => f.debug_struct("Custom").field("name", name).finish(),
552            Self::CustomTable { name, .. } => f.debug_struct("CustomTable").field("name", name).finish(),
553            Self::CustomTableWithGraph { name, .. } => {
554                f.debug_struct("CustomTableWithGraph").field("name", name).finish()
555            }
556        }
557    }
558}
559
560/// A resolved function with its variant.
561#[derive(Debug, Clone)]
562pub enum ResolvedFunction {
563    Scalar(ScalarFunction),
564    Aggregate(AggregateFunction),
565    Table(TableFunction),
566}
567
568// ==================== Registry ====================
569
570/// Registry of all built-in functions (scalar, aggregate, table).
571#[derive(Default)]
572pub struct FunctionRegistry {
573    scalar_functions: HashMap<String, ScalarFunction>,
574    aggregate_functions: HashMap<String, AggregateFunction>,
575    table_functions: HashMap<String, TableFunction>,
576}
577
578impl FunctionRegistry {
579    pub fn new() -> Self {
580        let mut reg = Self::default();
581        reg.register_builtins();
582        reg
583    }
584
585    fn register_builtins(&mut self) {
586        // --- Arithmetic ---
587        self.register_scalar("+", ScalarFunction::Arithmetic { op: ArithmeticOp::Add });
588        self.register_scalar("-", ScalarFunction::Arithmetic { op: ArithmeticOp::Sub });
589        self.register_scalar("*", ScalarFunction::Arithmetic { op: ArithmeticOp::Mul });
590        self.register_scalar("/", ScalarFunction::Arithmetic { op: ArithmeticOp::Div });
591        self.register_scalar("%", ScalarFunction::Arithmetic { op: ArithmeticOp::Mod });
592        self.register_scalar("abs", ScalarFunction::Arithmetic { op: ArithmeticOp::Abs });
593        self.register_scalar("ceil", ScalarFunction::Arithmetic { op: ArithmeticOp::Ceil });
594        // Standard SQL alias
595        self.register_scalar("ceiling", ScalarFunction::Arithmetic { op: ArithmeticOp::Ceil });
596        self.register_scalar(
597            "floor",
598            ScalarFunction::Arithmetic {
599                op: ArithmeticOp::Floor,
600            },
601        );
602        self.register_scalar(
603            "round",
604            ScalarFunction::Arithmetic {
605                op: ArithmeticOp::Round,
606            },
607        );
608        self.register_scalar(
609            "^",
610            ScalarFunction::Arithmetic {
611                op: ArithmeticOp::Power,
612            },
613        );
614        self.register_scalar("sqrt", ScalarFunction::Arithmetic { op: ArithmeticOp::Sqrt });
615        // Standard SQL aliases
616        self.register_scalar(
617            "pow",
618            ScalarFunction::Arithmetic {
619                op: ArithmeticOp::Power,
620            },
621        );
622        self.register_scalar("log10", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
623        // Math aliases
624        self.register_scalar("cbrt", ScalarFunction::Arithmetic { op: ArithmeticOp::Cbrt });
625        self.register_scalar("cot", ScalarFunction::Arithmetic { op: ArithmeticOp::Cot });
626        self.register_scalar("log", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
627        self.register_scalar("ln", ScalarFunction::Arithmetic { op: ArithmeticOp::Log });
628        self.register_scalar("log2", ScalarFunction::Arithmetic { op: ArithmeticOp::Log2 });
629        self.register_scalar("even", ScalarFunction::Arithmetic { op: ArithmeticOp::Even });
630        self.register_scalar(
631            "factorial",
632            ScalarFunction::Arithmetic {
633                op: ArithmeticOp::Factorial,
634            },
635        );
636        self.register_scalar(
637            "gamma",
638            ScalarFunction::Arithmetic {
639                op: ArithmeticOp::Gamma,
640            },
641        );
642        self.register_scalar(
643            "lgamma",
644            ScalarFunction::Arithmetic {
645                op: ArithmeticOp::Lgamma,
646            },
647        );
648        self.register_scalar(
649            "set_seed",
650            ScalarFunction::Arithmetic {
651                op: ArithmeticOp::SetSeed,
652            },
653        );
654        self.register_scalar("exp", ScalarFunction::Arithmetic { op: ArithmeticOp::Exp });
655        self.register_scalar("sin", ScalarFunction::Arithmetic { op: ArithmeticOp::Sin });
656        self.register_scalar("cos", ScalarFunction::Arithmetic { op: ArithmeticOp::Cos });
657        self.register_scalar("tan", ScalarFunction::Arithmetic { op: ArithmeticOp::Tan });
658        self.register_scalar("asin", ScalarFunction::Arithmetic { op: ArithmeticOp::Asin });
659        self.register_scalar("acos", ScalarFunction::Arithmetic { op: ArithmeticOp::Acos });
660        self.register_scalar("atan", ScalarFunction::Arithmetic { op: ArithmeticOp::Atan });
661        self.register_scalar(
662            "atan2",
663            ScalarFunction::Arithmetic {
664                op: ArithmeticOp::Atan2,
665            },
666        );
667        self.register_scalar(
668            "degrees",
669            ScalarFunction::Arithmetic {
670                op: ArithmeticOp::Degrees,
671            },
672        );
673        self.register_scalar(
674            "radians",
675            ScalarFunction::Arithmetic {
676                op: ArithmeticOp::Radians,
677            },
678        );
679        self.register_scalar("sign", ScalarFunction::Arithmetic { op: ArithmeticOp::Sign });
680        self.register_scalar("pi", ScalarFunction::Arithmetic { op: ArithmeticOp::Pi });
681        self.register_scalar("rand", ScalarFunction::Arithmetic { op: ArithmeticOp::Rand });
682
683        self.register_scalar("sinh", ScalarFunction::Arithmetic { op: ArithmeticOp::Sinh });
684        self.register_scalar("cosh", ScalarFunction::Arithmetic { op: ArithmeticOp::Cosh });
685        self.register_scalar("tanh", ScalarFunction::Arithmetic { op: ArithmeticOp::Tanh });
686        self.register_scalar("gcd", ScalarFunction::Arithmetic { op: ArithmeticOp::Gcd });
687        self.register_scalar("lcm", ScalarFunction::Arithmetic { op: ArithmeticOp::Lcm });
688
689        // --- Bitwise ---
690        self.register_scalar(
691            "bitwise_and",
692            ScalarFunction::Arithmetic {
693                op: ArithmeticOp::BitwiseAnd,
694            },
695        );
696        self.register_scalar(
697            "&",
698            ScalarFunction::Arithmetic {
699                op: ArithmeticOp::BitwiseAnd,
700            },
701        );
702        self.register_scalar(
703            "bitwise_or",
704            ScalarFunction::Arithmetic {
705                op: ArithmeticOp::BitwiseOr,
706            },
707        );
708        self.register_scalar(
709            "|",
710            ScalarFunction::Arithmetic {
711                op: ArithmeticOp::BitwiseOr,
712            },
713        );
714        self.register_scalar(
715            "bitwise_xor",
716            ScalarFunction::Arithmetic {
717                op: ArithmeticOp::BitwiseXor,
718            },
719        );
720        self.register_scalar(
721            "#",
722            ScalarFunction::Arithmetic {
723                op: ArithmeticOp::BitwiseXor,
724            },
725        );
726        self.register_scalar(
727            "bit_shift_left",
728            ScalarFunction::Arithmetic {
729                op: ArithmeticOp::BitShiftLeft,
730            },
731        );
732        self.register_scalar(
733            "<<",
734            ScalarFunction::Arithmetic {
735                op: ArithmeticOp::BitShiftLeft,
736            },
737        );
738        self.register_scalar(
739            "bit_shift_right",
740            ScalarFunction::Arithmetic {
741                op: ArithmeticOp::BitShiftRight,
742            },
743        );
744        self.register_scalar(
745            ">>",
746            ScalarFunction::Arithmetic {
747                op: ArithmeticOp::BitShiftRight,
748            },
749        );
750
751        // --- Comparison ---
752        self.register_scalar("=", ScalarFunction::Comparison { op: ComparisonOp::Eq });
753        self.register_scalar(
754            "<>",
755            ScalarFunction::Comparison {
756                op: ComparisonOp::NotEq,
757            },
758        );
759        self.register_scalar("<", ScalarFunction::Comparison { op: ComparisonOp::Lt });
760        self.register_scalar("<=", ScalarFunction::Comparison { op: ComparisonOp::Lte });
761        self.register_scalar(">", ScalarFunction::Comparison { op: ComparisonOp::Gt });
762        self.register_scalar(">=", ScalarFunction::Comparison { op: ComparisonOp::Gte });
763        self.register_scalar(
764            "IS NULL",
765            ScalarFunction::Comparison {
766                op: ComparisonOp::IsNull,
767            },
768        );
769        self.register_scalar(
770            "IS NOT NULL",
771            ScalarFunction::Comparison {
772                op: ComparisonOp::IsNotNull,
773            },
774        );
775
776        // --- String ---
777        self.register_scalar("concat", ScalarFunction::String { op: StringOp::Concat });
778        self.register_scalar("contains", ScalarFunction::String { op: StringOp::Contains });
779        self.register_scalar(
780            "starts_with",
781            ScalarFunction::String {
782                op: StringOp::StartsWith,
783            },
784        );
785        self.register_scalar("ends_with", ScalarFunction::String { op: StringOp::EndsWith });
786        // C++ function aliases
787        self.register_scalar(
788            "prefix",
789            ScalarFunction::String {
790                op: StringOp::StartsWith,
791            },
792        );
793        self.register_scalar("suffix", ScalarFunction::String { op: StringOp::EndsWith });
794        self.register_scalar("like", ScalarFunction::String { op: StringOp::Like });
795        self.register_scalar("to_upper", ScalarFunction::String { op: StringOp::ToUpper });
796        self.register_scalar("to_lower", ScalarFunction::String { op: StringOp::ToLower });
797        // Standard SQL aliases
798        self.register_scalar("upper", ScalarFunction::String { op: StringOp::ToUpper });
799        self.register_scalar("lower", ScalarFunction::String { op: StringOp::ToLower });
800        self.register_scalar("ucase", ScalarFunction::String { op: StringOp::ToUpper });
801        self.register_scalar("lcase", ScalarFunction::String { op: StringOp::ToLower });
802        self.register_scalar("trim", ScalarFunction::String { op: StringOp::Trim });
803        self.register_scalar("ltrim", ScalarFunction::String { op: StringOp::LTrim });
804        self.register_scalar("rtrim", ScalarFunction::String { op: StringOp::RTrim });
805        self.register_scalar("length", ScalarFunction::String { op: StringOp::Length });
806        self.register_scalar("reverse", ScalarFunction::String { op: StringOp::Reverse });
807        self.register_scalar("repeat", ScalarFunction::String { op: StringOp::Repeat });
808        self.register_scalar("replace", ScalarFunction::String { op: StringOp::Replace });
809        self.register_scalar(
810            "substring",
811            ScalarFunction::String {
812                op: StringOp::Substring,
813            },
814        );
815        self.register_scalar(
816            "regex_matches",
817            ScalarFunction::String {
818                op: StringOp::RegexMatches,
819            },
820        );
821        self.register_scalar(
822            "regex_replace",
823            ScalarFunction::String {
824                op: StringOp::RegexReplace,
825            },
826        );
827        self.register_scalar("split", ScalarFunction::String { op: StringOp::Split });
828        self.register_scalar("head", ScalarFunction::String { op: StringOp::Head });
829        self.register_scalar("tail", ScalarFunction::String { op: StringOp::Tail });
830        self.register_scalar("left", ScalarFunction::String { op: StringOp::Left });
831        self.register_scalar("right", ScalarFunction::String { op: StringOp::Right });
832        self.register_scalar("lpad", ScalarFunction::String { op: StringOp::Lpad });
833        self.register_scalar("rpad", ScalarFunction::String { op: StringOp::Rpad });
834
835        // --- String basic (C++ port) ---
836        self.register_scalar("initcap", ScalarFunction::String { op: StringOp::InitCap });
837        self.register_scalar("concat_ws", ScalarFunction::String { op: StringOp::ConcatWs });
838        self.register_scalar(
839            "split_part",
840            ScalarFunction::String {
841                op: StringOp::SplitPart,
842            },
843        );
844        self.register_scalar(
845            "array_extract",
846            ScalarFunction::String {
847                op: StringOp::ArrayExtract,
848            },
849        );
850
851        // --- Regex string functions ---
852        self.register_scalar(
853            "regexp_full_match",
854            ScalarFunction::String {
855                op: StringOp::RegexpFullMatch,
856            },
857        );
858        self.register_scalar(
859            "regexp_extract",
860            ScalarFunction::String {
861                op: StringOp::RegexpExtract,
862            },
863        );
864        self.register_scalar(
865            "regexp_extract_all",
866            ScalarFunction::String {
867                op: StringOp::RegexpExtractAll,
868            },
869        );
870        self.register_scalar(
871            "regexp_split_to_array",
872            ScalarFunction::String {
873                op: StringOp::RegexpSplitToArray,
874            },
875        );
876        self.register_scalar(
877            "levenshtein",
878            ScalarFunction::String {
879                op: StringOp::Levenshtein,
880            },
881        );
882        self.register_scalar("soundex", ScalarFunction::String { op: StringOp::Soundex });
883
884        // --- Hash functions ---
885        self.register_scalar("md5", ScalarFunction::Hash { op: HashOp::Md5 });
886        self.register_scalar("sha256", ScalarFunction::Hash { op: HashOp::Sha256 });
887        self.register_scalar("hash", ScalarFunction::Hash { op: HashOp::Hash });
888
889        // --- Interval ---
890        self.register_scalar(
891            "to_years",
892            ScalarFunction::Interval {
893                op: IntervalOp::ToYears,
894            },
895        );
896        self.register_scalar(
897            "to_months",
898            ScalarFunction::Interval {
899                op: IntervalOp::ToMonths,
900            },
901        );
902        self.register_scalar("to_days", ScalarFunction::Interval { op: IntervalOp::ToDays });
903        self.register_scalar(
904            "to_hours",
905            ScalarFunction::Interval {
906                op: IntervalOp::ToHours,
907            },
908        );
909        self.register_scalar(
910            "to_minutes",
911            ScalarFunction::Interval {
912                op: IntervalOp::ToMinutes,
913            },
914        );
915        self.register_scalar(
916            "to_seconds",
917            ScalarFunction::Interval {
918                op: IntervalOp::ToSeconds,
919            },
920        );
921        self.register_scalar(
922            "to_milliseconds",
923            ScalarFunction::Interval {
924                op: IntervalOp::ToMilliseconds,
925            },
926        );
927        self.register_scalar(
928            "to_microseconds",
929            ScalarFunction::Interval {
930                op: IntervalOp::ToMicroseconds,
931            },
932        );
933
934        // --- Date/Time ---
935        self.register_scalar("date_part", ScalarFunction::Date { op: DateOp::DatePart });
936        self.register_scalar("date_trunc", ScalarFunction::Date { op: DateOp::DateTrunc });
937        self.register_scalar("date_diff", ScalarFunction::Date { op: DateOp::DateDiff });
938        self.register_scalar("date_add", ScalarFunction::Date { op: DateOp::DateAdd });
939        self.register_scalar(
940            "current_date",
941            ScalarFunction::Date {
942                op: DateOp::CurrentDate,
943            },
944        );
945        self.register_scalar(
946            "current_timestamp",
947            ScalarFunction::Date {
948                op: DateOp::CurrentTimestamp,
949            },
950        );
951        self.register_scalar("year", ScalarFunction::Date { op: DateOp::Year });
952        self.register_scalar("month", ScalarFunction::Date { op: DateOp::Month });
953
954        // Sequence functions (require catalog access at connection level)
955        self.register_scalar("nextval", ScalarFunction::SequenceOp { is_nextval: true });
956        self.register_scalar("currval", ScalarFunction::SequenceOp { is_nextval: false });
957        self.register_scalar("day", ScalarFunction::Date { op: DateOp::Day });
958        self.register_scalar("hour", ScalarFunction::Date { op: DateOp::Hour });
959        self.register_scalar("minute", ScalarFunction::Date { op: DateOp::Minute });
960        self.register_scalar("second", ScalarFunction::Date { op: DateOp::Second });
961        self.register_scalar("dayname", ScalarFunction::Date { op: DateOp::DayName });
962        self.register_scalar("monthname", ScalarFunction::Date { op: DateOp::MonthName });
963        self.register_scalar("last_day", ScalarFunction::Date { op: DateOp::LastDay });
964        self.register_scalar("make_date", ScalarFunction::Date { op: DateOp::MakeDate });
965        self.register_scalar("century", ScalarFunction::Date { op: DateOp::Century });
966        self.register_scalar("epoch_ms", ScalarFunction::Date { op: DateOp::EpochMs });
967        self.register_scalar(
968            "to_timestamp",
969            ScalarFunction::Date {
970                op: DateOp::ToTimestamp,
971            },
972        );
973        self.register_scalar("to_epoch_ms", ScalarFunction::Date { op: DateOp::ToEpochMs });
974
975        // --- Cast ---
976        self.register_scalar(
977            "CAST",
978            ScalarFunction::Cast {
979                target_type: CastTarget::String,
980            },
981        );
982        self.register_scalar(
983            "cast_string",
984            ScalarFunction::Cast {
985                target_type: CastTarget::String,
986            },
987        );
988        self.register_scalar(
989            "cast_int64",
990            ScalarFunction::Cast {
991                target_type: CastTarget::Int64,
992            },
993        );
994        self.register_scalar(
995            "cast_double",
996            ScalarFunction::Cast {
997                target_type: CastTarget::Double,
998            },
999        );
1000        self.register_scalar(
1001            "cast_bool",
1002            ScalarFunction::Cast {
1003                target_type: CastTarget::Bool,
1004            },
1005        );
1006        // Cast function name aliases (Cypher/SQL standard)
1007        self.register_scalar(
1008            "date",
1009            ScalarFunction::Cast {
1010                target_type: CastTarget::Date,
1011            },
1012        );
1013        self.register_scalar(
1014            "timestamp",
1015            ScalarFunction::Cast {
1016                target_type: CastTarget::Timestamp,
1017            },
1018        );
1019        self.register_scalar(
1020            "float",
1021            ScalarFunction::Cast {
1022                target_type: CastTarget::Double,
1023            },
1024        );
1025        self.register_scalar(
1026            "double",
1027            ScalarFunction::Cast {
1028                target_type: CastTarget::Double,
1029            },
1030        );
1031        self.register_scalar(
1032            "int64",
1033            ScalarFunction::Cast {
1034                target_type: CastTarget::Int64,
1035            },
1036        );
1037        self.register_scalar(
1038            "int",
1039            ScalarFunction::Cast {
1040                target_type: CastTarget::Int64,
1041            },
1042        );
1043        self.register_scalar(
1044            "bool",
1045            ScalarFunction::Cast {
1046                target_type: CastTarget::Bool,
1047            },
1048        );
1049        self.register_scalar(
1050            "boolean",
1051            ScalarFunction::Cast {
1052                target_type: CastTarget::Bool,
1053            },
1054        );
1055        self.register_scalar(
1056            "string",
1057            ScalarFunction::Cast {
1058                target_type: CastTarget::String,
1059            },
1060        );
1061        self.register_scalar(
1062            "blob",
1063            ScalarFunction::Cast {
1064                target_type: CastTarget::String,
1065            },
1066        );
1067
1068        // --- Blob ---
1069        self.register_scalar("encode", ScalarFunction::Blob { op: BlobOp::Encode });
1070        self.register_scalar("decode", ScalarFunction::Blob { op: BlobOp::Decode });
1071        self.register_scalar(
1072            "octet_length",
1073            ScalarFunction::Blob {
1074                op: BlobOp::OctetLength,
1075            },
1076        );
1077
1078        // --- List ---
1079        self.register_scalar("list_creation", ScalarFunction::List { op: ListOp::Creation });
1080        self.register_scalar("list_extract", ScalarFunction::List { op: ListOp::Extract });
1081        self.register_scalar("list_concat", ScalarFunction::List { op: ListOp::Concat });
1082        self.register_scalar("list_cat", ScalarFunction::List { op: ListOp::Concat });
1083        self.register_scalar("list_len", ScalarFunction::List { op: ListOp::Len });
1084        self.register_scalar("list_sort", ScalarFunction::List { op: ListOp::Sort });
1085        self.register_scalar("list_reverse", ScalarFunction::List { op: ListOp::Reverse });
1086        self.register_scalar("list_contains", ScalarFunction::List { op: ListOp::Contains });
1087        self.register_scalar("list_append", ScalarFunction::List { op: ListOp::Append });
1088        self.register_scalar("list_prepend", ScalarFunction::List { op: ListOp::Prepend });
1089        self.register_scalar("list_slice", ScalarFunction::List { op: ListOp::Slice });
1090
1091        // --- List functions (C++ port) ---
1092        self.register_scalar("range", ScalarFunction::List { op: ListOp::Range });
1093        self.register_scalar("list_distinct", ScalarFunction::List { op: ListOp::Distinct });
1094        self.register_scalar("list_unique", ScalarFunction::List { op: ListOp::Unique });
1095        self.register_scalar("list_sum", ScalarFunction::List { op: ListOp::Sum });
1096        self.register_scalar("list_product", ScalarFunction::List { op: ListOp::Product });
1097        self.register_scalar("list_any_value", ScalarFunction::List { op: ListOp::AnyValue });
1098        self.register_scalar("list_to_string", ScalarFunction::List { op: ListOp::ToString });
1099        self.register_scalar("list_position", ScalarFunction::List { op: ListOp::Position });
1100        self.register_scalar("list_indexof", ScalarFunction::List { op: ListOp::Position });
1101        self.register_scalar("list_has_all", ScalarFunction::List { op: ListOp::HasAll });
1102        self.register_scalar("list_has_any", ScalarFunction::List { op: ListOp::HasAny });
1103        self.register_scalar("list_count", ScalarFunction::List { op: ListOp::Count });
1104        self.register_scalar("list_min", ScalarFunction::List { op: ListOp::Min });
1105        self.register_scalar("list_max", ScalarFunction::List { op: ListOp::Max });
1106        self.register_scalar(
1107            "list_reverse_sort",
1108            ScalarFunction::List {
1109                op: ListOp::ReverseSort,
1110            },
1111        );
1112
1113        // --- Lambda-based list functions (evaluated by expression evaluator) ---
1114        self.register_scalar("list_transform", ScalarFunction::List { op: ListOp::Transform });
1115        self.register_scalar("list_filter", ScalarFunction::List { op: ListOp::Filter });
1116        self.register_scalar("list_reduce", ScalarFunction::List { op: ListOp::Reduce });
1117
1118        // --- List predicate functions ---
1119        self.register_scalar("any", ScalarFunction::List { op: ListOp::Any });
1120        self.register_scalar("all", ScalarFunction::List { op: ListOp::All });
1121        self.register_scalar("none", ScalarFunction::List { op: ListOp::None });
1122        self.register_scalar("single", ScalarFunction::List { op: ListOp::Single });
1123
1124        // --- Map ---
1125        self.register_scalar("map_creation", ScalarFunction::Map { op: MapOp::Creation });
1126        self.register_scalar("map_extract", ScalarFunction::Map { op: MapOp::Extract });
1127        self.register_scalar("element_at", ScalarFunction::Map { op: MapOp::Extract });
1128        self.register_scalar("map_keys", ScalarFunction::Map { op: MapOp::Keys });
1129        self.register_scalar("map_values", ScalarFunction::Map { op: MapOp::Values });
1130
1131        // --- Struct ---
1132        self.register_scalar("struct_creation", ScalarFunction::Struct { op: StructOp::Creation });
1133        self.register_scalar("struct_extract", ScalarFunction::Struct { op: StructOp::Extract });
1134
1135        // --- Union ---
1136        self.register_scalar(
1137            "union_value",
1138            ScalarFunction::Union {
1139                op: UnionOp::UnionValue,
1140            },
1141        );
1142        self.register_scalar(
1143            "union_extract",
1144            ScalarFunction::Union {
1145                op: UnionOp::UnionExtract,
1146            },
1147        );
1148        self.register_scalar("union_tag", ScalarFunction::Union { op: UnionOp::UnionTag });
1149
1150        // --- Boolean ---
1151        self.register_scalar("AND", ScalarFunction::Boolean { op: BooleanOp::And });
1152        self.register_scalar("OR", ScalarFunction::Boolean { op: BooleanOp::Or });
1153        self.register_scalar("XOR", ScalarFunction::Boolean { op: BooleanOp::Xor });
1154        self.register_scalar("NOT", ScalarFunction::Boolean { op: BooleanOp::Not });
1155
1156        // --- Utility ---
1157        self.register_scalar(
1158            "coalesce",
1159            ScalarFunction::Utility {
1160                op: UtilityOp::Coalesce,
1161            },
1162        );
1163        self.register_scalar("ifnull", ScalarFunction::Utility { op: UtilityOp::IfNull });
1164        self.register_scalar("nullif", ScalarFunction::Utility { op: UtilityOp::NullIf });
1165        self.register_scalar("size", ScalarFunction::Utility { op: UtilityOp::Size });
1166        self.register_scalar("cardinality", ScalarFunction::Utility { op: UtilityOp::Size });
1167        self.register_scalar("typeof", ScalarFunction::Utility { op: UtilityOp::TypeOf });
1168        self.register_scalar("error", ScalarFunction::Utility { op: UtilityOp::Error });
1169        self.register_scalar(
1170            "pg_isready",
1171            ScalarFunction::Utility {
1172                op: UtilityOp::PgIsReady,
1173            },
1174        );
1175
1176        // --- Greatest/Least (SQL extremum functions) ---
1177        self.register_scalar(
1178            "greatest",
1179            ScalarFunction::Utility {
1180                op: UtilityOp::Greatest,
1181            },
1182        );
1183        self.register_scalar("least", ScalarFunction::Utility { op: UtilityOp::Least });
1184        self.register_scalar(
1185            "constant_or_null",
1186            ScalarFunction::Utility {
1187                op: UtilityOp::ConstantOrNull,
1188            },
1189        );
1190
1191        // --- Schema ---
1192        self.register_scalar("OFFSET", ScalarFunction::Schema { op: SchemaOp::Offset });
1193        self.register_scalar("ID", ScalarFunction::Schema { op: SchemaOp::Id });
1194        self.register_scalar(
1195            "START_NODE",
1196            ScalarFunction::Schema {
1197                op: SchemaOp::StartNode,
1198            },
1199        );
1200        self.register_scalar("END_NODE", ScalarFunction::Schema { op: SchemaOp::EndNode });
1201        self.register_scalar("LABEL", ScalarFunction::Schema { op: SchemaOp::Label });
1202        self.register_scalar("COST", ScalarFunction::Schema { op: SchemaOp::Cost });
1203        self.register_scalar("ROWID", ScalarFunction::Schema { op: SchemaOp::RowId });
1204
1205        // --- Array ---
1206        self.register_scalar(
1207            "array_cosine_similarity",
1208            ScalarFunction::Array {
1209                op: ArrayOp::CosineSimilarity,
1210            },
1211        );
1212        self.register_scalar("array_distance", ScalarFunction::Array { op: ArrayOp::Distance });
1213        self.register_scalar(
1214            "array_inner_product",
1215            ScalarFunction::Array {
1216                op: ArrayOp::InnerProduct,
1217            },
1218        );
1219        self.register_scalar(
1220            "array_dot_product",
1221            ScalarFunction::Array {
1222                op: ArrayOp::DotProduct,
1223            },
1224        );
1225        self.register_scalar(
1226            "array_cross_product",
1227            ScalarFunction::Array {
1228                op: ArrayOp::CrossProduct,
1229            },
1230        );
1231        self.register_scalar(
1232            "array_squared_distance",
1233            ScalarFunction::Array {
1234                op: ArrayOp::SquaredDistance,
1235            },
1236        );
1237        self.register_scalar("array_intersect", ScalarFunction::Array { op: ArrayOp::Intersect });
1238
1239        // --- Path ---
1240        self.register_scalar("nodes", ScalarFunction::Path { op: PathOp::Nodes });
1241        self.register_scalar("rels", ScalarFunction::Path { op: PathOp::Rels });
1242        self.register_scalar("relationships", ScalarFunction::Path { op: PathOp::Rels });
1243        self.register_scalar("properties", ScalarFunction::Path { op: PathOp::Properties });
1244        self.register_scalar("is_trail", ScalarFunction::Path { op: PathOp::IsTrail });
1245        self.register_scalar("is_acyclic", ScalarFunction::Path { op: PathOp::IsAcyclic });
1246
1247        // --- UUID ---
1248        self.register_scalar("gen_random_uuid", ScalarFunction::Uuid);
1249
1250        // --- Map ---
1251        self.register_scalar(
1252            "map_from_entries",
1253            ScalarFunction::Map {
1254                op: MapOp::MapFromEntries,
1255            },
1256        );
1257
1258        // --- Blob ---
1259        self.register_scalar(
1260            "blob_from_bytes",
1261            ScalarFunction::Blob {
1262                op: BlobOp::BlobFromBytes,
1263            },
1264        );
1265        self.register_scalar("to_base64", ScalarFunction::Blob { op: BlobOp::ToBase64 });
1266        self.register_scalar("from_base64", ScalarFunction::Blob { op: BlobOp::FromBase64 });
1267
1268        // --- Array utility aliases (delegate to list functions) ---
1269        self.register_scalar("array_concat", ScalarFunction::List { op: ListOp::Concat });
1270        self.register_scalar("array_cat", ScalarFunction::List { op: ListOp::Concat });
1271        self.register_scalar("array_append", ScalarFunction::List { op: ListOp::Append });
1272        self.register_scalar("array_push_back", ScalarFunction::List { op: ListOp::Append });
1273        self.register_scalar("array_prepend", ScalarFunction::List { op: ListOp::Prepend });
1274        self.register_scalar("array_push_front", ScalarFunction::List { op: ListOp::Prepend });
1275        self.register_scalar("array_contains", ScalarFunction::List { op: ListOp::Contains });
1276        self.register_scalar("array_has", ScalarFunction::List { op: ListOp::Contains });
1277        self.register_scalar("array_slice", ScalarFunction::List { op: ListOp::Slice });
1278        self.register_scalar("array_value", ScalarFunction::List { op: ListOp::Creation });
1279
1280        // --- Aggregate ---
1281        self.register_aggregate("COUNT", AggregateFunction::Count);
1282        self.register_aggregate("COUNT(*)", AggregateFunction::CountStar);
1283        self.register_aggregate("COUNT_IF", AggregateFunction::CountIf);
1284        self.register_aggregate("SUM", AggregateFunction::Sum);
1285        self.register_aggregate("AVG", AggregateFunction::Avg);
1286        self.register_aggregate("MIN", AggregateFunction::Min);
1287        self.register_aggregate("MAX", AggregateFunction::Max);
1288        self.register_aggregate("COLLECT", AggregateFunction::Collect);
1289        self.register_aggregate("STDDEV", AggregateFunction::StdDev);
1290        self.register_aggregate("VARIANCE", AggregateFunction::Variance);
1291        self.register_aggregate(
1292            "STRING_AGG",
1293            AggregateFunction::StringAgg {
1294                delimiter: ",".to_string(),
1295            },
1296        );
1297        self.register_aggregate(
1298            "GROUP_CONCAT",
1299            AggregateFunction::StringAgg {
1300                delimiter: ",".to_string(),
1301            },
1302        );
1303        self.register_aggregate("PERCENTILE_DISC", AggregateFunction::PercentileDisc { percentile: 0.5 });
1304        self.register_aggregate("PERCENTILE_CONT", AggregateFunction::PercentileCont { percentile: 0.5 });
1305
1306        // --- Table ---
1307        self.register_table("list_tables", TableFunction::ListTables);
1308    }
1309
1310    // --- Registration ---
1311
1312    pub fn register_scalar(&mut self, name: &str, func: ScalarFunction) {
1313        self.scalar_functions.insert(name.to_lowercase(), func);
1314    }
1315
1316    pub fn register_aggregate(&mut self, name: &str, func: AggregateFunction) {
1317        self.aggregate_functions.insert(name.to_lowercase(), func);
1318    }
1319
1320    pub fn register_table(&mut self, name: &str, func: TableFunction) {
1321        self.table_functions.insert(name.to_lowercase(), func);
1322    }
1323
1324    // --- Lookup ---
1325
1326    pub fn resolve(&self, name: &str) -> Option<ResolvedFunction> {
1327        let lower = name.to_lowercase();
1328        if let Some(f) = self.scalar_functions.get(&lower) {
1329            return Some(ResolvedFunction::Scalar(f.clone()));
1330        }
1331        if let Some(f) = self.aggregate_functions.get(&lower) {
1332            return Some(ResolvedFunction::Aggregate(f.clone()));
1333        }
1334        if let Some(f) = self.table_functions.get(&lower) {
1335            return Some(ResolvedFunction::Table(f.clone()));
1336        }
1337        None
1338    }
1339
1340    pub fn get_scalar(&self, name: &str) -> Option<&ScalarFunction> {
1341        self.scalar_functions.get(&name.to_lowercase())
1342    }
1343
1344    pub fn get_aggregate(&self, name: &str) -> Option<&AggregateFunction> {
1345        self.aggregate_functions.get(&name.to_lowercase())
1346    }
1347
1348    pub fn get_table(&self, name: &str) -> Option<&TableFunction> {
1349        self.table_functions.get(&name.to_lowercase())
1350    }
1351
1352    /// List all registered functions as (name, kind) pairs.
1353    /// Kind is one of: "SCALAR", "AGGREGATE", "TABLE".
1354    pub fn list_all(&self) -> Vec<(String, String)> {
1355        let mut result = Vec::new();
1356        for name in self.scalar_functions.keys() {
1357            result.push((name.clone(), "SCALAR".to_string()));
1358        }
1359        for name in self.aggregate_functions.keys() {
1360            result.push((name.clone(), "AGGREGATE".to_string()));
1361        }
1362        for name in self.table_functions.keys() {
1363            result.push((name.clone(), "TABLE".to_string()));
1364        }
1365        result.sort_by(|a, b| a.0.cmp(&b.0));
1366        result
1367    }
1368
1369    pub fn contains(&self, name: &str) -> bool {
1370        let lower = name.to_lowercase();
1371        self.scalar_functions.contains_key(&lower)
1372            || self.aggregate_functions.contains_key(&lower)
1373            || self.table_functions.contains_key(&lower)
1374    }
1375
1376    /// Number of registered scalar functions.
1377    pub fn scalar_count(&self) -> usize {
1378        self.scalar_functions.len()
1379    }
1380
1381    /// Number of registered aggregate functions.
1382    pub fn aggregate_count(&self) -> usize {
1383        self.aggregate_functions.len()
1384    }
1385
1386    /// Number of registered table functions.
1387    pub fn table_count(&self) -> usize {
1388        self.table_functions.len()
1389    }
1390
1391    /// Total number of registered functions.
1392    pub fn total_count(&self) -> usize {
1393        self.scalar_count() + self.aggregate_count() + self.table_count()
1394    }
1395
1396    /// Execute a table function by name with the given pre-evaluated arguments.
1397    ///
1398    /// Returns a `Vec<Vec<Value>>` representing rows of results.
1399    /// Each inner vec is one row with one or more column values.
1400    ///
1401    /// `graph` is an optional graph data source passed to graph-algorithm
1402    /// table functions. Callers that own a storage catalog (e.g. the query
1403    /// processor or the connection layer) supply it so GDS functions run
1404    /// against real node/rel tables; callers without catalog access pass
1405    /// `None`, in which case the closure may fall back to its built-in data.
1406    pub fn execute_table_function(
1407        &self,
1408        name: &str,
1409        args: &[Value],
1410        graph: Option<&dyn crate::graph::GraphDataSource>,
1411    ) -> Result<Vec<Vec<Value>>, String> {
1412        use akar_common::vector::DataChunk;
1413
1414        let func = self
1415            .get_table(name)
1416            .ok_or_else(|| format!("Table function '{}' not found", name))?;
1417
1418        match func {
1419            TableFunction::ListTables => Err("ListTables requires catalog access — handled at connection level".into()),
1420            TableFunction::ShowColumns { .. } => {
1421                Err("ShowColumns requires catalog access — handled at connection level".into())
1422            }
1423            TableFunction::Custom { name: custom_name } => Err(format!(
1424                "Table function '{}' requires an extension or external context to be loaded. \
1425                 Use LOAD EXTENSION or CALL with the appropriate handler.",
1426                custom_name
1427            )),
1428            TableFunction::CustomTable { name: _, execute } => {
1429                let mut chunk = DataChunk {
1430                    fields: Vec::new(),
1431                    field_types: Vec::new(),
1432                    size: 0,
1433                    field_names: vec![],
1434                    sel_vector: None,
1435                };
1436                execute(args, &mut chunk).map(|_| {
1437                    let mut rows = Vec::new();
1438                    for row in 0..chunk.size {
1439                        let mut row_vals = Vec::new();
1440                        for field_idx in 0..chunk.fields.len() {
1441                            row_vals.push(chunk.get_value(field_idx, row).unwrap_or(Value::Null));
1442                        }
1443                        rows.push(row_vals);
1444                    }
1445                    rows
1446                })
1447            }
1448            TableFunction::CustomTableWithGraph { name: _, execute } => {
1449                let mut chunk = DataChunk {
1450                    fields: Vec::new(),
1451                    field_types: Vec::new(),
1452                    size: 0,
1453                    field_names: vec![],
1454                    sel_vector: None,
1455                };
1456                execute(args, graph, &mut chunk).map(|_| {
1457                    let mut rows = Vec::new();
1458                    for row in 0..chunk.size {
1459                        let mut row_vals = Vec::new();
1460                        for field_idx in 0..chunk.fields.len() {
1461                            row_vals.push(chunk.get_value(field_idx, row).unwrap_or(Value::Null));
1462                        }
1463                        rows.push(row_vals);
1464                    }
1465                    rows
1466                })
1467            }
1468            TableFunction::ScanCsv { .. }
1469            | TableFunction::ScanParquet { .. }
1470            | TableFunction::ScanJson { .. }
1471            | TableFunction::CurrentSetting { .. } => Err(format!(
1472                "Table function '{}' cannot be executed via CALL — it requires file path or catalog context. \
1473                 Use COPY FROM 'file' FORMAT CSV/PARQUET/JSON or CALL current_setting('key') via the connection layer.",
1474                name
1475            )),
1476        }
1477    }
1478}