Skip to main content

vibesql_executor/insert/
defaults.rs

1use crate::errors::ExecutorError;
2
3/// Evaluate an INSERT expression to SqlValue
4/// Supports literals, DEFAULT keyword, procedural variables, and trigger pseudo-variables (OLD/NEW)
5#[allow(dead_code)]
6pub fn evaluate_insert_expression(
7    expr: &vibesql_ast::Expression,
8    column: &vibesql_catalog::ColumnSchema,
9    procedural_context: Option<&crate::procedural::ExecutionContext>,
10) -> Result<vibesql_types::SqlValue, ExecutorError> {
11    evaluate_insert_expression_with_trigger_context(
12        expr,
13        column,
14        procedural_context,
15        None,
16        None,
17        None,
18    )
19}
20
21/// Evaluate an INSERT expression with trigger context support
22/// This is used when executing INSERT statements inside trigger bodies
23///
24/// `cte_results` carries the enclosing INSERT statement's WITH-clause CTEs
25/// (if any) so subqueries in VALUES rows can reference CTE names, matching
26/// SQLite (issue #5359).
27pub fn evaluate_insert_expression_with_trigger_context(
28    expr: &vibesql_ast::Expression,
29    column: &vibesql_catalog::ColumnSchema,
30    procedural_context: Option<&crate::procedural::ExecutionContext>,
31    trigger_context: Option<&crate::trigger_execution::TriggerContext>,
32    database: Option<&vibesql_storage::Database>,
33    cte_results: Option<&std::collections::HashMap<String, crate::select::cte::CteResult>>,
34) -> Result<vibesql_types::SqlValue, ExecutorError> {
35    match expr {
36        vibesql_ast::Expression::Literal(lit) => Ok(lit.clone()),
37        vibesql_ast::Expression::Default => {
38            // Use column's default value, or NULL if no default is defined
39            if let Some(default_expr) = &column.default_value {
40                // Evaluate the default expression
41                evaluate_default_expression(default_expr)
42            } else {
43                // No default value defined, use NULL
44                Ok(vibesql_types::SqlValue::Null)
45            }
46        }
47        vibesql_ast::Expression::ColumnRef(col_id)
48            if col_id.schema_canonical().is_none() && col_id.table_canonical().is_none() =>
49        {
50            let col_name = col_id.column_canonical();
51            // Check if this is a procedural variable reference
52            if let Some(ctx) = procedural_context {
53                // Try to resolve as procedural variable
54                if let Some(value) = ctx.get_value(col_name) {
55                    return Ok(value.clone());
56                }
57            }
58            // Not a procedural variable, or no context provided
59            Err(ExecutorError::UnsupportedExpression(format!(
60                "Column reference '{}' not supported in INSERT VALUES. Did you mean to use a procedural variable?",
61                col_name
62            )))
63        }
64        vibesql_ast::Expression::PseudoVariable { .. } => {
65            // Pseudo-variables (OLD.x, NEW.y) require full expression evaluation with trigger
66            // context
67            if let (Some(ctx), Some(db)) = (trigger_context, database) {
68                // Create a dummy row for evaluation (pseudo-variables don't depend on current row)
69                let dummy_row = vibesql_storage::Row::new(vec![]);
70                let evaluator =
71                    crate::ExpressionEvaluator::with_trigger_context(ctx.table_schema, db, ctx);
72                evaluator.eval(expr, &dummy_row)
73            } else {
74                Err(ExecutorError::UnsupportedExpression(
75                    "Pseudo-variables (OLD/NEW) are only valid within trigger bodies".to_string(),
76                ))
77            }
78        }
79        _ => {
80            // For any other expression type, use full expression evaluator
81            if let (Some(ctx), Some(db)) = (trigger_context, database) {
82                // Create a dummy row for evaluation (trigger context available)
83                let dummy_row = vibesql_storage::Row::new(vec![]);
84                let mut evaluator =
85                    crate::ExpressionEvaluator::with_trigger_context(ctx.table_schema, db, ctx);
86                if let Some(ctes) = cte_results {
87                    evaluator = evaluator.with_cte_context(ctes);
88                }
89                evaluator.eval(expr, &dummy_row)
90            } else if let Some(db) = database {
91                // No trigger context, but we have database access - evaluate the expression
92                // Create a minimal dummy schema for expression evaluation
93                // INSERT value expressions don't reference columns from current row
94                let dummy_schema =
95                    vibesql_catalog::TableSchema::new("__insert_expr__".to_string(), vec![]);
96                let dummy_row = vibesql_storage::Row::new(vec![]);
97                let mut evaluator = crate::ExpressionEvaluator::with_database(&dummy_schema, db);
98                if let Some(ctes) = cte_results {
99                    evaluator = evaluator.with_cte_context(ctes);
100                }
101                evaluator.eval(expr, &dummy_row)
102            } else {
103                Err(ExecutorError::UnsupportedExpression(
104                    "Complex expressions in INSERT VALUES require database context".to_string(),
105                ))
106            }
107        }
108    }
109}
110
111/// Evaluate a DEFAULT expression to get its value
112/// Supports literals, special functions (CURRENT_DATE, CURRENT_USER, etc.), sequences,
113/// and unary expressions (+val, -val) for numeric defaults
114/// Note: For NextValue expressions, this function signature needs database access
115/// This will require refactoring to pass db context
116pub fn evaluate_default_expression(
117    expr: &vibesql_ast::Expression,
118) -> Result<vibesql_types::SqlValue, ExecutorError> {
119    match expr {
120        vibesql_ast::Expression::Literal(lit) => Ok(lit.clone()),
121        // Support unary plus/minus for DEFAULT expressions like "default +4.32" or "default -5"
122        vibesql_ast::Expression::UnaryOp { op, expr } => {
123            let inner = evaluate_default_expression(expr)?;
124            match op {
125                vibesql_ast::UnaryOperator::Plus => Ok(inner), // +x = x
126                vibesql_ast::UnaryOperator::Minus => {
127                    // Negate the value
128                    match inner {
129                        vibesql_types::SqlValue::Integer(i) => {
130                            Ok(vibesql_types::SqlValue::Integer(-i))
131                        }
132                        vibesql_types::SqlValue::Bigint(i) => {
133                            Ok(vibesql_types::SqlValue::Bigint(-i))
134                        }
135                        vibesql_types::SqlValue::Smallint(i) => {
136                            Ok(vibesql_types::SqlValue::Smallint(-i))
137                        }
138                        vibesql_types::SqlValue::Float(f) => Ok(vibesql_types::SqlValue::Float(-f)),
139                        vibesql_types::SqlValue::Real(f) => Ok(vibesql_types::SqlValue::Real(-f)),
140                        vibesql_types::SqlValue::Double(f) => {
141                            Ok(vibesql_types::SqlValue::Double(-f))
142                        }
143                        vibesql_types::SqlValue::Numeric(f) => {
144                            Ok(vibesql_types::SqlValue::Numeric(-f))
145                        }
146                        _ => Err(ExecutorError::UnsupportedExpression(format!(
147                            "Cannot apply unary minus to {:?}",
148                            inner
149                        ))),
150                    }
151                }
152                _ => Err(ExecutorError::UnsupportedExpression(format!(
153                    "Unary operator {:?} not supported in DEFAULT expressions",
154                    op
155                ))),
156            }
157        }
158        vibesql_ast::Expression::NextValue { sequence_name } => {
159            // NEXT VALUE FOR sequence - this should have been handled at a higher level
160            // with access to the database. If we get here, it's an error.
161            Err(ExecutorError::UnsupportedExpression(format!(
162                "Sequence '{}' requires database context - this should have been handled earlier",
163                sequence_name
164            )))
165        }
166        // SQLite compatibility: unquoted identifiers in DEFAULT clauses are treated as string literals
167        // e.g., CREATE TABLE t(x TEXT DEFAULT hello) treats 'hello' as the string "hello"
168        vibesql_ast::Expression::ColumnRef(col_id)
169            if col_id.schema_canonical().is_none() && col_id.table_canonical().is_none() =>
170        {
171            // Convert the column name to a string literal
172            let col_name = col_id.column_canonical();
173            Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(col_name)))
174        }
175        vibesql_ast::Expression::Function { name, .. } => {
176            // Evaluate special SQL functions that can be used in DEFAULT
177            match name.to_uppercase().as_str() {
178                "CURRENT_DATE" => {
179                    use chrono::Datelike;
180                    let now = chrono::Local::now();
181                    let date =
182                        vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
183                            .map_err(|e| {
184                                ExecutorError::UnsupportedFeature(format!(
185                                    "Failed to create date: {}",
186                                    e
187                                ))
188                            })?;
189                    Ok(vibesql_types::SqlValue::Date(date))
190                }
191                "CURRENT_TIME" => {
192                    use chrono::Timelike;
193                    let now = chrono::Local::now();
194                    let time_naive = now.time();
195                    let time = vibesql_types::Time::new(
196                        time_naive.hour() as u8,
197                        time_naive.minute() as u8,
198                        time_naive.second() as u8,
199                        time_naive.nanosecond(),
200                    )
201                    .map_err(|e| {
202                        ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
203                    })?;
204                    Ok(vibesql_types::SqlValue::Time(time))
205                }
206                "CURRENT_TIMESTAMP" => {
207                    use chrono::{Datelike, Timelike};
208                    let now = chrono::Local::now();
209                    let time_naive = now.time();
210                    let date =
211                        vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
212                            .map_err(|e| {
213                                ExecutorError::UnsupportedFeature(format!(
214                                    "Failed to create date: {}",
215                                    e
216                                ))
217                            })?;
218                    let time = vibesql_types::Time::new(
219                        time_naive.hour() as u8,
220                        time_naive.minute() as u8,
221                        time_naive.second() as u8,
222                        time_naive.nanosecond(),
223                    )
224                    .map_err(|e| {
225                        ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
226                    })?;
227                    Ok(vibesql_types::SqlValue::Timestamp(vibesql_types::Timestamp::new(
228                        date, time,
229                    )))
230                }
231                "CURRENT_USER" | "USER" | "SESSION_USER" => {
232                    // Return current user (placeholder - would come from session context)
233                    Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from("public")))
234                }
235                "CURRENT_ROLE" => {
236                    // Return current role (placeholder - would come from session context)
237                    Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from("public")))
238                }
239                _ => Err(ExecutorError::UnsupportedExpression(format!(
240                    "Function '{}' not supported in DEFAULT expressions",
241                    name
242                ))),
243            }
244        }
245        // The parser represents bare `DEFAULT CURRENT_DATE` /
246        // `CURRENT_TIME` / `CURRENT_TIMESTAMP` as dedicated AST variants
247        // (not `Function`), so evaluate them here with the same clock
248        // semantics as the `Function`-named forms above. This is the
249        // path the Raft freeze pass relies on to materialize a
250        // deterministic value at propose time (#5381).
251        vibesql_ast::Expression::CurrentDate => {
252            use chrono::Datelike;
253            let now = chrono::Local::now();
254            let date = vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
255                .map_err(|e| {
256                    ExecutorError::UnsupportedFeature(format!("Failed to create date: {}", e))
257                })?;
258            Ok(vibesql_types::SqlValue::Date(date))
259        }
260        vibesql_ast::Expression::CurrentTime { .. } => {
261            use chrono::Timelike;
262            let now = chrono::Local::now();
263            let time_naive = now.time();
264            let time = vibesql_types::Time::new(
265                time_naive.hour() as u8,
266                time_naive.minute() as u8,
267                time_naive.second() as u8,
268                time_naive.nanosecond(),
269            )
270            .map_err(|e| {
271                ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
272            })?;
273            Ok(vibesql_types::SqlValue::Time(time))
274        }
275        vibesql_ast::Expression::CurrentTimestamp { .. } => {
276            use chrono::{Datelike, Timelike};
277            let now = chrono::Local::now();
278            let time_naive = now.time();
279            let date = vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
280                .map_err(|e| {
281                    ExecutorError::UnsupportedFeature(format!("Failed to create date: {}", e))
282                })?;
283            let time = vibesql_types::Time::new(
284                time_naive.hour() as u8,
285                time_naive.minute() as u8,
286                time_naive.second() as u8,
287                time_naive.nanosecond(),
288            )
289            .map_err(|e| {
290                ExecutorError::UnsupportedFeature(format!("Failed to create time: {}", e))
291            })?;
292            Ok(vibesql_types::SqlValue::Timestamp(vibesql_types::Timestamp::new(date, time)))
293        }
294        _ => Err(ExecutorError::UnsupportedExpression(
295            "Only literals and functions are supported in DEFAULT expressions".to_string(),
296        )),
297    }
298}
299
300/// Apply DEFAULT values for unspecified columns
301/// Now accepts database parameter to handle sequence NextValue expressions
302///
303/// Returns the first auto-generated sequence value (for LAST_INSERT_ROWID support),
304/// or None if no sequence values were generated.
305#[allow(dead_code)]
306pub fn apply_default_values(
307    schema: &vibesql_catalog::TableSchema,
308    row_values: &mut [vibesql_types::SqlValue],
309    database: &mut vibesql_storage::Database,
310) -> Result<Option<i64>, ExecutorError> {
311    apply_default_values_with_table_name(schema, row_values, database, &schema.name)
312}
313
314/// Apply DEFAULT values for unspecified columns, with explicit table name for storage lookup
315///
316/// This variant is used when the storage table name differs from the schema name
317/// (e.g., for schema-qualified tables).
318///
319/// Returns the first auto-generated sequence value (for LAST_INSERT_ROWID support),
320/// or None if no sequence values were generated.
321pub fn apply_default_values_with_table_name(
322    schema: &vibesql_catalog::TableSchema,
323    row_values: &mut [vibesql_types::SqlValue],
324    database: &mut vibesql_storage::Database,
325    storage_table_name: &str,
326) -> Result<Option<i64>, ExecutorError> {
327    apply_default_values_with_batch_context(schema, row_values, database, storage_table_name, None)
328}
329
330/// Apply DEFAULT values for unspecified columns, with batch context for multi-row inserts
331///
332/// The `batch_max_ipk` parameter is used to track the maximum INTEGER PRIMARY KEY value
333/// already assigned within the current batch of inserts. This prevents duplicate values
334/// when multiple rows in the same INSERT have NULL for the INTEGER PRIMARY KEY column.
335///
336/// Returns the first auto-generated sequence value (for LAST_INSERT_ROWID support),
337/// or None if no sequence values were generated.
338pub fn apply_default_values_with_batch_context(
339    schema: &vibesql_catalog::TableSchema,
340    row_values: &mut [vibesql_types::SqlValue],
341    database: &mut vibesql_storage::Database,
342    storage_table_name: &str,
343    batch_max_ipk: Option<i64>,
344) -> Result<Option<i64>, ExecutorError> {
345    let mut first_generated_id: Option<i64> = None;
346
347    // Handle INTEGER PRIMARY KEY NULL auto-generation (SQLite semantics)
348    // This must happen before regular default value processing
349    if let Some(ipk_idx) = schema.get_integer_primary_key_index() {
350        if row_values[ipk_idx] == vibesql_types::SqlValue::Null {
351            // Auto-generate: max(existing_pk, batch_max) + 1, or 1 if table is empty
352            let table_max = compute_next_integer_pk_value(database, storage_table_name, ipk_idx)?;
353            // The next value should be max of (table_max, batch_max_ipk + 1)
354            let next_val = match batch_max_ipk {
355                Some(batch_max) => table_max.max(batch_max + 1),
356                None => table_max,
357            };
358            row_values[ipk_idx] = vibesql_types::SqlValue::Integer(next_val);
359
360            // Track for LAST_INSERT_ROWID
361            if first_generated_id.is_none() {
362                first_generated_id = Some(next_val);
363            }
364        }
365    }
366
367    for (col_idx, col) in schema.columns.iter().enumerate() {
368        // If column is NULL and has a default value, apply it
369        if row_values[col_idx] == vibesql_types::SqlValue::Null {
370            if let Some(default_expr) = &col.default_value {
371                // Handle NextValue expressions specially
372                let default_value = match default_expr {
373                    vibesql_ast::Expression::NextValue { sequence_name } => {
374                        // Get the next value from the sequence
375                        let seq =
376                            database.catalog.get_sequence_mut(sequence_name).map_err(|e| {
377                                ExecutorError::UnsupportedExpression(format!(
378                                    "Sequence error: {:?}",
379                                    e
380                                ))
381                            })?;
382                        let next_val = seq.next_value().map_err(|e| {
383                            ExecutorError::ConstraintViolation(format!("Sequence error: {}", e))
384                        })?;
385
386                        // Track the first generated ID for LAST_INSERT_ROWID
387                        if first_generated_id.is_none() {
388                            first_generated_id = Some(next_val);
389                        }
390
391                        vibesql_types::SqlValue::Integer(next_val)
392                    }
393                    _ => evaluate_default_expression(default_expr)?,
394                };
395                let coerced_value = super::validation::coerce_value(default_value, &col.data_type)?;
396                row_values[col_idx] = coerced_value;
397            }
398        }
399    }
400    Ok(first_generated_id)
401}
402
403/// Compute the next INTEGER PRIMARY KEY value for auto-generation
404///
405/// Returns max(existing_pk_values) + 1, or 1 if the table is empty.
406/// This implements SQLite's behavior where inserting NULL into an INTEGER PRIMARY KEY
407/// column auto-generates the next available value.
408fn compute_next_integer_pk_value(
409    database: &vibesql_storage::Database,
410    table_name: &str,
411    pk_col_idx: usize,
412) -> Result<i64, ExecutorError> {
413    let table = match database.get_table(table_name) {
414        Some(t) => t,
415        None => {
416            // Table doesn't exist in storage yet, start at 1
417            return Ok(1);
418        }
419    };
420
421    // Find the maximum value in the PRIMARY KEY column
422    let mut max_val: i64 = 0;
423
424    for row in table.scan() {
425        if let Some(value) = row.get(pk_col_idx) {
426            let int_val = match value {
427                vibesql_types::SqlValue::Integer(i) => *i,
428                vibesql_types::SqlValue::Bigint(i) => *i,
429                vibesql_types::SqlValue::Null => continue, // Skip NULL values
430                _ => continue, // Skip non-integer values (shouldn't happen)
431            };
432            if int_val > max_val {
433                max_val = int_val;
434            }
435        }
436    }
437
438    // Return max + 1 (or 1 if table was empty, since max_val starts at 0)
439    Ok(max_val + 1)
440}
441
442/// Apply generated/computed column values
443/// Generated columns are defined with AS(expression) syntax and computed on INSERT/UPDATE
444pub fn apply_generated_columns(
445    schema: &vibesql_catalog::TableSchema,
446    row_values: &mut [vibesql_types::SqlValue],
447    _database: &vibesql_storage::Database,
448) -> Result<(), ExecutorError> {
449    // Create a temporary row to evaluate generated expressions.
450    // GeneratedColumn context: non-deterministic date/time uses (e.g.
451    // `z AS (date())`) are rejected at evaluation time (SQLite, date2-140).
452    let temp_row = vibesql_storage::Row::new(row_values.to_vec());
453    let evaluator = crate::ExpressionEvaluator::new(schema)
454        .with_schema_context(crate::evaluator::SchemaExprContext::GeneratedColumn);
455
456    for (col_idx, col) in schema.columns.iter().enumerate() {
457        // If column has a generated expression, compute and apply it
458        if let Some(generated_expr) = &col.generated_expr {
459            let generated_value = evaluator.eval(generated_expr, &temp_row)?;
460            let coerced_value = super::validation::coerce_value(generated_value, &col.data_type)?;
461            row_values[col_idx] = coerced_value;
462        }
463    }
464    Ok(())
465}