Skip to main content

radixdb_executor/expression/
compiler.rs

1// Copyright 2026 RadixDB Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Expression Compiler
16//
17// Transforms AST Expressions into compiled Programs.
18// This is where the magic happens - we convert recursive AST into linear bytecode.
19//
20// Design principles:
21// 1. Resolve everything at compile time (column indices, function pointers, patterns)
22// 2. Flatten recursion into linear instruction sequences
23// 3. Handle short-circuit evaluation with jumps
24// 4. Pre-compute constant expressions where possible
25
26use std::cell::Cell;
27use std::sync::Arc;
28
29use radixdb_core::CompactArc;
30use radixdb_core::SmartString;
31use radixdb_core::StringMap;
32use rustc_hash::{FxHashMap, FxHashSet};
33
34use super::execution_context::ExecuteContext;
35use super::ops::{CompiledPattern, Op};
36use super::program::{Program, ProgramBuilder};
37use super::vm::ExprVM;
38use radixdb_core::{DataType, Row, Value, ValueSet};
39use radixdb_functions::{global_registry, FunctionRegistry};
40use radixdb_sql::ast::*;
41
42/// Convert a SQL type name into the scalar type encoded by expression
43/// bytecode. Width/precision modifiers do not alter the VM scalar kind.
44pub fn string_to_datatype(type_str: &str) -> DataType {
45    let upper = type_str.to_uppercase();
46    let base_type = upper.split('(').next().unwrap_or(&upper).trim();
47    match base_type {
48        "INTEGER" | "INT" | "BIGINT" | "SMALLINT" | "TINYINT" => DataType::Integer,
49        "FLOAT" | "DOUBLE" | "REAL" => DataType::Float,
50        "DECIMAL" | "NUMERIC" => DataType::Decimal,
51        "TEXT" | "VARCHAR" | "CHAR" | "STRING" | "CLOB" => DataType::Text,
52        "BOOLEAN" | "BOOL" => DataType::Boolean,
53        "TIMESTAMP" | "DATETIME" | "TIME" => DataType::Timestamp,
54        "DATE" => DataType::Date,
55        "JSON" | "JSONB" => DataType::Json,
56        "UUID" => DataType::Uuid,
57        "BYTES" | "BLOB" | "BINARY" | "VARBINARY" => DataType::Bytes,
58        "VECTOR" => DataType::Vector,
59        _ => DataType::Text,
60    }
61}
62
63/// Return the stable textual form used to bind expression aliases.
64pub fn expression_to_string(expr: &Expression) -> String {
65    match expr {
66        Expression::Identifier(id) => id.value.to_string(),
67        Expression::QualifiedIdentifier(qid) => {
68            format!("{}.{}", qid.qualifier.value, qid.name.value)
69        }
70        Expression::IntegerLiteral(lit) => lit.value.to_string(),
71        Expression::FloatLiteral(lit) => lit.value.to_string(),
72        Expression::StringLiteral(lit) => format!("'{}'", lit.value),
73        Expression::BooleanLiteral(lit) => lit.value.to_string(),
74        Expression::FunctionCall(func) => {
75            let args: Vec<String> = func.arguments.iter().map(expression_to_string).collect();
76            format!("{}({})", func.function, args.join(", "))
77        }
78        Expression::Infix(infix) => format!(
79            "{} {} {}",
80            expression_to_string(&infix.left),
81            infix.operator,
82            expression_to_string(&infix.right)
83        ),
84        _ => format!("{expr}"),
85    }
86}
87
88/// Compilation error
89#[derive(Debug, Clone)]
90pub enum CompileError {
91    /// Column not found
92    ColumnNotFound(String),
93    /// Function not found
94    FunctionNotFound(String),
95    /// Invalid expression
96    InvalidExpression(String),
97    /// Unsupported expression type
98    UnsupportedExpression(String),
99    /// Type error
100    TypeError(String),
101}
102
103impl std::fmt::Display for CompileError {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            CompileError::ColumnNotFound(name) => {
107                write!(f, "Column '{}' not found", name)
108            }
109            CompileError::FunctionNotFound(name) => write!(f, "Function not found: {}", name),
110            CompileError::InvalidExpression(msg) => write!(f, "Invalid expression: {}", msg),
111            CompileError::UnsupportedExpression(msg) => {
112                write!(f, "Unsupported expression: {}", msg)
113            }
114            CompileError::TypeError(msg) => write!(f, "Type error: {}", msg),
115        }
116    }
117}
118
119impl std::error::Error for CompileError {}
120
121/// Result of column resolution - indicates which row the column is from
122#[derive(Debug, Clone, Copy)]
123pub enum ColumnSource {
124    /// Column from first row with given index
125    Row1(u16),
126    /// Column from second row (for joins) with given index
127    Row2(u16),
128}
129
130/// Compilation context
131///
132/// Contains all the information needed to compile expressions:
133/// - Column name to index mapping
134/// - Function registry
135/// - Outer query columns (for correlated subqueries)
136pub struct CompileContext<'a> {
137    /// Column name -> index (case-insensitive)
138    columns: StringMap<u16>,
139
140    /// Qualified column name -> (index, is_row2)
141    /// For row1 columns: index is the direct row1 index
142    /// For row2 columns: index is the row2 index (not offset)
143    qualified_columns: StringMap<StringMap<ColumnSource>>,
144
145    /// Second row columns (for joins)
146    columns2: Option<StringMap<u16>>,
147
148    /// Tables that belong to row2 (for tracking which tables are from second row)
149    row2_tables: FxHashSet<String>,
150
151    /// Outer query columns (for correlated subqueries)
152    outer_columns: Option<FxHashMap<CompactArc<str>, u16>>,
153
154    /// Function registry
155    functions: &'a FunctionRegistry,
156
157    /// Expression alias mapping (for HAVING with GROUP BY expressions)
158    expression_aliases: StringMap<u16>,
159
160    /// Column aliases
161    column_aliases: StringMap<String>,
162
163    /// Deferred context validation error. Context builders remain ergonomic,
164    /// while compilation fails before any truncated bytecode operand exists.
165    invalid_context: Option<String>,
166}
167
168impl<'a> CompileContext<'a> {
169    /// Create a new compilation context
170    pub fn new(columns: &[String], functions: &'a FunctionRegistry) -> Self {
171        let invalid_context = (columns.len() > (u16::MAX as usize + 1)).then(|| {
172            format!(
173                "primary row has {} columns; expression bytecode supports at most {}",
174                columns.len(),
175                u16::MAX as usize + 1
176            )
177        });
178        let mut col_map = StringMap::new();
179        let mut qualified_map: StringMap<StringMap<ColumnSource>> = StringMap::new();
180
181        for (i, col) in columns.iter().enumerate() {
182            let Ok(index) = u16::try_from(i) else {
183                continue;
184            };
185            let lower = col.to_lowercase();
186            col_map.insert(lower.clone(), index);
187
188            // Handle qualified names (table.column)
189            if let Some(dot_idx) = col.rfind('.') {
190                let table = col[..dot_idx].to_lowercase();
191                let column = col[dot_idx + 1..].to_lowercase();
192                qualified_map
193                    .entry(table)
194                    .or_default()
195                    .insert(column.clone(), ColumnSource::Row1(index));
196
197                // Also map unqualified column name for lookup without table prefix
198                // Don't overwrite if already exists (first occurrence wins)
199                col_map.entry(column).or_insert(index);
200            }
201        }
202
203        Self {
204            columns: col_map,
205            qualified_columns: qualified_map,
206            columns2: None,
207            row2_tables: FxHashSet::default(),
208            outer_columns: None,
209            functions,
210            expression_aliases: StringMap::new(),
211            column_aliases: StringMap::new(),
212            invalid_context,
213        }
214    }
215
216    /// Create context using global function registry
217    pub fn with_global_registry(columns: &[String]) -> Self {
218        Self::new(columns, global_registry())
219    }
220
221    /// Add second row columns (for join compilation)
222    pub fn with_second_row(mut self, columns2: &[String]) -> Self {
223        if columns2.len() > (u16::MAX as usize + 1) {
224            self.invalid_context = Some(format!(
225                "second row has {} columns; expression bytecode supports at most {}",
226                columns2.len(),
227                u16::MAX as usize + 1
228            ));
229        }
230        let mut col_map = StringMap::new();
231        for (i, col) in columns2.iter().enumerate() {
232            let Ok(index) = u16::try_from(i) else {
233                continue;
234            };
235            let lower = col.to_lowercase();
236            col_map.insert(lower.clone(), index);
237
238            // Handle qualified names (table.column)
239            if let Some(dot_idx) = col.rfind('.') {
240                let table = col[..dot_idx].to_lowercase();
241                let column = col[dot_idx + 1..].to_lowercase();
242
243                // Track this table as belonging to row2
244                self.row2_tables.insert(table.clone());
245
246                // Add qualified name with Row2 source (index is local to row2)
247                self.qualified_columns
248                    .entry(table)
249                    .or_default()
250                    .insert(column.clone(), ColumnSource::Row2(index));
251
252                // Also map unqualified column name (don't overwrite if exists)
253                col_map.entry(column).or_insert(index);
254            }
255        }
256        self.columns2 = Some(col_map);
257        self
258    }
259
260    /// Add outer columns for correlated subqueries
261    pub fn with_outer_columns(mut self, outer_cols: &[String]) -> Self {
262        if outer_cols.len() > (u16::MAX as usize + 1) {
263            self.invalid_context = Some(format!(
264                "outer row has {} columns; expression bytecode supports at most {}",
265                outer_cols.len(),
266                u16::MAX as usize + 1
267            ));
268        }
269        let mut map = FxHashMap::default();
270        for (i, col) in outer_cols.iter().enumerate() {
271            if let Ok(index) = u16::try_from(i) {
272                map.insert(CompactArc::from(col.to_lowercase().as_str()), index);
273            }
274        }
275        self.outer_columns = Some(map);
276        self
277    }
278
279    /// Add expression aliases (for HAVING clause)
280    pub fn with_expression_aliases(mut self, aliases: StringMap<u16>) -> Self {
281        self.expression_aliases = aliases;
282        self
283    }
284
285    /// Add column aliases
286    pub fn with_column_aliases(mut self, aliases: StringMap<String>) -> Self {
287        self.column_aliases = aliases;
288        self
289    }
290
291    /// Resolve a column name to its source (Row1 or Row2)
292    fn resolve_column(&self, name: &str) -> Option<ColumnSource> {
293        let lower = name.to_lowercase();
294
295        // Check column aliases first
296        if let Some(original) = self.column_aliases.get(&lower) {
297            if let Some(&idx) = self.columns.get(original) {
298                return Some(ColumnSource::Row1(idx));
299            }
300        }
301
302        // Direct lookup in primary columns (Row1)
303        if let Some(&idx) = self.columns.get(&lower) {
304            return Some(ColumnSource::Row1(idx));
305        }
306
307        // Try second row if available (Row2)
308        if let Some(ref cols2) = self.columns2 {
309            if let Some(&idx) = cols2.get(&lower) {
310                return Some(ColumnSource::Row2(idx));
311            }
312        }
313
314        None
315    }
316
317    /// Resolve a qualified column name (table.column)
318    fn resolve_qualified(&self, table: &str, column: &str) -> Option<ColumnSource> {
319        let table_lower = table.to_lowercase();
320        let column_lower = column.to_lowercase();
321
322        if let Some(table_cols) = self.qualified_columns.get(&table_lower) {
323            if let Some(&source) = table_cols.get(&column_lower) {
324                return Some(source);
325            }
326        }
327
328        // Check if the FULLY QUALIFIED name (table.column) exists in outer_columns.
329        // This distinguishes between `t.id` (outer reference) and `t2.id` (current row).
330        // Only if the qualified name is in outer context should we skip the fallback.
331        if let Some(ref outer_cols) = self.outer_columns {
332            let qualified_name = format!("{}.{}", table_lower, column_lower);
333            if outer_cols.contains_key(qualified_name.as_str()) {
334                // Qualified name exists in outer context - don't fall back
335                return None;
336            }
337        }
338
339        // Qualified name not in outer context - safe to fall back to unqualified lookup
340        self.resolve_column(&column_lower)
341    }
342
343    /// Resolve outer column (for correlated subqueries)
344    fn resolve_outer_column(&self, name: &str) -> Option<CompactArc<str>> {
345        let lower = name.to_lowercase();
346        self.outer_columns.as_ref().and_then(|cols| {
347            if cols.contains_key(lower.as_str()) {
348                Some(CompactArc::from(lower.as_str()))
349            } else {
350                None
351            }
352        })
353    }
354
355    /// Check if an expression matches an expression alias
356    fn check_expression_alias(&self, expr: &Expression) -> Option<u16> {
357        if self.expression_aliases.is_empty() {
358            return None;
359        }
360        let expr_str = expression_to_string(expr).to_lowercase();
361        self.expression_aliases.get(&expr_str).copied()
362    }
363}
364
365/// Expression compiler
366pub struct ExprCompiler<'a> {
367    ctx: &'a CompileContext<'a>,
368    /// Guard flag to prevent recursive constant folding
369    folding: Cell<bool>,
370}
371
372impl<'a> ExprCompiler<'a> {
373    pub fn new(ctx: &'a CompileContext<'a>) -> Self {
374        Self {
375            ctx,
376            folding: Cell::new(false),
377        }
378    }
379
380    /// Compile an expression into a Program
381    pub fn compile(&self, expr: &Expression) -> Result<Program, CompileError> {
382        if let Some(error) = &self.ctx.invalid_context {
383            return Err(CompileError::InvalidExpression(error.clone()));
384        }
385        let mut builder = ProgramBuilder::new();
386        self.compile_expr(expr, &mut builder)?;
387        builder.emit(Op::Return);
388        if builder.is_overflowed() {
389            return Err(CompileError::InvalidExpression(
390                "expression bytecode exceeds the u16 instruction limit".to_string(),
391            ));
392        }
393        builder
394            .build()
395            .map_err(|error| CompileError::InvalidExpression(error.to_string()))
396    }
397
398    /// Compile an expression for use as a boolean filter
399    pub fn compile_filter(&self, expr: &Expression) -> Result<Program, CompileError> {
400        if let Some(error) = &self.ctx.invalid_context {
401            return Err(CompileError::InvalidExpression(error.clone()));
402        }
403        // For simple filter expressions, we can optimize
404        let mut builder = ProgramBuilder::new();
405        self.compile_expr(expr, &mut builder)?;
406        builder.emit(Op::Return);
407        if builder.is_overflowed() {
408            return Err(CompileError::InvalidExpression(
409                "expression bytecode exceeds the u16 instruction limit".to_string(),
410            ));
411        }
412        builder
413            .build()
414            .map_err(|error| CompileError::InvalidExpression(error.to_string()))
415    }
416
417    /// Flatten chained concatenation operators into a list of operands.
418    /// For `a || b || c || d`, returns [a, b, c, d] in order.
419    fn flatten_concat_chain_infix<'b>(
420        infix: &'b InfixExpression,
421        operands: &mut Vec<&'b Expression>,
422    ) {
423        // Flatten left side
424        if let Expression::Infix(left_infix) = &*infix.left {
425            if left_infix.op_type == InfixOperator::Concat {
426                Self::flatten_concat_chain_infix(left_infix, operands);
427            } else {
428                operands.push(&infix.left);
429            }
430        } else {
431            operands.push(&infix.left);
432        }
433        // Flatten right side
434        if let Expression::Infix(right_infix) = &*infix.right {
435            if right_infix.op_type == InfixOperator::Concat {
436                Self::flatten_concat_chain_infix(right_infix, operands);
437            } else {
438                operands.push(&infix.right);
439            }
440        } else {
441            operands.push(&infix.right);
442        }
443    }
444
445    /// Try to fold a column-free expression into a constant at compile time.
446    /// Compiles the expression into a temporary program, executes it with an empty
447    /// row context, and returns the result if successful.
448    fn try_fold_constant(&self, expr: &Expression) -> Option<Value> {
449        use std::cell::RefCell;
450
451        thread_local! {
452            static FOLD_VM: RefCell<ExprVM> = RefCell::new(ExprVM::new());
453            static FOLD_ROW: Row = Row::new();
454        }
455
456        self.folding.set(true);
457
458        let empty_cols: &[String] = &[];
459        let ctx = CompileContext::new(empty_cols, self.ctx.functions);
460        let compiler = ExprCompiler::new(&ctx);
461        compiler.folding.set(true);
462
463        let mut builder = ProgramBuilder::new();
464        let ok = compiler.compile_expr(expr, &mut builder);
465        self.folding.set(false);
466
467        ok.ok()?;
468        builder.emit(Op::Return);
469        let program = builder.build_unoptimized().ok()?;
470
471        FOLD_ROW.with(|empty_row| {
472            let exec_ctx = ExecuteContext::new(empty_row);
473            FOLD_VM.with(|vm_cell| {
474                let mut vm = vm_cell.borrow_mut();
475                vm.execute(&program, &exec_ctx).ok()
476            })
477        })
478    }
479
480    /// Compile an expression, emitting ops to the builder
481    fn compile_expr(
482        &self,
483        expr: &Expression,
484        builder: &mut ProgramBuilder,
485    ) -> Result<(), CompileError> {
486        // Check if this expression matches an expression alias (for HAVING)
487        if let Some(idx) = self.ctx.check_expression_alias(expr) {
488            builder.emit(Op::LoadAggregateResult(idx));
489            return Ok(());
490        }
491
492        // Constant folding: if not already folding and expression is column-free
493        // and non-trivial, evaluate once at compile time and emit as LoadConst.
494        // This optimizes deterministic expressions like ABS(-5) + 1 or UPPER('text').
495        // Non-deterministic functions (NOW, RANDOM, etc.) are excluded by is_foldable_expr
496        // and handled separately by pushdown's try_eval_constant_expr at query time.
497        if !self.folding.get() && is_foldable_expr(expr, self.ctx.functions) {
498            if let Some(value) = self.try_fold_constant(expr) {
499                builder.emit(Op::LoadConst(value));
500                return Ok(());
501            }
502        }
503
504        match expr {
505            // === LITERALS ===
506            Expression::IntegerLiteral(lit) => {
507                builder.emit(Op::LoadConst(Value::Integer(lit.value)));
508            }
509
510            Expression::FloatLiteral(lit) => {
511                builder.emit(Op::LoadConst(Value::Float(lit.value)));
512            }
513
514            Expression::StringLiteral(lit) => {
515                // Handle type hints (DATE, TIMESTAMP, etc.)
516                let value = if let Some(ref hint) = lit.type_hint {
517                    match hint.to_uppercase().as_str() {
518                        "TIMESTAMP" | "DATETIME" => {
519                            radixdb_core::value::parse_timestamp(&lit.value)
520                                .map(Value::Timestamp)
521                                .unwrap_or_else(|_| Value::Text(lit.value.clone()))
522                        }
523                        "DATE" => radixdb_core::value::parse_date_days_since_unix_epoch(&lit.value)
524                            .map(Value::date)
525                            .unwrap_or_else(|| Value::Text(lit.value.clone())),
526                        _ => Value::Text(lit.value.clone()),
527                    }
528                } else {
529                    Value::Text(lit.value.clone())
530                };
531                builder.emit(Op::LoadConst(value));
532            }
533
534            Expression::BooleanLiteral(lit) => {
535                builder.emit(Op::LoadConst(Value::Boolean(lit.value)));
536            }
537
538            Expression::NullLiteral(_) => {
539                builder.emit(Op::LoadNull(DataType::Null));
540            }
541
542            Expression::BoundValue(value) => {
543                builder.emit(Op::LoadConst((**value).clone()));
544            }
545
546            // === IDENTIFIERS ===
547            Expression::Identifier(id) => {
548                // CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP are now parsed as
549                // FunctionCall by the parser, so they no longer reach this path.
550                match id.value_lower.as_str() {
551                    "true" => {
552                        builder.emit(Op::LoadConst(Value::Boolean(true)));
553                        return Ok(());
554                    }
555                    "false" => {
556                        builder.emit(Op::LoadConst(Value::Boolean(false)));
557                        return Ok(());
558                    }
559                    _ => {}
560                }
561
562                // Try to resolve as column
563                // First, check if the identifier contains a dot (qualified name like "table.column")
564                if let Some(dot_idx) = id.value_lower.rfind('.') {
565                    // Treat as qualified identifier
566                    let table = &id.value_lower[..dot_idx];
567                    let column = &id.value_lower[dot_idx + 1..];
568                    if let Some(source) = self.ctx.resolve_qualified(table, column) {
569                        match source {
570                            ColumnSource::Row1(idx) => builder.emit(Op::LoadColumn(idx)),
571                            ColumnSource::Row2(idx) => builder.emit(Op::LoadColumn2(idx)),
572                        }
573                    } else if let Some(name) = self.ctx.resolve_outer_column(column) {
574                        builder.emit(Op::LoadOuterColumn(name));
575                    } else {
576                        return Err(CompileError::ColumnNotFound(id.value.to_string()));
577                    }
578                } else if let Some(source) = self.ctx.resolve_column(&id.value_lower) {
579                    match source {
580                        ColumnSource::Row1(idx) => builder.emit(Op::LoadColumn(idx)),
581                        ColumnSource::Row2(idx) => builder.emit(Op::LoadColumn2(idx)),
582                    }
583                } else if let Some(name) = self.ctx.resolve_outer_column(&id.value_lower) {
584                    builder.emit(Op::LoadOuterColumn(name));
585                } else {
586                    return Err(CompileError::ColumnNotFound(id.value.to_string()));
587                }
588            }
589
590            Expression::QualifiedIdentifier(qid) => {
591                let table = &qid.qualifier.value_lower;
592                let column = &qid.name.value_lower;
593
594                if let Some(source) = self.ctx.resolve_qualified(table, column) {
595                    match source {
596                        ColumnSource::Row1(idx) => builder.emit(Op::LoadColumn(idx)),
597                        ColumnSource::Row2(idx) => builder.emit(Op::LoadColumn2(idx)),
598                    }
599                } else {
600                    // For qualified identifiers (e.g., c.id), prefer the qualified
601                    // name in outer_columns over unqualified. This prevents incorrect
602                    // resolution when the unqualified key ("id") is overwritten in
603                    // outer_row by an inner row's column with the same name.
604                    let qualified_name =
605                        format!("{}.{}", table.to_lowercase(), column.to_lowercase());
606                    if let Some(name) = self
607                        .ctx
608                        .resolve_outer_column(&qualified_name)
609                        .or_else(|| self.ctx.resolve_outer_column(column))
610                    {
611                        builder.emit(Op::LoadOuterColumn(name));
612                    } else {
613                        return Err(CompileError::ColumnNotFound(format!(
614                            "{}.{}",
615                            table, column
616                        )));
617                    }
618                }
619            }
620
621            // === PARAMETERS ===
622            Expression::Parameter(param) => {
623                if param.name.starts_with(':') {
624                    let name = &param.name[1..];
625                    builder.emit(Op::LoadNamedParam(CompactArc::from(name)));
626                } else if param.index > 0 {
627                    let index = u16::try_from(param.index - 1).map_err(|_| {
628                        CompileError::InvalidExpression(
629                            "parameter ordinal exceeds the u16 bytecode limit".to_string(),
630                        )
631                    })?;
632                    builder.emit(Op::LoadParam(index));
633                } else {
634                    return Err(CompileError::InvalidExpression(
635                        "Invalid parameter".to_string(),
636                    ));
637                }
638            }
639
640            // === INFIX EXPRESSIONS ===
641            Expression::Infix(infix) => {
642                self.compile_infix(infix, builder)?;
643            }
644
645            // === PREFIX EXPRESSIONS ===
646            Expression::Prefix(prefix) => {
647                self.compile_prefix(prefix, builder)?;
648            }
649
650            // === IN EXPRESSION ===
651            Expression::In(in_expr) => {
652                self.compile_in(in_expr, builder)?;
653            }
654
655            Expression::InHashSet(in_hash) => {
656                self.compile_expr(&in_hash.column, builder)?;
657                let has_null = in_hash.values.iter().any(|v| v.is_null());
658                if in_hash.not {
659                    builder.emit(Op::NotInSet(in_hash.values.clone(), has_null));
660                } else {
661                    builder.emit(Op::InSet(in_hash.values.clone(), has_null));
662                }
663            }
664
665            // === BETWEEN EXPRESSION ===
666            Expression::Between(between) => {
667                self.compile_expr(&between.expr, builder)?;
668                self.compile_expr(&between.lower, builder)?;
669                self.compile_expr(&between.upper, builder)?;
670                if between.not {
671                    builder.emit(Op::NotBetween);
672                } else {
673                    builder.emit(Op::Between);
674                }
675            }
676
677            // === LIKE EXPRESSION ===
678            Expression::Like(like) => {
679                self.compile_like(like, builder)?;
680            }
681
682            // === CASE EXPRESSION ===
683            Expression::Case(case) => {
684                self.compile_case(case, builder)?;
685            }
686
687            // === CAST EXPRESSION ===
688            Expression::Cast(cast) => {
689                self.compile_expr(&cast.expr, builder)?;
690                if cast.type_name.contains('.') {
691                    builder.emit(Op::CastExternal(CompactArc::from(cast.type_name.as_str())));
692                } else {
693                    let dt = string_to_datatype(&cast.type_name);
694                    builder.emit(Op::Cast(dt));
695                }
696            }
697
698            // === FUNCTION CALL ===
699            Expression::FunctionCall(func) => {
700                self.compile_function(func, builder)?;
701            }
702
703            // === ALIASED EXPRESSION ===
704            Expression::Aliased(aliased) => {
705                self.compile_expr(&aliased.expression, builder)?;
706            }
707
708            // === DISTINCT ===
709            Expression::Distinct(distinct) => {
710                self.compile_expr(&distinct.expr, builder)?;
711            }
712
713            // === LIST ===
714            Expression::List(_) | Expression::ExpressionList(_) => {
715                return Err(CompileError::InvalidExpression(
716                    "tuple/list expressions are only valid in an IN predicate".to_string(),
717                ));
718            }
719
720            // === INTERVAL ===
721            Expression::IntervalLiteral(interval) => {
722                let s = format!("{} {}", interval.quantity, interval.unit);
723                builder.emit(Op::LoadConst(Value::Text(SmartString::from_string(s))));
724            }
725
726            // === SUBQUERIES ===
727            Expression::ScalarSubquery(_) | Expression::Exists(_) | Expression::AllAny(_) => {
728                return Err(CompileError::UnsupportedExpression(
729                    "subqueries must be resolved by the query executor before VM compilation"
730                        .to_string(),
731                ));
732            }
733
734            // === WINDOW (not supported in VM, requires special handling) ===
735            Expression::Window(_) => {
736                return Err(CompileError::UnsupportedExpression(
737                    "Window functions require special execution context".to_string(),
738                ));
739            }
740
741            // === TABLE SOURCES (not expressions) ===
742            Expression::TableSource(_)
743            | Expression::JoinSource(_)
744            | Expression::SubquerySource(_)
745            | Expression::ValuesSource(_)
746            | Expression::CteReference(_)
747            | Expression::FunctionTableSource(_)
748            | Expression::Star(_)
749            | Expression::QualifiedStar(_)
750            | Expression::Default(_) => {
751                return Err(CompileError::InvalidExpression(
752                    "unexpected table reference or '*' in expression context".to_string(),
753                ));
754            }
755        }
756
757        Ok(())
758    }
759
760    /// Compile an infix expression
761    fn compile_infix(
762        &self,
763        infix: &InfixExpression,
764        builder: &mut ProgramBuilder,
765    ) -> Result<(), CompileError> {
766        match infix.op_type {
767            // Short-circuit AND
768            InfixOperator::And => {
769                // Compile left side
770                self.compile_expr(&infix.left, builder)?;
771
772                // Emit AND with placeholder jump target
773                let and_pos = builder.position();
774                builder.emit(Op::And(0)); // Placeholder
775
776                // Compile right side
777                self.compile_expr(&infix.right, builder)?;
778
779                // Emit finalize
780                builder.emit(Op::AndFinalize);
781
782                // Patch jump to skip right side if left is false
783                let end_pos = builder.position();
784                builder.patch_jump(and_pos as usize, end_pos);
785            }
786
787            // Short-circuit OR
788            InfixOperator::Or => {
789                // Compile left side
790                self.compile_expr(&infix.left, builder)?;
791
792                // Emit OR with placeholder jump target
793                let or_pos = builder.position();
794                builder.emit(Op::Or(0)); // Placeholder
795
796                // Compile right side
797                self.compile_expr(&infix.right, builder)?;
798
799                // Emit finalize
800                builder.emit(Op::OrFinalize);
801
802                // Patch jump to skip right side if left is true
803                let end_pos = builder.position();
804                builder.patch_jump(or_pos as usize, end_pos);
805            }
806
807            // Comparison operators
808            InfixOperator::Equal => {
809                self.compile_expr(&infix.left, builder)?;
810                self.compile_expr(&infix.right, builder)?;
811                builder.emit(Op::Eq);
812            }
813
814            InfixOperator::NotEqual => {
815                self.compile_expr(&infix.left, builder)?;
816                self.compile_expr(&infix.right, builder)?;
817                builder.emit(Op::Ne);
818            }
819
820            InfixOperator::LessThan => {
821                self.compile_expr(&infix.left, builder)?;
822                self.compile_expr(&infix.right, builder)?;
823                builder.emit(Op::Lt);
824            }
825
826            InfixOperator::LessEqual => {
827                self.compile_expr(&infix.left, builder)?;
828                self.compile_expr(&infix.right, builder)?;
829                builder.emit(Op::Le);
830            }
831
832            InfixOperator::GreaterThan => {
833                self.compile_expr(&infix.left, builder)?;
834                self.compile_expr(&infix.right, builder)?;
835                builder.emit(Op::Gt);
836            }
837
838            InfixOperator::GreaterEqual => {
839                self.compile_expr(&infix.left, builder)?;
840                self.compile_expr(&infix.right, builder)?;
841                builder.emit(Op::Ge);
842            }
843
844            // Arithmetic operators
845            InfixOperator::Add => {
846                self.compile_expr(&infix.left, builder)?;
847                self.compile_expr(&infix.right, builder)?;
848                builder.emit(Op::Add);
849            }
850
851            InfixOperator::Subtract => {
852                self.compile_expr(&infix.left, builder)?;
853                self.compile_expr(&infix.right, builder)?;
854                builder.emit(Op::Sub);
855            }
856
857            InfixOperator::Multiply => {
858                self.compile_expr(&infix.left, builder)?;
859                self.compile_expr(&infix.right, builder)?;
860                builder.emit(Op::Mul);
861            }
862
863            InfixOperator::Divide => {
864                self.compile_expr(&infix.left, builder)?;
865                self.compile_expr(&infix.right, builder)?;
866                builder.emit(Op::Div);
867            }
868
869            InfixOperator::Modulo => {
870                self.compile_expr(&infix.left, builder)?;
871                self.compile_expr(&infix.right, builder)?;
872                builder.emit(Op::Mod);
873            }
874
875            // String concatenation - optimize chained || into single ConcatN
876            InfixOperator::Concat => {
877                // Flatten chained concatenations: a || b || c -> ConcatN(3)
878                let mut operands = Vec::new();
879                Self::flatten_concat_chain_infix(infix, &mut operands);
880
881                if operands.len() > 2 && operands.len() <= 255 {
882                    // Compile all operands in order
883                    for operand in &operands {
884                        self.compile_expr(operand, builder)?;
885                    }
886                    builder.emit(Op::ConcatN(operands.len() as u8));
887                } else {
888                    // Fallback to binary concat
889                    self.compile_expr(&infix.left, builder)?;
890                    self.compile_expr(&infix.right, builder)?;
891                    builder.emit(Op::Concat);
892                }
893            }
894
895            // Bitwise operators
896            InfixOperator::BitwiseAnd => {
897                self.compile_expr(&infix.left, builder)?;
898                self.compile_expr(&infix.right, builder)?;
899                builder.emit(Op::BitAnd);
900            }
901
902            InfixOperator::BitwiseOr => {
903                self.compile_expr(&infix.left, builder)?;
904                self.compile_expr(&infix.right, builder)?;
905                builder.emit(Op::BitOr);
906            }
907
908            InfixOperator::BitwiseXor => {
909                self.compile_expr(&infix.left, builder)?;
910                self.compile_expr(&infix.right, builder)?;
911                builder.emit(Op::BitXor);
912            }
913
914            InfixOperator::LeftShift => {
915                self.compile_expr(&infix.left, builder)?;
916                self.compile_expr(&infix.right, builder)?;
917                builder.emit(Op::Shl);
918            }
919
920            InfixOperator::RightShift => {
921                self.compile_expr(&infix.left, builder)?;
922                self.compile_expr(&infix.right, builder)?;
923                builder.emit(Op::Shr);
924            }
925
926            // XOR
927            InfixOperator::Xor => {
928                self.compile_expr(&infix.left, builder)?;
929                self.compile_expr(&infix.right, builder)?;
930                builder.emit(Op::Xor);
931            }
932
933            // IS / IS NOT
934            InfixOperator::Is => {
935                self.compile_expr(&infix.left, builder)?;
936                // Check if right side is NULL, TRUE, or FALSE
937                match &*infix.right {
938                    Expression::NullLiteral(_) => {
939                        builder.emit(Op::IsNull);
940                    }
941                    Expression::BooleanLiteral(lit) if lit.value => {
942                        builder.emit(Op::IsTrue);
943                    }
944                    Expression::BooleanLiteral(lit) if !lit.value => {
945                        builder.emit(Op::IsFalse);
946                    }
947                    Expression::Identifier(id) if id.value_lower == "true" => {
948                        builder.emit(Op::IsTrue);
949                    }
950                    Expression::Identifier(id) if id.value_lower == "false" => {
951                        builder.emit(Op::IsFalse);
952                    }
953                    _ => {
954                        self.compile_expr(&infix.right, builder)?;
955                        builder.emit(Op::IsNotDistinctFrom);
956                    }
957                }
958            }
959
960            InfixOperator::IsNot => {
961                self.compile_expr(&infix.left, builder)?;
962                match &*infix.right {
963                    Expression::NullLiteral(_) => {
964                        builder.emit(Op::IsNotNull);
965                    }
966                    Expression::BooleanLiteral(lit) if lit.value => {
967                        builder.emit(Op::IsNotTrue);
968                    }
969                    Expression::BooleanLiteral(lit) if !lit.value => {
970                        builder.emit(Op::IsNotFalse);
971                    }
972                    Expression::Identifier(id) if id.value_lower == "true" => {
973                        builder.emit(Op::IsNotTrue);
974                    }
975                    Expression::Identifier(id) if id.value_lower == "false" => {
976                        builder.emit(Op::IsNotFalse);
977                    }
978                    _ => {
979                        self.compile_expr(&infix.right, builder)?;
980                        builder.emit(Op::IsDistinctFrom);
981                    }
982                }
983            }
984
985            InfixOperator::IsDistinctFrom => {
986                self.compile_expr(&infix.left, builder)?;
987                self.compile_expr(&infix.right, builder)?;
988                builder.emit(Op::IsDistinctFrom);
989            }
990
991            InfixOperator::IsNotDistinctFrom => {
992                self.compile_expr(&infix.left, builder)?;
993                self.compile_expr(&infix.right, builder)?;
994                builder.emit(Op::IsNotDistinctFrom);
995            }
996
997            // Pattern matching via infix
998            InfixOperator::Like => {
999                self.compile_expr(&infix.left, builder)?;
1000                let pattern_str = Self::extract_pattern_string(&infix.right);
1001                if let Some(s) = pattern_str {
1002                    let pattern = CompiledPattern::compile(&s, false)
1003                        .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1004                    builder.emit(Op::Like(Arc::new(pattern), false));
1005                } else {
1006                    self.compile_expr(&infix.right, builder)?;
1007                    builder.emit(Op::LikeDynamic(false));
1008                }
1009            }
1010
1011            InfixOperator::ILike => {
1012                self.compile_expr(&infix.left, builder)?;
1013                let pattern_str = Self::extract_pattern_string(&infix.right);
1014                if let Some(s) = pattern_str {
1015                    let pattern = CompiledPattern::compile(&s, true)
1016                        .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1017                    builder.emit(Op::Like(Arc::new(pattern), true));
1018                } else {
1019                    self.compile_expr(&infix.right, builder)?;
1020                    builder.emit(Op::LikeDynamic(true));
1021                }
1022            }
1023
1024            InfixOperator::NotLike => {
1025                self.compile_expr(&infix.left, builder)?;
1026                let pattern_str = Self::extract_pattern_string(&infix.right);
1027                if let Some(s) = pattern_str {
1028                    let pattern = CompiledPattern::compile(&s, false)
1029                        .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1030                    builder.emit(Op::Like(Arc::new(pattern), false));
1031                    builder.emit(Op::Not);
1032                } else {
1033                    self.compile_expr(&infix.right, builder)?;
1034                    builder.emit(Op::LikeDynamic(false));
1035                    builder.emit(Op::Not);
1036                }
1037            }
1038
1039            InfixOperator::NotILike => {
1040                self.compile_expr(&infix.left, builder)?;
1041                let pattern_str = Self::extract_pattern_string(&infix.right);
1042                if let Some(s) = pattern_str {
1043                    let pattern = CompiledPattern::compile(&s, true)
1044                        .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1045                    builder.emit(Op::Like(Arc::new(pattern), true));
1046                    builder.emit(Op::Not);
1047                } else {
1048                    self.compile_expr(&infix.right, builder)?;
1049                    builder.emit(Op::LikeDynamic(true));
1050                    builder.emit(Op::Not);
1051                }
1052            }
1053
1054            InfixOperator::Glob | InfixOperator::NotGlob => {
1055                self.compile_expr(&infix.left, builder)?;
1056                let pattern_str = Self::extract_pattern_string(&infix.right);
1057                if let Some(s) = pattern_str {
1058                    let pattern = CompiledPattern::compile_glob(&s)
1059                        .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1060                    builder.emit(Op::Glob(Arc::new(pattern)));
1061                } else {
1062                    self.compile_expr(&infix.right, builder)?;
1063                    builder.emit(Op::GlobDynamic);
1064                }
1065                if matches!(infix.op_type, InfixOperator::NotGlob) {
1066                    builder.emit(Op::Not);
1067                }
1068            }
1069
1070            InfixOperator::Regexp | InfixOperator::NotRegexp => {
1071                self.compile_expr(&infix.left, builder)?;
1072                let pattern_str = Self::extract_pattern_string(&infix.right);
1073                if let Some(s) = pattern_str {
1074                    let regex = regex::Regex::new(&s).map_err(|e| {
1075                        CompileError::InvalidExpression(format!("Invalid regex: {}", e))
1076                    })?;
1077                    builder.emit(Op::Regexp(Arc::new(regex)));
1078                } else {
1079                    self.compile_expr(&infix.right, builder)?;
1080                    builder.emit(Op::RegexpDynamic);
1081                }
1082                if matches!(infix.op_type, InfixOperator::NotRegexp) {
1083                    builder.emit(Op::Not);
1084                }
1085            }
1086
1087            // JSON operators
1088            InfixOperator::JsonAccess => {
1089                // json -> key (returns JSON)
1090                self.compile_expr(&infix.left, builder)?;
1091                self.compile_expr(&infix.right, builder)?;
1092                builder.emit(Op::JsonAccess);
1093            }
1094
1095            InfixOperator::JsonAccessText => {
1096                // json ->> key (returns TEXT)
1097                self.compile_expr(&infix.left, builder)?;
1098                self.compile_expr(&infix.right, builder)?;
1099                builder.emit(Op::JsonAccessText);
1100            }
1101
1102            InfixOperator::Index => {
1103                // Array/JSON index access - treat as JsonAccess
1104                self.compile_expr(&infix.left, builder)?;
1105                self.compile_expr(&infix.right, builder)?;
1106                builder.emit(Op::JsonAccess);
1107            }
1108
1109            // Vector distance operator (<=>)
1110            InfixOperator::VectorDistance => {
1111                self.compile_expr(&infix.left, builder)?;
1112                self.compile_expr(&infix.right, builder)?;
1113                builder.emit(Op::VectorDistanceL2);
1114            }
1115
1116            // Other/unknown operators
1117            InfixOperator::Other => {
1118                self.compile_expr(&infix.left, builder)?;
1119                self.compile_expr(&infix.right, builder)?;
1120                let name = format!(
1121                    "{}{}",
1122                    crate::context::STORED_OPERATOR_CALL_PREFIX,
1123                    infix.operator
1124                );
1125                builder.emit(Op::CallStored {
1126                    name: CompactArc::from(name.as_str()),
1127                    arg_count: 2,
1128                });
1129            }
1130        }
1131
1132        Ok(())
1133    }
1134
1135    /// Compile a prefix expression
1136    fn compile_prefix(
1137        &self,
1138        prefix: &PrefixExpression,
1139        builder: &mut ProgramBuilder,
1140    ) -> Result<(), CompileError> {
1141        self.compile_expr(&prefix.right, builder)?;
1142
1143        match prefix.operator.to_uppercase().as_str() {
1144            "NOT" => builder.emit(Op::Not),
1145            "-" => builder.emit(Op::Neg),
1146            "+" => {} // Unary plus is a no-op
1147            "~" => builder.emit(Op::BitNot),
1148            _ => {
1149                return Err(CompileError::InvalidExpression(format!(
1150                    "Unknown prefix operator: {}",
1151                    prefix.operator
1152                )));
1153            }
1154        }
1155
1156        Ok(())
1157    }
1158
1159    /// Compile an IN expression
1160    fn compile_in(
1161        &self,
1162        in_expr: &InExpression,
1163        builder: &mut ProgramBuilder,
1164    ) -> Result<(), CompileError> {
1165        // Check if this is a multi-column IN: (a, b) IN ((1, 2), (3, 4))
1166        let left_columns: Vec<&Expression> = match &*in_expr.left {
1167            Expression::List(list) if list.elements.len() > 1 => list.elements.iter().collect(),
1168            Expression::ExpressionList(list) if list.expressions.len() > 1 => {
1169                list.expressions.iter().collect()
1170            }
1171            _ => Vec::new(),
1172        };
1173
1174        if !left_columns.is_empty() {
1175            // Multi-column IN expression
1176            return self.compile_multi_column_in(in_expr, &left_columns, builder);
1177        }
1178
1179        // Single-value IN expression
1180        // Build the set of values at compile time if possible
1181        let mut values = ValueSet::default();
1182        let mut has_null = false;
1183        let mut all_constant = true;
1184
1185        // Check if right side is a list of constants
1186        match &*in_expr.right {
1187            Expression::List(list) => {
1188                for item in &list.elements {
1189                    if let Some(value) = try_eval_constant(item) {
1190                        if value.is_null() {
1191                            has_null = true;
1192                        } else {
1193                            values.insert(value);
1194                        }
1195                    } else {
1196                        all_constant = false;
1197                        break;
1198                    }
1199                }
1200            }
1201            Expression::ExpressionList(list) => {
1202                for item in &list.expressions {
1203                    if let Some(value) = try_eval_constant(item) {
1204                        if value.is_null() {
1205                            has_null = true;
1206                        } else {
1207                            values.insert(value);
1208                        }
1209                    } else {
1210                        all_constant = false;
1211                        break;
1212                    }
1213                }
1214            }
1215            _ => {
1216                all_constant = false;
1217            }
1218        }
1219
1220        if all_constant {
1221            if values.is_empty() && !has_null {
1222                // Empty IN list with no NULLs:
1223                // x IN () -> FALSE (nothing matches)
1224                // x NOT IN () -> TRUE (x is not in empty set)
1225                if in_expr.not {
1226                    builder.emit(Op::LoadConst(Value::Boolean(true)));
1227                } else {
1228                    builder.emit(Op::LoadConst(Value::Boolean(false)));
1229                }
1230            } else {
1231                // Optimized: use pre-built HashSet
1232                self.compile_expr(&in_expr.left, builder)?;
1233                if in_expr.not {
1234                    builder.emit(Op::NotInSet(CompactArc::new(values), has_null));
1235                } else {
1236                    builder.emit(Op::InSet(CompactArc::new(values), has_null));
1237                }
1238            }
1239        } else {
1240            // Fallback: evaluate each item (less efficient)
1241            // For now, return error - would need runtime set building
1242            return Err(CompileError::UnsupportedExpression(
1243                "Dynamic IN list not yet supported in VM".to_string(),
1244            ));
1245        }
1246
1247        Ok(())
1248    }
1249
1250    /// Compile multi-column IN expression: (a, b) IN ((1, 2), (3, 4))
1251    fn compile_multi_column_in(
1252        &self,
1253        in_expr: &InExpression,
1254        left_columns: &[&Expression],
1255        builder: &mut ProgramBuilder,
1256    ) -> Result<(), CompileError> {
1257        let tuple_size = left_columns.len();
1258
1259        // Extract tuples from right side
1260        let mut tuple_values: Vec<Vec<Value>> = Vec::new();
1261        let mut all_constant = true;
1262
1263        match &*in_expr.right {
1264            Expression::List(list) => {
1265                for item in &list.elements {
1266                    if let Some(tuple) = self.extract_tuple_values(item, tuple_size) {
1267                        tuple_values.push(tuple);
1268                    } else {
1269                        all_constant = false;
1270                        break;
1271                    }
1272                }
1273            }
1274            Expression::ExpressionList(list) => {
1275                for item in &list.expressions {
1276                    if let Some(tuple) = self.extract_tuple_values(item, tuple_size) {
1277                        tuple_values.push(tuple);
1278                    } else {
1279                        all_constant = false;
1280                        break;
1281                    }
1282                }
1283            }
1284            _ => {
1285                all_constant = false;
1286            }
1287        }
1288
1289        if !all_constant || tuple_values.is_empty() {
1290            return Err(CompileError::UnsupportedExpression(
1291                "Dynamic multi-column IN not yet supported in VM".to_string(),
1292            ));
1293        }
1294
1295        // Compile each column expression to push onto stack
1296        for col in left_columns {
1297            self.compile_expr(col, builder)?;
1298        }
1299
1300        // Emit InTupleSet operation
1301        let tuple_size = u8::try_from(tuple_size).map_err(|_| {
1302            CompileError::InvalidExpression("tuple arity exceeds the u8 bytecode limit".to_string())
1303        })?;
1304        builder.emit(Op::InTupleSet {
1305            tuple_size,
1306            values: Arc::new(tuple_values),
1307            negated: in_expr.not,
1308        });
1309
1310        Ok(())
1311    }
1312
1313    /// Extract tuple values from an expression (e.g., (1, 2) -> [1, 2])
1314    fn extract_tuple_values(&self, expr: &Expression, expected_size: usize) -> Option<Vec<Value>> {
1315        let elements: Vec<&Expression> = match expr {
1316            Expression::List(list) => list.elements.iter().collect(),
1317            Expression::ExpressionList(list) => list.expressions.iter().collect(),
1318            _ => return None,
1319        };
1320
1321        if elements.len() != expected_size {
1322            return None;
1323        }
1324
1325        let mut values = Vec::with_capacity(expected_size);
1326        for element in elements {
1327            let value = try_eval_constant(element)?;
1328            values.push(value);
1329        }
1330
1331        Some(values)
1332    }
1333
1334    /// Compile a LIKE expression
1335    fn compile_like(
1336        &self,
1337        like: &LikeExpression,
1338        builder: &mut ProgramBuilder,
1339    ) -> Result<(), CompileError> {
1340        self.compile_expr(&like.left, builder)?;
1341
1342        // Determine case sensitivity and negation from operator
1343        let op_upper = like.operator.to_uppercase();
1344        let case_insensitive = op_upper.contains("ILIKE");
1345        let negated = op_upper.contains("NOT");
1346        let is_glob = op_upper.contains("GLOB");
1347        let is_regexp = op_upper.contains("REGEXP") || op_upper.contains("RLIKE");
1348
1349        // Extract escape character if present
1350        let escape_char: Option<char> = if let Some(ref escape_expr) = like.escape {
1351            if let Expression::StringLiteral(lit) = &**escape_expr {
1352                let mut chars = lit.value.chars();
1353                let first = chars.next().ok_or_else(|| {
1354                    CompileError::InvalidExpression(
1355                        "LIKE ESCAPE must contain exactly one character".to_string(),
1356                    )
1357                })?;
1358                if chars.next().is_some() {
1359                    return Err(CompileError::InvalidExpression(
1360                        "LIKE ESCAPE must contain exactly one character".to_string(),
1361                    ));
1362                }
1363                Some(first)
1364            } else {
1365                return Err(CompileError::InvalidExpression(
1366                    "LIKE ESCAPE must be a string literal".to_string(),
1367                ));
1368            }
1369        } else {
1370            None
1371        };
1372
1373        // Try to compile pattern at compile time
1374        let pattern_str = Self::extract_pattern_string(&like.pattern);
1375        if let Some(s) = pattern_str {
1376            if is_regexp {
1377                let regex = regex::Regex::new(&s).map_err(|e| {
1378                    CompileError::InvalidExpression(format!("Invalid regex: {}", e))
1379                })?;
1380                builder.emit(Op::Regexp(Arc::new(regex)));
1381            } else if is_glob {
1382                // Use compile_glob for GLOB patterns (uses * and ? wildcards)
1383                let pattern = CompiledPattern::compile_glob(&s)
1384                    .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1385                builder.emit(Op::Glob(Arc::new(pattern)));
1386            } else if let Some(esc) = escape_char {
1387                // LIKE with ESCAPE - pre-process pattern to handle escape character
1388                let processed_pattern = self.process_like_escape(&s, esc);
1389                let pattern = CompiledPattern::compile(&processed_pattern, case_insensitive)
1390                    .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1391                builder.emit(Op::LikeEscape(Arc::new(pattern), case_insensitive, esc));
1392            } else {
1393                let pattern = CompiledPattern::compile(&s, case_insensitive)
1394                    .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1395                builder.emit(Op::Like(Arc::new(pattern), case_insensitive));
1396            }
1397
1398            if negated {
1399                builder.emit(Op::Not);
1400            }
1401        } else {
1402            // Dynamic pattern (e.g. parameter $1) — compile the pattern expression
1403            // onto the stack and use the dynamic op
1404            self.compile_expr(&like.pattern, builder)?;
1405            if is_regexp {
1406                builder.emit(Op::RegexpDynamic);
1407            } else if is_glob {
1408                builder.emit(Op::GlobDynamic);
1409            } else if let Some(esc) = escape_char {
1410                builder.emit(Op::LikeDynamicEscape(case_insensitive, esc));
1411            } else {
1412                builder.emit(Op::LikeDynamic(case_insensitive));
1413            }
1414            if negated {
1415                builder.emit(Op::Not);
1416            }
1417        }
1418
1419        Ok(())
1420    }
1421
1422    /// Extract a static pattern string from a StringLiteral or a double-quoted Identifier.
1423    /// Returns None for dynamic expressions (column references, function calls, etc.).
1424    fn extract_pattern_string(expr: &Expression) -> Option<SmartString> {
1425        match expr {
1426            Expression::StringLiteral(lit) => Some(lit.value.clone()),
1427            Expression::Identifier(id) if id.token.quoted => Some(id.value.clone()),
1428            _ => None,
1429        }
1430    }
1431
1432    /// Process LIKE pattern with escape character
1433    /// Converts escaped wildcards to special markers and then to literal characters
1434    fn process_like_escape(&self, pattern: &str, escape: char) -> String {
1435        let mut result = String::with_capacity(pattern.len());
1436        let mut chars = pattern.chars().peekable();
1437
1438        while let Some(c) = chars.next() {
1439            if c == escape {
1440                // Next character should be treated literally
1441                if let Some(&next) = chars.peek() {
1442                    if next == '%' || next == '_' || next == escape {
1443                        // Escape the wildcard - use regex escape sequence
1444                        result.push('\\');
1445                        result.push(chars.next().unwrap());
1446                    } else {
1447                        // Not escaping a special character, keep the escape char
1448                        result.push(c);
1449                    }
1450                } else {
1451                    // Escape at end of pattern
1452                    result.push(c);
1453                }
1454            } else {
1455                result.push(c);
1456            }
1457        }
1458
1459        result
1460    }
1461
1462    /// Compile a CASE expression
1463    fn compile_case(
1464        &self,
1465        case: &CaseExpression,
1466        builder: &mut ProgramBuilder,
1467    ) -> Result<(), CompileError> {
1468        builder.emit(Op::CaseStart);
1469
1470        let is_simple = case.value.is_some();
1471        let mut end_jumps = Vec::new();
1472
1473        // For simple CASE, compile the operand once
1474        if let Some(ref operand) = case.value {
1475            self.compile_expr(operand, builder)?;
1476        }
1477
1478        for when_clause in &case.when_clauses {
1479            if is_simple {
1480                // Simple CASE: compare operand with WHEN value
1481                builder.emit(Op::Dup); // Keep operand on stack
1482                self.compile_expr(&when_clause.condition, builder)?;
1483                builder.emit(Op::CaseCompare);
1484            } else {
1485                // Searched CASE: evaluate condition
1486                self.compile_expr(&when_clause.condition, builder)?;
1487            }
1488
1489            // Jump to next branch if condition is false
1490            let when_pos = builder.position();
1491            builder.emit(Op::CaseWhen(0)); // Placeholder
1492
1493            // Compile THEN result
1494            if is_simple {
1495                builder.emit(Op::Pop); // Remove operand copy
1496            }
1497            self.compile_expr(&when_clause.then_result, builder)?;
1498
1499            // Jump to end after THEN
1500            let then_pos = builder.position();
1501            builder.emit(Op::CaseThen(0)); // Placeholder
1502            end_jumps.push(then_pos);
1503
1504            // Patch WHEN jump to here
1505            let next_pos = builder.position();
1506            builder.patch_jump(when_pos as usize, next_pos);
1507        }
1508
1509        // Compile ELSE
1510        if is_simple {
1511            builder.emit(Op::Pop); // Remove operand
1512        }
1513        if let Some(ref else_value) = case.else_value {
1514            builder.emit(Op::CaseElse);
1515            self.compile_expr(else_value, builder)?;
1516        } else {
1517            builder.emit(Op::LoadNull(DataType::Null));
1518        }
1519
1520        // Patch all THEN jumps to end
1521        let end_pos = builder.position();
1522        builder.emit(Op::CaseEnd);
1523
1524        for pos in end_jumps {
1525            builder.patch_jump(pos as usize, end_pos);
1526        }
1527
1528        Ok(())
1529    }
1530
1531    /// Compile a function call
1532    fn compile_function(
1533        &self,
1534        func: &FunctionCall,
1535        builder: &mut ProgramBuilder,
1536    ) -> Result<(), CompileError> {
1537        let func_name = func.function.to_uppercase();
1538
1539        if func.is_distinct || !func.order_by.is_empty() || func.filter.is_some() {
1540            return Err(CompileError::InvalidExpression(format!(
1541                "DISTINCT, ORDER BY, and FILTER modifiers require an aggregate function; {func_name} is being compiled as a scalar function"
1542            )));
1543        }
1544
1545        if let Some(info) = self.ctx.functions.get_info(&func_name) {
1546            info.signature
1547                .validate_arg_count(func.arguments.len())
1548                .map_err(|error| CompileError::InvalidExpression(error.to_string()))?;
1549        }
1550
1551        // Special handling for certain functions
1552        match func_name.as_str() {
1553            "NOW" | "CURRENT_TIMESTAMP" => {
1554                builder.emit(Op::LoadNamedParam(CompactArc::from(
1555                    "CURRENT_STATEMENT_TIMESTAMP",
1556                )));
1557                return Ok(());
1558            }
1559            "CURRENT_TRANSACTION_ID" => {
1560                // Context-dependent function - loads from ExecuteContext
1561                builder.emit(Op::LoadTransactionId);
1562                return Ok(());
1563            }
1564
1565            "IIF" => {
1566                if func.arguments.len() != 3 {
1567                    return Err(CompileError::InvalidExpression(
1568                        "IIF requires exactly 3 arguments".to_string(),
1569                    ));
1570                }
1571
1572                self.compile_expr(&func.arguments[0], builder)?;
1573                let false_jump = builder.position();
1574                builder.emit(Op::PopJumpIfFalse(0));
1575                self.compile_expr(&func.arguments[1], builder)?;
1576                let end_jump = builder.position();
1577                builder.emit(Op::Jump(0));
1578
1579                let false_pos = builder.position();
1580                builder.patch_jump(false_jump as usize, false_pos);
1581                self.compile_expr(&func.arguments[2], builder)?;
1582                let end_pos = builder.position();
1583                builder.patch_jump(end_jump as usize, end_pos);
1584                return Ok(());
1585            }
1586
1587            "COALESCE" => {
1588                // Short-circuit COALESCE: stop evaluation as soon as we find non-null
1589                // Bytecode pattern:
1590                //   Eval(Arg1)
1591                //   JumpIfNotNull(End)  // If not null, jump to end (keep value)
1592                //   Pop                  // Pop the null value
1593                //   Eval(Arg2)
1594                //   JumpIfNotNull(End)
1595                //   Pop
1596                //   ...
1597                //   Eval(ArgN)          // Last arg: keep on stack (null or not)
1598                //   Label(End)
1599                if func.arguments.is_empty() {
1600                    builder.emit(Op::LoadNull(DataType::Null));
1601                    return Ok(());
1602                }
1603
1604                let mut jump_positions = Vec::new();
1605                let last_idx = func.arguments.len() - 1;
1606
1607                for (i, arg) in func.arguments.iter().enumerate() {
1608                    self.compile_expr(arg, builder)?;
1609
1610                    if i < last_idx {
1611                        // For all but last: jump to end if not null, else pop and continue
1612                        let jump_pos = builder.position();
1613                        builder.emit(Op::JumpIfNotNull(0)); // Placeholder, will patch
1614                        jump_positions.push(jump_pos);
1615                        builder.emit(Op::Pop); // Pop the null value
1616                    }
1617                    // Last argument: just leave on stack
1618                }
1619
1620                // Patch all jumps to point to end
1621                let end_pos = builder.position();
1622                for pos in jump_positions {
1623                    builder.patch_jump(pos as usize, end_pos);
1624                }
1625
1626                return Ok(());
1627            }
1628
1629            "NULLIF" if func.arguments.len() == 2 => {
1630                self.compile_expr(&func.arguments[0], builder)?;
1631                self.compile_expr(&func.arguments[1], builder)?;
1632                builder.emit(Op::NullIf);
1633                return Ok(());
1634            }
1635
1636            "GREATEST" => {
1637                let arg_count = u8::try_from(func.arguments.len()).map_err(|_| {
1638                    CompileError::InvalidExpression(
1639                        "GREATEST arity exceeds the u8 bytecode limit".to_string(),
1640                    )
1641                })?;
1642                for arg in &func.arguments {
1643                    self.compile_expr(arg, builder)?;
1644                }
1645                builder.emit(Op::Greatest(arg_count));
1646                return Ok(());
1647            }
1648
1649            "LEAST" => {
1650                let arg_count = u8::try_from(func.arguments.len()).map_err(|_| {
1651                    CompileError::InvalidExpression(
1652                        "LEAST arity exceeds the u8 bytecode limit".to_string(),
1653                    )
1654                })?;
1655                for arg in &func.arguments {
1656                    self.compile_expr(arg, builder)?;
1657                }
1658                builder.emit(Op::Least(arg_count));
1659                return Ok(());
1660            }
1661
1662            _ => {}
1663        }
1664
1665        // Get function from registry
1666        if let Some(scalar_func) = self.ctx.functions.get_scalar(&func_name) {
1667            let arg_count = u8::try_from(func.arguments.len()).map_err(|_| {
1668                CompileError::InvalidExpression(
1669                    "function arity exceeds the u8 bytecode limit".to_string(),
1670                )
1671            })?;
1672            // Compile arguments
1673            for arg in &func.arguments {
1674                self.compile_expr(arg, builder)?;
1675            }
1676
1677            // Try native function pointer for single-arg functions (no dynamic dispatch)
1678            if func.arguments.len() == 1 {
1679                if let Some(native_fn) = scalar_func.native_fn1() {
1680                    builder.emit(Op::NativeFn1(native_fn));
1681                    return Ok(());
1682                }
1683            }
1684
1685            // Fallback to dynamic dispatch
1686            builder.emit(Op::CallScalar {
1687                func: scalar_func.into(),
1688                arg_count,
1689            });
1690            Ok(())
1691        } else {
1692            let arg_count = u8::try_from(func.arguments.len()).map_err(|_| {
1693                CompileError::InvalidExpression(
1694                    "stored function arity exceeds the u8 bytecode limit".to_string(),
1695                )
1696            })?;
1697            for argument in &func.arguments {
1698                self.compile_expr(argument, builder)?;
1699            }
1700            builder.emit(Op::CallStored {
1701                name: CompactArc::from(func.function.as_str()),
1702                arg_count,
1703            });
1704            Ok(())
1705        }
1706    }
1707}
1708
1709// ============================================================================
1710// HELPER FUNCTIONS
1711// ============================================================================
1712
1713/// Check if a function must NOT be constant-folded.
1714///
1715/// Looks up the function in the global registry and checks `FunctionInfo.deterministic`.
1716/// Functions not in the registry (CURRENT_TRANSACTION_ID, UUID, RAND — handled as
1717/// special compiler ops or aliases) are hardcoded here.
1718///
1719/// Also used by `query_classification.rs` to detect non-deterministic functions
1720/// for semantic cache bypass, and by `evaluator_bridge.rs` to reject pushdown
1721/// of expressions containing non-deterministic functions.
1722pub fn is_non_foldable_function(name: &str) -> bool {
1723    is_non_foldable_function_with_registry(name, radixdb_functions::registry::global_registry())
1724}
1725
1726#[inline]
1727fn is_non_foldable_function_with_registry(
1728    name: &str,
1729    registry: &radixdb_functions::FunctionRegistry,
1730) -> bool {
1731    !registry.is_deterministic(name)
1732}
1733
1734/// Check if an expression is column-free AND non-trivial (worth folding).
1735/// Returns true for expressions like `NOW()`, `1 + 2`, `NOW() - INTERVAL '24 hours'`
1736/// that can be evaluated once at compile time instead of per-row.
1737/// Simple literals return false (already handled efficiently by LoadConst).
1738fn is_foldable_expr(expr: &Expression, registry: &radixdb_functions::FunctionRegistry) -> bool {
1739    match expr {
1740        // Simple literals are already constants — no folding benefit
1741        Expression::IntegerLiteral(_)
1742        | Expression::FloatLiteral(_)
1743        | Expression::StringLiteral(_)
1744        | Expression::BooleanLiteral(_)
1745        | Expression::NullLiteral(_)
1746        | Expression::BoundValue(_) => false,
1747
1748        // INTERVAL literals alone are already LoadConst — no folding benefit
1749        Expression::IntervalLiteral(_) => false,
1750
1751        // Binary operations: foldable if BOTH sides are column-free
1752        Expression::Infix(infix) => {
1753            is_column_free(&infix.left, registry) && is_column_free(&infix.right, registry)
1754        }
1755
1756        // Unary operations: foldable if operand is column-free
1757        Expression::Prefix(prefix) => is_column_free(&prefix.right, registry),
1758
1759        // Function calls: foldable if ALL arguments are column-free
1760        // This covers NOW(), CURRENT_DATE, UPPER('text'), ABS(-5), etc.
1761        // Excludes context-dependent and non-deterministic-per-call functions
1762        Expression::FunctionCall(func) => {
1763            if is_non_foldable_function_with_registry(&func.function, registry) {
1764                return false;
1765            }
1766            func.arguments
1767                .iter()
1768                .all(|argument| is_column_free(argument, registry))
1769        }
1770
1771        // CAST: foldable if inner expression is column-free
1772        Expression::Cast(cast) => {
1773            !cast.type_name.contains('.') && is_column_free(&cast.expr, registry)
1774        }
1775
1776        // Everything else: not foldable
1777        _ => false,
1778    }
1779}
1780
1781/// Check if an expression references no columns (is entirely self-contained).
1782fn is_column_free(expr: &Expression, registry: &radixdb_functions::FunctionRegistry) -> bool {
1783    match expr {
1784        // Literals are always column-free
1785        Expression::IntegerLiteral(_)
1786        | Expression::FloatLiteral(_)
1787        | Expression::StringLiteral(_)
1788        | Expression::BooleanLiteral(_)
1789        | Expression::NullLiteral(_)
1790        | Expression::IntervalLiteral(_)
1791        | Expression::BoundValue(_) => true,
1792
1793        // Identifiers reference columns (not column-free)
1794        Expression::Identifier(_) | Expression::QualifiedIdentifier { .. } => false,
1795
1796        // Parameters: values aren't known at compile time
1797        Expression::Parameter(_) => false,
1798
1799        // Binary operations
1800        Expression::Infix(infix) => {
1801            is_column_free(&infix.left, registry) && is_column_free(&infix.right, registry)
1802        }
1803
1804        // Unary operations
1805        Expression::Prefix(prefix) => is_column_free(&prefix.right, registry),
1806
1807        // Function calls (NOW(), UPPER('text'), etc.)
1808        // Exclude context-dependent and non-deterministic-per-call functions
1809        Expression::FunctionCall(func) => {
1810            if is_non_foldable_function_with_registry(&func.function, registry) {
1811                return false;
1812            }
1813            func.arguments
1814                .iter()
1815                .all(|argument| is_column_free(argument, registry))
1816        }
1817
1818        // CAST
1819        Expression::Cast(cast) => {
1820            !cast.type_name.contains('.') && is_column_free(&cast.expr, registry)
1821        }
1822
1823        // CASE WHEN
1824        Expression::Case(case) => {
1825            case.value
1826                .as_ref()
1827                .is_none_or(|e| is_column_free(e, registry))
1828                && case.when_clauses.iter().all(|wc| {
1829                    is_column_free(&wc.condition, registry)
1830                        && is_column_free(&wc.then_result, registry)
1831                })
1832                && case
1833                    .else_value
1834                    .as_ref()
1835                    .is_none_or(|e| is_column_free(e, registry))
1836        }
1837
1838        // Subqueries, EXISTS — not column-free
1839        Expression::ScalarSubquery(_) | Expression::Exists(_) => false,
1840
1841        // Between
1842        Expression::Between(between) => {
1843            is_column_free(&between.expr, registry)
1844                && is_column_free(&between.lower, registry)
1845                && is_column_free(&between.upper, registry)
1846        }
1847
1848        // Anything else: conservatively assume it references columns
1849        _ => false,
1850    }
1851}
1852
1853/// Try to evaluate a constant expression at compile time
1854fn try_eval_constant(expr: &Expression) -> Option<Value> {
1855    match expr {
1856        Expression::IntegerLiteral(lit) => Some(Value::Integer(lit.value)),
1857        Expression::FloatLiteral(lit) => Some(Value::Float(lit.value)),
1858        Expression::StringLiteral(lit) => Some(Value::Text(lit.value.clone())),
1859        Expression::BooleanLiteral(lit) => Some(Value::Boolean(lit.value)),
1860        Expression::NullLiteral(_) => Some(Value::null_unknown()),
1861        Expression::BoundValue(value) => Some((**value).clone()),
1862        _ => None,
1863    }
1864}
1865
1866// Note: string_to_datatype and expression_to_string are now imported from utils
1867
1868#[cfg(test)]
1869mod tests {
1870    use super::*;
1871    use radixdb_sql::ast::IntegerLiteral;
1872    use radixdb_sql::token::{Position, Token, TokenType};
1873
1874    fn make_token() -> Token {
1875        Token {
1876            token_type: TokenType::Integer,
1877            literal: "1".into(),
1878            position: Position {
1879                offset: 0,
1880                line: 1,
1881                column: 1,
1882            },
1883            quoted: false,
1884        }
1885    }
1886
1887    #[test]
1888    fn test_compile_simple_comparison() {
1889        let columns = vec!["a".to_string(), "b".to_string()];
1890        let ctx = CompileContext::with_global_registry(&columns);
1891        let compiler = ExprCompiler::new(&ctx);
1892
1893        // a > 5
1894        let expr = Expression::Infix(InfixExpression {
1895            token: make_token(),
1896            left: Box::new(Expression::Identifier(Identifier::new(
1897                make_token(),
1898                "a".to_string(),
1899            ))),
1900            operator: ">".into(),
1901            op_type: InfixOperator::GreaterThan,
1902            right: Box::new(Expression::IntegerLiteral(IntegerLiteral {
1903                token: make_token(),
1904                value: 5,
1905            })),
1906        });
1907
1908        let program = compiler.compile(&expr).unwrap();
1909        assert!(!program.is_empty());
1910        println!("{}", program.disassemble());
1911    }
1912
1913    #[test]
1914    fn test_compile_and_expression() {
1915        let columns = vec!["a".to_string(), "b".to_string()];
1916        let ctx = CompileContext::with_global_registry(&columns);
1917        let compiler = ExprCompiler::new(&ctx);
1918
1919        // a > 5 AND b < 10
1920        let expr = Expression::Infix(InfixExpression {
1921            token: make_token(),
1922            left: Box::new(Expression::Infix(InfixExpression {
1923                token: make_token(),
1924                left: Box::new(Expression::Identifier(Identifier::new(
1925                    make_token(),
1926                    "a".to_string(),
1927                ))),
1928                operator: ">".into(),
1929                op_type: InfixOperator::GreaterThan,
1930                right: Box::new(Expression::IntegerLiteral(IntegerLiteral {
1931                    token: make_token(),
1932                    value: 5,
1933                })),
1934            })),
1935            operator: "AND".into(),
1936            op_type: InfixOperator::And,
1937            right: Box::new(Expression::Infix(InfixExpression {
1938                token: make_token(),
1939                left: Box::new(Expression::Identifier(Identifier::new(
1940                    make_token(),
1941                    "b".to_string(),
1942                ))),
1943                operator: "<".into(),
1944                op_type: InfixOperator::LessThan,
1945                right: Box::new(Expression::IntegerLiteral(IntegerLiteral {
1946                    token: make_token(),
1947                    value: 10,
1948                })),
1949            })),
1950        });
1951
1952        let program = compiler.compile(&expr).unwrap();
1953        assert!(!program.is_empty());
1954        println!("{}", program.disassemble());
1955    }
1956}