Skip to main content

vibesql_executor/
select_into.rs

1//! SELECT INTO executor - SQL:1999 Feature E111
2//!
3//! Implements SELECT INTO statements which create a new table and insert query results.
4//! Feature E111 requires exactly one row to be returned.
5
6use vibesql_ast::{ColumnDef, CreateTableStmt, SelectItem, SelectStmt};
7use vibesql_storage::Database;
8use vibesql_types::DataType;
9
10use crate::{errors::ExecutorError, select::SelectExecutor};
11
12pub struct SelectIntoExecutor;
13
14impl SelectIntoExecutor {
15    /// Execute a SELECT INTO statement
16    ///
17    /// This creates a new table with the specified name and inserts the query results.
18    /// Per SQL:1999 Feature E111, exactly one row must be returned.
19    pub fn execute(
20        stmt: &SelectStmt,
21        target_table: &str,
22        database: &mut Database,
23    ) -> Result<String, ExecutorError> {
24        // Validate that this is a SELECT INTO
25        if stmt.into_table.is_none() {
26            return Err(ExecutorError::Other(
27                "execute_select_into called on non-SELECT INTO statement".to_string(),
28            ));
29        }
30
31        // Execute the SELECT query
32        let executor = SelectExecutor::new(database);
33        let rows = executor.execute(stmt)?;
34
35        // SQL:1999 Feature E111 requires exactly one row
36        if rows.is_empty() {
37            return Err(ExecutorError::UnsupportedFeature(
38                "SELECT INTO returned no rows (expected exactly 1 row for Feature E111)"
39                    .to_string(),
40            ));
41        }
42        if rows.len() > 1 {
43            return Err(ExecutorError::UnsupportedFeature(format!(
44                "SELECT INTO returned {} rows (expected exactly 1 row for Feature E111)",
45                rows.len()
46            )));
47        }
48
49        // Derive column definitions from the SELECT list and result row
50        let column_defs = Self::derive_column_definitions(&stmt.select_list, &rows[0], database)?;
51
52        // Create the target table
53        let create_stmt = CreateTableStmt {
54            temporary: false,
55            if_not_exists: false,
56            table_name: target_table.to_string(),
57            columns: column_defs,
58            table_constraints: vec![],
59            table_options: vec![],
60            quoted: false, // Synthesized from SELECT INTO, treat as unquoted
61            name_source: None,
62            as_query: None,
63            without_rowid: false,
64        };
65
66        crate::CreateTableExecutor::execute(&create_stmt, database)?;
67
68        // Insert the row
69        database
70            .insert_row(target_table, rows[0].clone())
71            .map_err(|e| ExecutorError::StorageError(e.to_string()))?;
72
73        Ok(format!("SELECT INTO: Created table '{}' with 1 row", target_table))
74    }
75
76    /// Derive column definitions from SELECT list and result row
77    fn derive_column_definitions(
78        select_list: &[SelectItem],
79        result_row: &vibesql_storage::Row,
80        database: &Database,
81    ) -> Result<Vec<ColumnDef>, ExecutorError> {
82        let mut columns = Vec::new();
83
84        for (idx, item) in select_list.iter().enumerate() {
85            match item {
86                SelectItem::Wildcard { .. } => {
87                    return Err(ExecutorError::UnsupportedFeature(
88                        "SELECT * is not supported in SELECT INTO statements".to_string(),
89                    ));
90                }
91                SelectItem::QualifiedWildcard { .. } => {
92                    return Err(ExecutorError::UnsupportedFeature(
93                        "SELECT table.* is not supported in SELECT INTO statements".to_string(),
94                    ));
95                }
96                SelectItem::Expression { expr, alias, .. } => {
97                    // Column name: use alias if present, otherwise derive from expression
98                    let column_name = if let Some(alias) = alias {
99                        alias.clone()
100                    } else {
101                        Self::derive_column_name(expr, database)?
102                    };
103
104                    // Infer data type from the result value
105                    let data_type = Self::infer_data_type(&result_row.values[idx]);
106
107                    columns.push(ColumnDef {
108                        name: column_name,
109                        data_type,
110                        nullable: true, // Allow NULL by default
111                        constraints: vec![],
112                        default_value: None,
113                        comment: None,
114                        generated_expr: None,
115                        is_exact_integer_type: false, // SELECT INTO doesn't preserve exact type
116                    });
117                }
118            }
119        }
120
121        Ok(columns)
122    }
123
124    /// Derive a column name from an expression
125    fn derive_column_name(
126        expr: &vibesql_ast::Expression,
127        _database: &Database,
128    ) -> Result<String, ExecutorError> {
129        match expr {
130            vibesql_ast::Expression::ColumnRef(col_id) => Ok(col_id.column_canonical().to_string()),
131            vibesql_ast::Expression::Literal(_) => Ok("column".to_string()),
132            vibesql_ast::Expression::BinaryOp { .. } => Ok("expr".to_string()),
133            vibesql_ast::Expression::UnaryOp { .. } => Ok("expr".to_string()),
134            vibesql_ast::Expression::Function { name, .. } => Ok(name.to_lowercase()),
135            _ => Ok("column".to_string()),
136        }
137    }
138
139    /// Infer SQL data type from a runtime value
140    fn infer_data_type(value: &vibesql_types::SqlValue) -> DataType {
141        match value {
142            vibesql_types::SqlValue::Null => DataType::Varchar { max_length: Some(255) }, /* Default for */
143            // NULL
144            vibesql_types::SqlValue::Integer(_) => DataType::Integer,
145            vibesql_types::SqlValue::Bigint(_) => DataType::Bigint,
146            vibesql_types::SqlValue::Unsigned(_) => DataType::Unsigned,
147            vibesql_types::SqlValue::Smallint(_) => DataType::Smallint,
148            vibesql_types::SqlValue::Numeric(_) => DataType::Numeric { precision: 38, scale: 0 },
149            vibesql_types::SqlValue::Float(_) => DataType::Float { precision: 53 },
150            vibesql_types::SqlValue::Real(_) => DataType::Real,
151            vibesql_types::SqlValue::Double(_) => DataType::DoublePrecision,
152            vibesql_types::SqlValue::Varchar(s) | vibesql_types::SqlValue::Character(s) => {
153                DataType::Varchar { max_length: Some(s.len().max(255)) }
154            }
155            vibesql_types::SqlValue::Boolean(_) => DataType::Boolean,
156            vibesql_types::SqlValue::Date(_) => DataType::Date,
157            vibesql_types::SqlValue::Time(_) => DataType::Time { with_timezone: false },
158            vibesql_types::SqlValue::Timestamp(_) => DataType::Timestamp { with_timezone: false },
159            vibesql_types::SqlValue::Interval(_) => DataType::Interval {
160                start_field: vibesql_types::IntervalField::Year,
161                end_field: Some(vibesql_types::IntervalField::Month),
162            },
163            vibesql_types::SqlValue::Vector(v) => DataType::Vector { dimensions: v.len() as u32 },
164            vibesql_types::SqlValue::Blob(_) => DataType::BinaryLargeObject,
165        }
166    }
167}