1use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
2use std::cell::RefCell;
3use std::collections::VecDeque;
4use std::convert::TryFrom;
5use std::sync::{
6 Arc, OnceLock,
7 atomic::{AtomicBool, Ordering as AtomicOrdering},
8};
9
10use crate::SqlResult;
11use crate::SqlValue;
12use arrow::array::{Array, ArrayRef, UInt32Array};
13use arrow::compute::{concat_batches, take};
14use arrow::datatypes::{DataType, Field, Schema};
15use arrow::record_batch::RecordBatch;
16use arrow::row::{RowConverter, SortField};
17
18use llkv_executor::{SelectExecution, push_query_label};
19use llkv_expr::literal::Literal;
20use llkv_plan::validation::{
21 ensure_known_columns_case_insensitive, ensure_non_empty, ensure_unique_case_insensitive,
22};
23use llkv_plan::{SubqueryCorrelatedColumnTracker, SubqueryCorrelatedTracker, TransformFrame};
24use llkv_result::Error;
25use llkv_runtime::TEMPORARY_NAMESPACE_ID;
26use llkv_runtime::{
27 AggregateExpr, AssignmentValue, ColumnAssignment, CreateIndexPlan, CreateTablePlan,
28 CreateTableSource, CreateViewPlan, DeletePlan, ForeignKeyAction, ForeignKeySpec,
29 IndexColumnPlan, InsertConflictAction, InsertPlan, InsertSource, MultiColumnUniqueSpec,
30 OrderByPlan, OrderSortType, OrderTarget, PlanColumnSpec, PlanStatement, PlanValue, ReindexPlan,
31 RenameTablePlan, RuntimeContext, RuntimeEngine, RuntimeSession, RuntimeStatementResult,
32 SelectPlan, SelectProjection, TruncatePlan, UpdatePlan, extract_rows_from_range,
33};
34use llkv_storage::pager::{BoxedPager, Pager};
35use llkv_table::catalog::{ColumnResolution, IdentifierContext, IdentifierResolver};
36use llkv_table::{CatalogDdl, TriggerEventMeta, TriggerTimingMeta};
37use regex::Regex;
38use simd_r_drive_entry_handle::EntryHandle;
39use sqlparser::ast::{
40 AlterColumnOperation, AlterTableOperation, Assignment, AssignmentTarget, BeginTransactionKind,
41 BinaryOperator, ColumnOption, ColumnOptionDef, ConstraintCharacteristics, CreateTrigger,
42 DataType as SqlDataType, Delete, Distinct, DropTrigger, ExceptionWhen, Expr as SqlExpr,
43 FromTable, FunctionArg, FunctionArgExpr, FunctionArguments, GroupByExpr, Ident, JoinConstraint,
44 JoinOperator, LimitClause, NullsDistinctOption, ObjectName, ObjectNamePart, ObjectType,
45 OrderBy, OrderByKind, Query, ReferentialAction, SchemaName, Select, SelectItem,
46 SelectItemQualifiedWildcardKind, Set, SetExpr, SetOperator, SetQuantifier, SqlOption,
47 Statement, TableConstraint, TableFactor, TableObject, TableWithJoins, TransactionMode,
48 TransactionModifier, TriggerEvent, TriggerObject, TriggerPeriod, UnaryOperator,
49 UpdateTableFromKind, VacuumStatement, Value, ValueWithSpan,
50};
51use sqlparser::dialect::GenericDialect;
52use sqlparser::parser::Parser;
53use sqlparser::tokenizer::Span;
54
55type SqlPager = BoxedPager;
56type SqlRuntimePager = SqlPager;
57type SqlStatementResult = RuntimeStatementResult<SqlPager>;
58type SqlContext = RuntimeContext<SqlPager>;
59type SqlSession = RuntimeSession;
60type P = SqlRuntimePager;
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub enum StatementExpectation {
64 Ok,
65 Error,
66 Count(u64),
67}
68
69thread_local! {
70 static PENDING_STATEMENT_EXPECTATIONS: RefCell<VecDeque<StatementExpectation>> = const {
71 RefCell::new(VecDeque::new())
72 };
73}
74
75pub fn register_statement_expectation(expectation: StatementExpectation) {
76 PENDING_STATEMENT_EXPECTATIONS.with(|queue| {
77 queue.borrow_mut().push_back(expectation);
78 });
79}
80
81pub fn clear_pending_statement_expectations() {
82 PENDING_STATEMENT_EXPECTATIONS.with(|queue| {
83 queue.borrow_mut().clear();
84 });
85}
86
87fn next_statement_expectation() -> StatementExpectation {
88 PENDING_STATEMENT_EXPECTATIONS
89 .with(|queue| queue.borrow_mut().pop_front())
90 .unwrap_or(StatementExpectation::Ok)
91}
92
93const PARSER_RECURSION_LIMIT: usize = 200;
101
102trait ScalarSubqueryResolver {
103 fn handle_scalar_subquery(
104 &mut self,
105 subquery: &Query,
106 resolver: &IdentifierResolver<'_>,
107 context: &IdentifierContext,
108 outer_scopes: &[IdentifierContext],
109 ) -> SqlResult<llkv_expr::expr::ScalarExpr<String>>;
110}
111
112trait SubqueryCorrelatedTrackerExt {
114 fn placeholder_for_resolution(
115 &mut self,
116 resolution: &llkv_table::catalog::ColumnResolution,
117 ) -> Option<String>;
118}
119
120impl SubqueryCorrelatedTrackerExt for SubqueryCorrelatedTracker<'_> {
121 fn placeholder_for_resolution(
122 &mut self,
123 resolution: &llkv_table::catalog::ColumnResolution,
124 ) -> Option<String> {
125 self.placeholder_for_column_path(resolution.column(), resolution.field_path())
126 }
127}
128
129trait SubqueryCorrelatedTrackerOptionExt {
132 fn reborrow(&mut self) -> Option<&mut SubqueryCorrelatedColumnTracker>;
133}
134
135impl SubqueryCorrelatedTrackerOptionExt for Option<&mut SubqueryCorrelatedColumnTracker> {
136 fn reborrow(&mut self) -> Option<&mut SubqueryCorrelatedColumnTracker> {
137 self.as_mut().map(|tracker| &mut **tracker)
138 }
139}
140
141const MAX_BUFFERED_INSERT_ROWS: usize = 8192;
191
192struct InsertBuffer {
198 table_name: String,
199 columns: Vec<String>,
200 on_conflict: InsertConflictAction,
202 total_rows: usize,
204 statement_row_counts: Vec<usize>,
206 rows: Vec<Vec<PlanValue>>,
208}
209
210impl InsertBuffer {
211 fn new(
212 table_name: String,
213 columns: Vec<String>,
214 rows: Vec<Vec<PlanValue>>,
215 on_conflict: InsertConflictAction,
216 ) -> Self {
217 let row_count = rows.len();
218 Self {
219 table_name,
220 columns,
221 on_conflict,
222 total_rows: row_count,
223 statement_row_counts: vec![row_count],
224 rows,
225 }
226 }
227
228 fn can_accept(
229 &self,
230 table_name: &str,
231 columns: &[String],
232 on_conflict: InsertConflictAction,
233 ) -> bool {
234 self.table_name == table_name && self.columns == columns && self.on_conflict == on_conflict
235 }
236
237 fn push_statement(&mut self, rows: Vec<Vec<PlanValue>>) {
238 let row_count = rows.len();
239 self.total_rows += row_count;
240 self.statement_row_counts.push(row_count);
241 self.rows.extend(rows);
242 }
243
244 fn should_flush(&self) -> bool {
245 self.total_rows >= MAX_BUFFERED_INSERT_ROWS
246 }
247}
248
249enum PreparedInsert {
256 Values {
257 table_name: String,
258 columns: Vec<String>,
259 rows: Vec<Vec<PlanValue>>,
260 on_conflict: InsertConflictAction,
261 },
262 Immediate(InsertPlan),
263}
264
265struct BufferedInsertResult {
268 flushed: Vec<SqlStatementResult>,
269 current: Option<SqlStatementResult>,
270}
271
272pub struct SqlEngine {
273 engine: RuntimeEngine,
274 default_nulls_first: AtomicBool,
275 insert_buffer: RefCell<Option<InsertBuffer>>,
277 insert_buffering_enabled: AtomicBool,
284}
285
286const DROPPED_TABLE_TRANSACTION_ERR: &str = "another transaction has dropped this table";
287
288impl Drop for SqlEngine {
289 fn drop(&mut self) {
290 if let Err(e) = self.flush_buffer_results() {
292 tracing::warn!("Failed to flush INSERT buffer on drop: {:?}", e);
293 }
294 }
295}
296
297impl Clone for SqlEngine {
298 fn clone(&self) -> Self {
299 tracing::warn!(
300 "[SQL_ENGINE] SqlEngine::clone() called - will create new Engine with new session!"
301 );
302 Self {
304 engine: self.engine.clone(),
305 default_nulls_first: AtomicBool::new(
306 self.default_nulls_first.load(AtomicOrdering::Relaxed),
307 ),
308 insert_buffer: RefCell::new(None),
309 insert_buffering_enabled: AtomicBool::new(
310 self.insert_buffering_enabled.load(AtomicOrdering::Relaxed),
311 ),
312 }
313 }
314}
315
316#[allow(dead_code)]
317impl SqlEngine {
318 fn from_runtime_engine(
319 engine: RuntimeEngine,
320 default_nulls_first: bool,
321 insert_buffering_enabled: bool,
322 ) -> Self {
323 Self {
324 engine,
325 default_nulls_first: AtomicBool::new(default_nulls_first),
326 insert_buffer: RefCell::new(None),
327 insert_buffering_enabled: AtomicBool::new(insert_buffering_enabled),
328 }
329 }
330
331 fn map_table_error(table_name: &str, err: Error) -> Error {
332 match err {
333 Error::NotFound => Self::table_not_found_error(table_name),
334 Error::InvalidArgumentError(msg) if msg.contains("unknown table") => {
335 Self::table_not_found_error(table_name)
336 }
337 other => other,
338 }
339 }
340
341 fn table_not_found_error(table_name: &str) -> Error {
342 Error::CatalogError(format!(
343 "Catalog Error: Table '{table_name}' does not exist"
344 ))
345 }
346
347 fn is_table_missing_error(err: &Error) -> bool {
348 match err {
349 Error::NotFound => true,
350 Error::CatalogError(msg) => {
351 msg.contains("Catalog Error: Table") || msg.contains("unknown table")
352 }
353 Error::InvalidArgumentError(msg) => {
354 msg.contains("Catalog Error: Table") || msg.contains("unknown table")
355 }
356 _ => false,
357 }
358 }
359
360 fn execute_plan_statement(&self, statement: PlanStatement) -> SqlResult<SqlStatementResult> {
361 let should_map_error = !matches!(
365 &statement,
366 PlanStatement::CreateView(_) | PlanStatement::DropView(_)
367 );
368
369 let table = if should_map_error {
370 llkv_runtime::statement_table_name(&statement).map(str::to_string)
371 } else {
372 None
373 };
374
375 self.engine.execute_statement(statement).map_err(|err| {
376 if let Some(table_name) = table {
377 Self::map_table_error(&table_name, err)
378 } else {
379 err
380 }
381 })
382 }
383
384 pub fn new<Pg>(pager: Arc<Pg>) -> Self
389 where
390 Pg: Pager<Blob = EntryHandle> + Send + Sync + 'static,
391 {
392 let engine = RuntimeEngine::new(pager);
393 Self::from_runtime_engine(engine, false, false)
394 }
395
396 fn preprocess_create_type_syntax(sql: &str) -> String {
404 static CREATE_TYPE_REGEX: OnceLock<Regex> = OnceLock::new();
405 static DROP_TYPE_REGEX: OnceLock<Regex> = OnceLock::new();
406
407 let create_re = CREATE_TYPE_REGEX.get_or_init(|| {
409 Regex::new(r"(?i)\bCREATE\s+TYPE\s+").expect("valid CREATE TYPE regex")
410 });
411
412 let drop_re = DROP_TYPE_REGEX
414 .get_or_init(|| Regex::new(r"(?i)\bDROP\s+TYPE\s+").expect("valid DROP TYPE regex"));
415
416 let sql = create_re.replace_all(sql, "CREATE DOMAIN ").to_string();
418
419 drop_re.replace_all(&sql, "DROP DOMAIN ").to_string()
421 }
422
423 fn preprocess_exclude_syntax(sql: &str) -> String {
424 static EXCLUDE_REGEX: OnceLock<Regex> = OnceLock::new();
425
426 let re = EXCLUDE_REGEX.get_or_init(|| {
429 Regex::new(
430 r"(?i)EXCLUDE\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)+)\s*\)",
431 )
432 .expect("valid EXCLUDE qualifier regex")
433 });
434
435 re.replace_all(sql, |caps: ®ex::Captures| {
436 let qualified_name = &caps[1];
437 format!("EXCLUDE (\"{}\")", qualified_name)
438 })
439 .to_string()
440 }
441
442 fn preprocess_trailing_commas_in_values(sql: &str) -> String {
445 static TRAILING_COMMA_REGEX: OnceLock<Regex> = OnceLock::new();
446
447 let re = TRAILING_COMMA_REGEX
450 .get_or_init(|| Regex::new(r",(\s*)\)").expect("valid trailing comma regex"));
451
452 re.replace_all(sql, "$1)").to_string()
453 }
454
455 fn preprocess_empty_in_lists(sql: &str) -> String {
464 static EMPTY_IN_REGEX: OnceLock<Regex> = OnceLock::new();
465
466 let re = EMPTY_IN_REGEX.get_or_init(|| {
469 Regex::new(r"(?i)(\([^)]*\)|x'[0-9a-fA-F]*'|'(?:[^']|'')*'|[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*|\d+(?:\.\d+)?)\s+(NOT\s+)?IN\s*\(\s*\)")
470 .expect("valid empty IN regex")
471 });
472
473 re.replace_all(sql, |caps: ®ex::Captures| {
474 let expr = &caps[1];
475 if caps.get(2).is_some() {
476 format!("({} = NULL OR 1 = 1)", expr)
478 } else {
479 format!("({} = NULL AND 0 = 1)", expr)
481 }
482 })
483 .to_string()
484 }
485
486 fn preprocess_index_hints(sql: &str) -> String {
493 static INDEX_HINT_REGEX: OnceLock<Regex> = OnceLock::new();
494
495 let re = INDEX_HINT_REGEX.get_or_init(|| {
498 Regex::new(r"(?i)\s+(INDEXED\s+BY\s+[a-zA-Z_][a-zA-Z0-9_]*|NOT\s+INDEXED)\b")
499 .expect("valid index hint regex")
500 });
501
502 re.replace_all(sql, "").to_string()
503 }
504
505 fn preprocess_reindex_syntax(sql: &str) -> String {
511 static REINDEX_REGEX: OnceLock<Regex> = OnceLock::new();
512
513 let re = REINDEX_REGEX.get_or_init(|| {
516 Regex::new(r"(?i)\bREINDEX\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\b")
517 .expect("valid reindex regex")
518 });
519
520 re.replace_all(sql, "VACUUM REINDEX $1").to_string()
521 }
522
523 fn preprocess_sqlite_trigger_shorthand(sql: &str) -> String {
536 static IDENT_PATTERN: OnceLock<String> = OnceLock::new();
537 static COLUMN_IDENT_PATTERN: OnceLock<String> = OnceLock::new();
538 static TIMING_REGEX: OnceLock<Regex> = OnceLock::new();
539 static FOR_EACH_BEGIN_REGEX: OnceLock<Regex> = OnceLock::new();
540 static FOR_EACH_WHEN_REGEX: OnceLock<Regex> = OnceLock::new();
541
542 IDENT_PATTERN.get_or_init(|| {
543 r#"(?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[a-zA-Z_][a-zA-Z0-9_]*)(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*"#
545 .to_string()
546 });
547 COLUMN_IDENT_PATTERN
548 .get_or_init(|| r#"(?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[a-zA-Z_][a-zA-Z0-9_]*)"#.to_string());
549
550 let timing_re = TIMING_REGEX.get_or_init(|| {
551 let event = format!(
552 "UPDATE(?:\\s+OF\\s+{col}(?:\\s*,\\s*{col})*)?|DELETE|INSERT",
553 col = COLUMN_IDENT_PATTERN
554 .get()
555 .expect("column ident pattern initialized")
556 );
557 let pattern = format!(
558 r"(?ix)(?P<head>CREATE\s+TRIGGER\s+(?:IF\s+NOT\s+EXISTS\s+)?{ident})\s+(?P<event>{event})\s+ON",
559 ident = IDENT_PATTERN
560 .get()
561 .expect("ident pattern initialized"),
562 event = event
563 );
564 Regex::new(&pattern).expect("valid trigger timing regex")
565 });
566
567 let with_timing = timing_re
568 .replace_all(sql, |caps: ®ex::Captures| {
569 let head = caps.name("head").unwrap().as_str();
570 let event = caps.name("event").unwrap().as_str().trim();
571 format!("{head} AFTER {event} ON")
572 })
573 .to_string();
574
575 let for_each_begin_re = FOR_EACH_BEGIN_REGEX.get_or_init(|| {
576 let pattern = format!(
577 r"(?ix)(?P<prefix>ON\s+{ident})\s+(?P<keyword>BEGIN\b)",
578 ident = IDENT_PATTERN.get().expect("ident pattern initialized"),
579 );
580 Regex::new(&pattern).expect("valid trigger FOR EACH BEGIN regex")
581 });
582
583 let with_for_each_begin = for_each_begin_re
584 .replace_all(&with_timing, |caps: ®ex::Captures| {
585 let prefix = caps.name("prefix").unwrap().as_str();
586 let keyword = caps.name("keyword").unwrap().as_str();
587 format!("{prefix} FOR EACH ROW {keyword}")
588 })
589 .to_string();
590
591 let for_each_when_re = FOR_EACH_WHEN_REGEX.get_or_init(|| {
592 let pattern = format!(
593 r"(?ix)(?P<prefix>ON\s+{ident})\s+(?P<keyword>WHEN\b)",
594 ident = IDENT_PATTERN.get().expect("ident pattern initialized"),
595 );
596 Regex::new(&pattern).expect("valid trigger FOR EACH WHEN regex")
597 });
598
599 for_each_when_re
600 .replace_all(&with_for_each_begin, |caps: ®ex::Captures| {
601 let prefix = caps.name("prefix").unwrap().as_str();
602 let keyword = caps.name("keyword").unwrap().as_str();
603 format!("{prefix} FOR EACH ROW {keyword}")
604 })
605 .to_string()
606 }
607
608 fn preprocess_bare_table_in_clauses(sql: &str) -> String {
613 static BARE_TABLE_IN_REGEX: OnceLock<Regex> = OnceLock::new();
614
615 let re = BARE_TABLE_IN_REGEX.get_or_init(|| {
618 Regex::new(r"(?i)\b(NOT\s+)?IN\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)(\s|$|;|,|\))")
619 .expect("valid bare table IN regex")
620 });
621
622 re.replace_all(sql, |caps: ®ex::Captures| {
623 let table_name = &caps[2];
624 let trailing = &caps[3];
625 if let Some(not_keyword) = caps.get(1) {
626 format!(
627 "{}IN (SELECT * FROM {}){}",
628 not_keyword.as_str(),
629 table_name,
630 trailing
631 )
632 } else {
633 format!("IN (SELECT * FROM {}){}", table_name, trailing)
634 }
635 })
636 .to_string()
637 }
638
639 pub(crate) fn context_arc(&self) -> Arc<SqlContext> {
640 self.engine.context()
641 }
642
643 pub fn with_context(context: Arc<SqlContext>, default_nulls_first: bool) -> Self {
644 Self::from_runtime_engine(
645 RuntimeEngine::from_context(context),
646 default_nulls_first,
647 false,
648 )
649 }
650
651 pub fn set_insert_buffering(&self, enabled: bool) -> SqlResult<()> {
663 if !enabled {
664 let _ = self.flush_buffer_results()?;
665 }
666 self.insert_buffering_enabled
667 .store(enabled, AtomicOrdering::Relaxed);
668 Ok(())
669 }
670
671 #[cfg(test)]
672 fn default_nulls_first_for_tests(&self) -> bool {
673 self.default_nulls_first.load(AtomicOrdering::Relaxed)
674 }
675
676 fn has_active_transaction(&self) -> bool {
677 self.engine.session().has_active_transaction()
678 }
679
680 pub fn session(&self) -> &SqlSession {
682 self.engine.session()
683 }
684
685 pub fn execute(&self, sql: &str) -> SqlResult<Vec<SqlStatementResult>> {
698 tracing::trace!("DEBUG SQL execute: {}", sql);
699
700 let processed_sql = Self::preprocess_create_type_syntax(sql);
702 let processed_sql = Self::preprocess_exclude_syntax(&processed_sql);
703 let processed_sql = Self::preprocess_trailing_commas_in_values(&processed_sql);
704 let processed_sql = Self::preprocess_bare_table_in_clauses(&processed_sql);
705 let processed_sql = Self::preprocess_empty_in_lists(&processed_sql);
706 let processed_sql = Self::preprocess_index_hints(&processed_sql);
707 let processed_sql = Self::preprocess_reindex_syntax(&processed_sql);
708
709 let dialect = GenericDialect {};
710 let statements = match parse_sql_with_recursion_limit(&dialect, &processed_sql) {
711 Ok(stmts) => stmts,
712 Err(parse_err) => {
713 if processed_sql.to_uppercase().contains("CREATE TRIGGER") {
718 let expanded = Self::preprocess_sqlite_trigger_shorthand(&processed_sql);
719 parse_sql_with_recursion_limit(&dialect, &expanded).map_err(|_| {
720 Error::InvalidArgumentError(format!("failed to parse SQL: {parse_err}"))
721 })?
722 } else {
723 return Err(Error::InvalidArgumentError(format!(
724 "failed to parse SQL: {parse_err}"
725 )));
726 }
727 }
728 };
729 let mut results = Vec::with_capacity(statements.len());
730 for statement in statements.iter() {
731 let statement_expectation = next_statement_expectation();
732 match statement {
733 Statement::Insert(insert) => {
734 let mut outcome = self.buffer_insert(insert.clone(), statement_expectation)?;
735 if let Some(current) = outcome.current.take() {
736 results.push(current);
737 }
738 results.append(&mut outcome.flushed);
739 }
740 Statement::StartTransaction { .. }
741 | Statement::Commit { .. }
742 | Statement::Rollback { .. } => {
743 let mut flushed = self.flush_buffer_results()?;
745 let current = self.execute_statement(statement.clone())?;
746 results.push(current);
747 results.append(&mut flushed);
748 }
749 _ => {
750 let mut flushed = self.flush_buffer_results()?;
752 let current = self.execute_statement(statement.clone())?;
753 results.push(current);
754 results.append(&mut flushed);
755 }
756 }
757 }
758
759 Ok(results)
760 }
761
762 pub fn flush_pending_inserts(&self) -> SqlResult<Vec<SqlStatementResult>> {
768 self.flush_buffer_results()
769 }
770
771 fn buffer_insert(
778 &self,
779 insert: sqlparser::ast::Insert,
780 expectation: StatementExpectation,
781 ) -> SqlResult<BufferedInsertResult> {
782 let execute_immediately = matches!(
787 expectation,
788 StatementExpectation::Error | StatementExpectation::Count(_)
789 );
790 if execute_immediately {
791 let flushed = self.flush_buffer_results()?;
792 let current = self.handle_insert(insert)?;
793 return Ok(BufferedInsertResult {
794 flushed,
795 current: Some(current),
796 });
797 }
798
799 if !self.insert_buffering_enabled.load(AtomicOrdering::Relaxed) {
803 let flushed = self.flush_buffer_results()?;
804 let current = self.handle_insert(insert)?;
805 return Ok(BufferedInsertResult {
806 flushed,
807 current: Some(current),
808 });
809 }
810
811 let prepared = self.prepare_insert(insert)?;
812 match prepared {
813 PreparedInsert::Values {
814 table_name,
815 columns,
816 rows,
817 on_conflict,
818 } => {
819 let mut flushed = Vec::new();
820 let statement_rows = rows.len();
821 let mut buf = self.insert_buffer.borrow_mut();
822 match buf.as_mut() {
823 Some(buffer) if buffer.can_accept(&table_name, &columns, on_conflict) => {
824 buffer.push_statement(rows);
825 if buffer.should_flush() {
826 drop(buf);
827 flushed = self.flush_buffer_results()?;
828 return Ok(BufferedInsertResult {
829 flushed,
830 current: None,
831 });
832 }
833 Ok(BufferedInsertResult {
834 flushed,
835 current: Some(RuntimeStatementResult::Insert {
836 table_name,
837 rows_inserted: statement_rows,
838 }),
839 })
840 }
841 Some(_) => {
842 drop(buf);
843 flushed = self.flush_buffer_results()?;
844 let mut buf = self.insert_buffer.borrow_mut();
845 *buf = Some(InsertBuffer::new(
846 table_name.clone(),
847 columns,
848 rows,
849 on_conflict,
850 ));
851 Ok(BufferedInsertResult {
852 flushed,
853 current: Some(RuntimeStatementResult::Insert {
854 table_name,
855 rows_inserted: statement_rows,
856 }),
857 })
858 }
859 None => {
860 *buf = Some(InsertBuffer::new(
861 table_name.clone(),
862 columns,
863 rows,
864 on_conflict,
865 ));
866 Ok(BufferedInsertResult {
867 flushed,
868 current: Some(RuntimeStatementResult::Insert {
869 table_name,
870 rows_inserted: statement_rows,
871 }),
872 })
873 }
874 }
875 }
876 PreparedInsert::Immediate(plan) => {
877 let flushed = self.flush_buffer_results()?;
878 let executed = self.execute_plan_statement(PlanStatement::Insert(plan))?;
879 Ok(BufferedInsertResult {
880 flushed,
881 current: Some(executed),
882 })
883 }
884 }
885 }
886
887 fn flush_buffer_results(&self) -> SqlResult<Vec<SqlStatementResult>> {
889 let mut buf = self.insert_buffer.borrow_mut();
890 let buffer = match buf.take() {
891 Some(b) => b,
892 None => return Ok(Vec::new()),
893 };
894 drop(buf);
895
896 let InsertBuffer {
897 table_name,
898 columns,
899 on_conflict,
900 total_rows,
901 statement_row_counts,
902 rows,
903 } = buffer;
904
905 if total_rows == 0 {
906 return Ok(Vec::new());
907 }
908
909 let plan = InsertPlan {
910 table: table_name.clone(),
911 columns,
912 source: InsertSource::Rows(rows),
913 on_conflict,
914 };
915
916 let executed = self.execute_plan_statement(PlanStatement::Insert(plan))?;
917 let inserted = match executed {
918 RuntimeStatementResult::Insert { rows_inserted, .. } => {
919 if rows_inserted != total_rows {
920 tracing::warn!(
921 "Buffered INSERT row count mismatch: expected {}, runtime inserted {}",
922 total_rows,
923 rows_inserted
924 );
925 }
926 rows_inserted
927 }
928 other => {
929 return Err(Error::Internal(format!(
930 "expected Insert result when flushing buffer, got {other:?}"
931 )));
932 }
933 };
934
935 let mut per_statement = Vec::with_capacity(statement_row_counts.len());
936 let mut assigned = 0usize;
937 for rows in statement_row_counts {
938 assigned += rows;
939 per_statement.push(RuntimeStatementResult::Insert {
940 table_name: table_name.clone(),
941 rows_inserted: rows,
942 });
943 }
944
945 if inserted != assigned {
946 tracing::warn!(
947 "Buffered INSERT per-statement totals ({}) do not match runtime ({}).",
948 assigned,
949 inserted
950 );
951 }
952
953 Ok(per_statement)
954 }
955
956 fn prepare_insert(&self, stmt: sqlparser::ast::Insert) -> SqlResult<PreparedInsert> {
970 let table_name_debug =
971 Self::table_name_from_insert(&stmt).unwrap_or_else(|_| "unknown".to_string());
972 tracing::trace!(
973 "DEBUG SQL prepare_insert called for table={}",
974 table_name_debug
975 );
976
977 if !self.engine.session().has_active_transaction()
978 && self.is_table_marked_dropped(&table_name_debug)?
979 {
980 return Err(Error::TransactionContextError(
981 DROPPED_TABLE_TRANSACTION_ERR.into(),
982 ));
983 }
984
985 use sqlparser::ast::SqliteOnConflict;
987 let on_conflict = if stmt.replace_into {
988 InsertConflictAction::Replace
989 } else if stmt.ignore {
990 InsertConflictAction::Ignore
991 } else if let Some(or_clause) = stmt.or {
992 match or_clause {
993 SqliteOnConflict::Replace => InsertConflictAction::Replace,
994 SqliteOnConflict::Ignore => InsertConflictAction::Ignore,
995 SqliteOnConflict::Abort => InsertConflictAction::Abort,
996 SqliteOnConflict::Fail => InsertConflictAction::Fail,
997 SqliteOnConflict::Rollback => InsertConflictAction::Rollback,
998 }
999 } else {
1000 InsertConflictAction::None
1001 };
1002
1003 if stmt.overwrite {
1004 return Err(Error::InvalidArgumentError(
1005 "INSERT OVERWRITE is not supported".into(),
1006 ));
1007 }
1008 if !stmt.assignments.is_empty() {
1009 return Err(Error::InvalidArgumentError(
1010 "INSERT ... SET is not supported".into(),
1011 ));
1012 }
1013 if stmt.partitioned.is_some() || !stmt.after_columns.is_empty() {
1014 return Err(Error::InvalidArgumentError(
1015 "partitioned INSERT is not supported".into(),
1016 ));
1017 }
1018 if stmt.returning.is_some() {
1019 return Err(Error::InvalidArgumentError(
1020 "INSERT ... RETURNING is not supported".into(),
1021 ));
1022 }
1023 if stmt.format_clause.is_some() || stmt.settings.is_some() {
1024 return Err(Error::InvalidArgumentError(
1025 "INSERT with FORMAT or SETTINGS is not supported".into(),
1026 ));
1027 }
1028
1029 let (display_name, _canonical_name) = match &stmt.table {
1030 TableObject::TableName(name) => canonical_object_name(name)?,
1031 _ => {
1032 return Err(Error::InvalidArgumentError(
1033 "INSERT requires a plain table name".into(),
1034 ));
1035 }
1036 };
1037
1038 let columns: Vec<String> = stmt
1039 .columns
1040 .iter()
1041 .map(|ident| ident.value.clone())
1042 .collect();
1043
1044 let source_expr = stmt
1045 .source
1046 .as_ref()
1047 .ok_or_else(|| Error::InvalidArgumentError("INSERT requires a VALUES clause".into()))?;
1048 validate_simple_query(source_expr)?;
1049
1050 match source_expr.body.as_ref() {
1051 SetExpr::Values(values) => {
1052 if values.rows.is_empty() {
1053 return Err(Error::InvalidArgumentError(
1054 "INSERT VALUES list must contain at least one row".into(),
1055 ));
1056 }
1057 let mut rows: Vec<Vec<PlanValue>> = Vec::with_capacity(values.rows.len());
1058 for row in &values.rows {
1059 let mut converted = Vec::with_capacity(row.len());
1060 for expr in row {
1061 converted.push(PlanValue::from(SqlValue::try_from_expr(expr)?));
1062 }
1063 rows.push(converted);
1064 }
1065 Ok(PreparedInsert::Values {
1066 table_name: display_name,
1067 columns,
1068 rows,
1069 on_conflict,
1070 })
1071 }
1072 SetExpr::Select(select) => {
1073 if let Some(rows) = extract_constant_select_rows(select.as_ref())? {
1074 return Ok(PreparedInsert::Values {
1075 table_name: display_name,
1076 columns,
1077 rows,
1078 on_conflict,
1079 });
1080 }
1081 if let Some(range_rows) = extract_rows_from_range(select.as_ref())? {
1082 return Ok(PreparedInsert::Values {
1083 table_name: display_name,
1084 columns,
1085 rows: range_rows.into_rows(),
1086 on_conflict,
1087 });
1088 }
1089
1090 let select_plan = self.build_select_plan((**source_expr).clone())?;
1091 Ok(PreparedInsert::Immediate(InsertPlan {
1092 table: display_name,
1093 columns,
1094 source: InsertSource::Select {
1095 plan: Box::new(select_plan),
1096 },
1097 on_conflict,
1098 }))
1099 }
1100 _ => Err(Error::InvalidArgumentError(
1101 "unsupported INSERT source".into(),
1102 )),
1103 }
1104 }
1105
1106 pub fn sql(&self, sql: &str) -> SqlResult<Vec<RecordBatch>> {
1143 let mut results = self.execute(sql)?;
1144 if results.is_empty() {
1145 return Err(Error::InvalidArgumentError(
1146 "SqlEngine::sql expects a SELECT statement".into(),
1147 ));
1148 }
1149
1150 let primary = results.remove(0);
1151
1152 match primary {
1153 RuntimeStatementResult::Select { execution, .. } => execution.collect(),
1154 other => Err(Error::InvalidArgumentError(format!(
1155 "SqlEngine::sql requires a SELECT statement, got {other:?}",
1156 ))),
1157 }
1158 }
1159
1160 fn execute_statement(&self, statement: Statement) -> SqlResult<SqlStatementResult> {
1161 let statement_sql = statement.to_string();
1162 let _query_label_guard = push_query_label(statement_sql.clone());
1163 tracing::debug!("SQL execute_statement: {}", statement_sql.trim());
1164 tracing::trace!(
1165 "DEBUG SQL execute_statement: {:?}",
1166 match &statement {
1167 Statement::Insert(insert) =>
1168 format!("Insert(table={:?})", Self::table_name_from_insert(insert)),
1169 Statement::Query(_) => "Query".to_string(),
1170 Statement::StartTransaction { .. } => "StartTransaction".to_string(),
1171 Statement::Commit { .. } => "Commit".to_string(),
1172 Statement::Rollback { .. } => "Rollback".to_string(),
1173 Statement::CreateTable(_) => "CreateTable".to_string(),
1174 Statement::Update { .. } => "Update".to_string(),
1175 Statement::Delete(_) => "Delete".to_string(),
1176 other => format!("Other({:?})", other),
1177 }
1178 );
1179 match statement {
1180 Statement::StartTransaction {
1181 modes,
1182 begin,
1183 transaction,
1184 modifier,
1185 statements,
1186 exception,
1187 has_end_keyword,
1188 } => self.handle_start_transaction(
1189 modes,
1190 begin,
1191 transaction,
1192 modifier,
1193 statements,
1194 exception,
1195 has_end_keyword,
1196 ),
1197 Statement::Commit {
1198 chain,
1199 end,
1200 modifier,
1201 } => self.handle_commit(chain, end, modifier),
1202 Statement::Rollback { chain, savepoint } => self.handle_rollback(chain, savepoint),
1203 other => self.execute_statement_non_transactional(other),
1204 }
1205 }
1206
1207 fn execute_statement_non_transactional(
1208 &self,
1209 statement: Statement,
1210 ) -> SqlResult<SqlStatementResult> {
1211 tracing::trace!("DEBUG SQL execute_statement_non_transactional called");
1212 match statement {
1213 Statement::CreateTable(stmt) => {
1214 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateTable");
1215 self.handle_create_table(stmt)
1216 }
1217 Statement::CreateIndex(stmt) => {
1218 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateIndex");
1219 self.handle_create_index(stmt)
1220 }
1221 Statement::CreateSchema {
1222 schema_name,
1223 if_not_exists,
1224 with,
1225 options,
1226 default_collate_spec,
1227 clone,
1228 } => {
1229 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateSchema");
1230 self.handle_create_schema(
1231 schema_name,
1232 if_not_exists,
1233 with,
1234 options,
1235 default_collate_spec,
1236 clone,
1237 )
1238 }
1239 Statement::CreateView {
1240 name,
1241 columns,
1242 query,
1243 materialized,
1244 or_replace,
1245 or_alter,
1246 options,
1247 cluster_by,
1248 comment,
1249 with_no_schema_binding,
1250 if_not_exists,
1251 temporary,
1252 to,
1253 params,
1254 secure,
1255 name_before_not_exists,
1256 } => {
1257 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateView");
1258 self.handle_create_view(
1259 name,
1260 columns,
1261 query,
1262 materialized,
1263 or_replace,
1264 or_alter,
1265 options,
1266 cluster_by,
1267 comment,
1268 with_no_schema_binding,
1269 if_not_exists,
1270 temporary,
1271 to,
1272 params,
1273 secure,
1274 name_before_not_exists,
1275 )
1276 }
1277 Statement::CreateDomain(create_domain) => {
1278 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateDomain");
1279 self.handle_create_domain(create_domain)
1280 }
1281 Statement::CreateTrigger(create_trigger) => {
1282 tracing::trace!("DEBUG SQL execute_statement_non_transactional: CreateTrigger");
1283 self.handle_create_trigger(create_trigger)
1284 }
1285 Statement::DropDomain(drop_domain) => {
1286 tracing::trace!("DEBUG SQL execute_statement_non_transactional: DropDomain");
1287 self.handle_drop_domain(drop_domain)
1288 }
1289 Statement::Insert(stmt) => {
1290 let table_name =
1291 Self::table_name_from_insert(&stmt).unwrap_or_else(|_| "unknown".to_string());
1292 tracing::trace!(
1293 "DEBUG SQL execute_statement_non_transactional: Insert(table={})",
1294 table_name
1295 );
1296 self.handle_insert(stmt)
1297 }
1298 Statement::Query(query) => {
1299 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Query");
1300 self.handle_query(*query)
1301 }
1302 Statement::Update {
1303 table,
1304 assignments,
1305 from,
1306 selection,
1307 returning,
1308 ..
1309 } => {
1310 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Update");
1311 self.handle_update(table, assignments, from, selection, returning)
1312 }
1313 Statement::Delete(delete) => {
1314 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Delete");
1315 self.handle_delete(delete)
1316 }
1317 Statement::Truncate {
1318 ref table_names,
1319 ref partitions,
1320 table,
1321 ref identity,
1322 cascade,
1323 ref on_cluster,
1324 } => {
1325 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Truncate");
1326 self.handle_truncate(
1327 table_names,
1328 partitions,
1329 table,
1330 identity,
1331 cascade,
1332 on_cluster,
1333 )
1334 }
1335 Statement::Drop {
1336 object_type,
1337 if_exists,
1338 names,
1339 cascade,
1340 restrict,
1341 purge,
1342 temporary,
1343 ..
1344 } => {
1345 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Drop");
1346 self.handle_drop(
1347 object_type,
1348 if_exists,
1349 names,
1350 cascade,
1351 restrict,
1352 purge,
1353 temporary,
1354 )
1355 }
1356 Statement::DropTrigger(drop_trigger) => {
1357 tracing::trace!("DEBUG SQL execute_statement_non_transactional: DropTrigger");
1358 self.handle_drop_trigger(drop_trigger)
1359 }
1360 Statement::AlterTable {
1361 name,
1362 if_exists,
1363 only,
1364 operations,
1365 ..
1366 } => {
1367 tracing::trace!("DEBUG SQL execute_statement_non_transactional: AlterTable");
1368 self.handle_alter_table(name, if_exists, only, operations)
1369 }
1370 Statement::Set(set_stmt) => {
1371 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Set");
1372 self.handle_set(set_stmt)
1373 }
1374 Statement::Pragma { name, value, is_eq } => {
1375 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Pragma");
1376 self.handle_pragma(name, value, is_eq)
1377 }
1378 Statement::Vacuum(vacuum) => {
1379 tracing::trace!("DEBUG SQL execute_statement_non_transactional: Vacuum");
1380 self.handle_vacuum(vacuum)
1381 }
1382 other => {
1383 tracing::trace!(
1384 "DEBUG SQL execute_statement_non_transactional: Other({:?})",
1385 other
1386 );
1387 Err(Error::InvalidArgumentError(format!(
1388 "unsupported SQL statement: {other:?}"
1389 )))
1390 }
1391 }
1392 }
1393
1394 fn table_name_from_insert(insert: &sqlparser::ast::Insert) -> SqlResult<String> {
1395 match &insert.table {
1396 TableObject::TableName(name) => Self::object_name_to_string(name),
1397 _ => Err(Error::InvalidArgumentError(
1398 "INSERT requires a plain table name".into(),
1399 )),
1400 }
1401 }
1402
1403 fn table_name_from_update(table: &TableWithJoins) -> SqlResult<Option<String>> {
1404 if !table.joins.is_empty() {
1405 return Err(Error::InvalidArgumentError(
1406 "UPDATE with JOIN targets is not supported yet".into(),
1407 ));
1408 }
1409 Self::table_with_joins_name(table)
1410 }
1411
1412 fn table_name_from_delete(delete: &Delete) -> SqlResult<Option<String>> {
1413 if !delete.tables.is_empty() {
1414 return Err(Error::InvalidArgumentError(
1415 "multi-table DELETE is not supported yet".into(),
1416 ));
1417 }
1418 let from_tables = match &delete.from {
1419 FromTable::WithFromKeyword(tables) | FromTable::WithoutKeyword(tables) => tables,
1420 };
1421 if from_tables.is_empty() {
1422 return Ok(None);
1423 }
1424 if from_tables.len() != 1 {
1425 return Err(Error::InvalidArgumentError(
1426 "DELETE over multiple tables is not supported yet".into(),
1427 ));
1428 }
1429 Self::table_with_joins_name(&from_tables[0])
1430 }
1431
1432 fn object_name_to_string(name: &ObjectName) -> SqlResult<String> {
1433 let (display, _) = canonical_object_name(name)?;
1434 Ok(display)
1435 }
1436
1437 #[allow(dead_code)]
1438 fn table_object_to_name(table: &TableObject) -> SqlResult<Option<String>> {
1439 match table {
1440 TableObject::TableName(name) => Ok(Some(Self::object_name_to_string(name)?)),
1441 TableObject::TableFunction(_) => Ok(None),
1442 }
1443 }
1444
1445 fn table_with_joins_name(table: &TableWithJoins) -> SqlResult<Option<String>> {
1446 match &table.relation {
1447 TableFactor::Table { name, .. } => Ok(Some(Self::object_name_to_string(name)?)),
1448 _ => Ok(None),
1449 }
1450 }
1451
1452 fn tables_in_query(query: &Query) -> SqlResult<Vec<String>> {
1453 let mut tables = Vec::new();
1454 if let sqlparser::ast::SetExpr::Select(select) = query.body.as_ref() {
1455 for table in &select.from {
1456 if let TableFactor::Table { name, .. } = &table.relation {
1457 tables.push(Self::object_name_to_string(name)?);
1458 }
1459 }
1460 }
1461 Ok(tables)
1462 }
1463
1464 fn collect_known_columns(
1465 &self,
1466 display_name: &str,
1467 canonical_name: &str,
1468 ) -> SqlResult<FxHashSet<String>> {
1469 let context = self.engine.context();
1470
1471 if context.is_table_marked_dropped(canonical_name) {
1472 return Err(Self::table_not_found_error(display_name));
1473 }
1474
1475 if let Some(specs) = self
1477 .engine
1478 .session()
1479 .table_column_specs_from_transaction(canonical_name)
1480 {
1481 return Ok(specs
1482 .into_iter()
1483 .map(|spec| spec.name.to_ascii_lowercase())
1484 .collect());
1485 }
1486
1487 let (_, canonical_name) = llkv_table::canonical_table_name(display_name)
1489 .map_err(|e| arrow::error::ArrowError::ExternalError(Box::new(e)))?;
1490
1491 match context.catalog().table_column_specs(&canonical_name) {
1492 Ok(specs) if !specs.is_empty() => Ok(specs
1493 .into_iter()
1494 .map(|spec| spec.name.to_ascii_lowercase())
1495 .collect()),
1496 Ok(_) => {
1497 if let Some(table_id) = context.catalog().table_id(&canonical_name)
1498 && let Some(resolver) = context.catalog().field_resolver(table_id)
1499 {
1500 let fallback: FxHashSet<String> = resolver
1501 .field_names()
1502 .into_iter()
1503 .map(|name| name.to_ascii_lowercase())
1504 .collect();
1505 tracing::debug!(
1506 "collect_known_columns: using resolver fallback for '{}': {:?}",
1507 display_name,
1508 fallback
1509 );
1510 return Ok(fallback);
1511 }
1512 Ok(FxHashSet::default())
1513 }
1514 Err(err) => {
1515 if Self::is_table_missing_error(&err) {
1516 Ok(FxHashSet::default())
1517 } else {
1518 Err(Self::map_table_error(display_name, err))
1519 }
1520 }
1521 }
1522 }
1523
1524 fn parse_view_query(sql: &str) -> SqlResult<Query> {
1525 use sqlparser::ast::Statement;
1526
1527 let dialect = GenericDialect {};
1528 let mut statements = Parser::parse_sql(&dialect, sql).map_err(|err| {
1529 Error::InvalidArgumentError(format!("failed to parse view definition: {}", err))
1530 })?;
1531
1532 if statements.len() != 1 {
1533 return Err(Error::InvalidArgumentError(
1534 "view definition must contain a single SELECT statement".into(),
1535 ));
1536 }
1537
1538 match statements.pop().unwrap() {
1539 Statement::Query(query) => Ok(*query),
1540 _ => Err(Error::InvalidArgumentError(
1541 "view definition must be expressed as a SELECT query".into(),
1542 )),
1543 }
1544 }
1545
1546 fn is_table_marked_dropped(&self, table_name: &str) -> SqlResult<bool> {
1547 let canonical = table_name.to_ascii_lowercase();
1548 Ok(self.engine.context().is_table_marked_dropped(&canonical))
1549 }
1550
1551 fn handle_create_table(
1552 &self,
1553 mut stmt: sqlparser::ast::CreateTable,
1554 ) -> SqlResult<SqlStatementResult> {
1555 validate_create_table_common(&stmt)?;
1556
1557 let (mut schema_name, table_name) = parse_schema_qualified_name(&stmt.name)?;
1558
1559 let namespace = if stmt.temporary {
1560 if schema_name.is_some() {
1561 return Err(Error::InvalidArgumentError(
1562 "temporary tables cannot specify an explicit schema".into(),
1563 ));
1564 }
1565 schema_name = None;
1566 Some(TEMPORARY_NAMESPACE_ID.to_string())
1567 } else {
1568 None
1569 };
1570
1571 if let Some(ref schema) = schema_name {
1573 let catalog = self.engine.context().table_catalog();
1574 if !catalog.schema_exists(schema) {
1575 return Err(Error::CatalogError(format!(
1576 "Schema '{}' does not exist",
1577 schema
1578 )));
1579 }
1580 }
1581
1582 let display_name = match &schema_name {
1584 Some(schema) => format!("{}.{}", schema, table_name),
1585 None => table_name.clone(),
1586 };
1587 let canonical_name = display_name.to_ascii_lowercase();
1588 tracing::trace!(
1589 "\n=== HANDLE_CREATE_TABLE: table='{}' columns={} ===",
1590 display_name,
1591 stmt.columns.len()
1592 );
1593 if display_name.is_empty() {
1594 return Err(Error::InvalidArgumentError(
1595 "table name must not be empty".into(),
1596 ));
1597 }
1598
1599 if let Some(query) = stmt.query.take() {
1600 validate_create_table_as(&stmt)?;
1601 if let Some(result) = self.try_handle_range_ctas(
1602 &display_name,
1603 &canonical_name,
1604 &query,
1605 stmt.if_not_exists,
1606 stmt.or_replace,
1607 namespace.clone(),
1608 )? {
1609 return Ok(result);
1610 }
1611 return self.handle_create_table_as(
1612 display_name,
1613 canonical_name,
1614 *query,
1615 stmt.if_not_exists,
1616 stmt.or_replace,
1617 namespace.clone(),
1618 );
1619 }
1620
1621 if stmt.columns.is_empty() {
1622 return Err(Error::InvalidArgumentError(
1623 "CREATE TABLE requires at least one column".into(),
1624 ));
1625 }
1626
1627 validate_create_table_definition(&stmt)?;
1628
1629 let column_defs_ast = std::mem::take(&mut stmt.columns);
1630 let constraints = std::mem::take(&mut stmt.constraints);
1631
1632 let column_names: Vec<String> = column_defs_ast
1633 .iter()
1634 .map(|column_def| column_def.name.value.clone())
1635 .collect();
1636 ensure_unique_case_insensitive(column_names.iter().map(|name| name.as_str()), |dup| {
1637 format!(
1638 "duplicate column name '{}' in table '{}'",
1639 dup, display_name
1640 )
1641 })?;
1642 let column_names_lower: FxHashSet<String> = column_names
1643 .iter()
1644 .map(|name| name.to_ascii_lowercase())
1645 .collect();
1646
1647 let mut columns: Vec<PlanColumnSpec> = Vec::with_capacity(column_defs_ast.len());
1648 let mut primary_key_columns: FxHashSet<String> = FxHashSet::default();
1649 let mut foreign_keys: Vec<ForeignKeySpec> = Vec::new();
1650 let mut multi_column_uniques: Vec<MultiColumnUniqueSpec> = Vec::new();
1651
1652 for column_def in column_defs_ast {
1654 let is_nullable = column_def
1655 .options
1656 .iter()
1657 .all(|opt| !matches!(opt.option, ColumnOption::NotNull));
1658
1659 let is_primary_key = column_def.options.iter().any(|opt| {
1660 matches!(
1661 opt.option,
1662 ColumnOption::Unique {
1663 is_primary: true,
1664 characteristics: _
1665 }
1666 )
1667 });
1668
1669 let has_unique_constraint = column_def
1670 .options
1671 .iter()
1672 .any(|opt| matches!(opt.option, ColumnOption::Unique { .. }));
1673
1674 let check_expr = column_def.options.iter().find_map(|opt| {
1676 if let ColumnOption::Check(expr) = &opt.option {
1677 Some(expr)
1678 } else {
1679 None
1680 }
1681 });
1682
1683 if let Some(check_expr) = check_expr {
1685 let all_col_refs: Vec<&str> = column_names.iter().map(|s| s.as_str()).collect();
1686 validate_check_constraint(check_expr, &display_name, &all_col_refs)?;
1687 }
1688
1689 let check_expr_str = check_expr.map(|e| e.to_string());
1690
1691 for opt in &column_def.options {
1693 if let ColumnOption::ForeignKey {
1694 foreign_table,
1695 referred_columns,
1696 on_delete,
1697 on_update,
1698 characteristics,
1699 } = &opt.option
1700 {
1701 let spec = self.build_foreign_key_spec(
1702 &display_name,
1703 &canonical_name,
1704 vec![column_def.name.value.clone()],
1705 foreign_table,
1706 referred_columns,
1707 *on_delete,
1708 *on_update,
1709 characteristics,
1710 &column_names_lower,
1711 None,
1712 )?;
1713 foreign_keys.push(spec);
1714 }
1715 }
1716
1717 tracing::trace!(
1718 "DEBUG CREATE TABLE column '{}' is_primary_key={} has_unique={} check_expr={:?}",
1719 column_def.name.value,
1720 is_primary_key,
1721 has_unique_constraint,
1722 check_expr_str
1723 );
1724
1725 let resolved_data_type = self.engine.context().resolve_type(&column_def.data_type);
1727
1728 let mut column = PlanColumnSpec::new(
1729 column_def.name.value.clone(),
1730 arrow_type_from_sql(&resolved_data_type)?,
1731 is_nullable,
1732 );
1733 tracing::trace!(
1734 "DEBUG PlanColumnSpec after new(): primary_key={} unique={}",
1735 column.primary_key,
1736 column.unique
1737 );
1738
1739 column = column
1740 .with_primary_key(is_primary_key)
1741 .with_unique(has_unique_constraint)
1742 .with_check(check_expr_str);
1743
1744 if is_primary_key {
1745 column.nullable = false;
1746 primary_key_columns.insert(column.name.to_ascii_lowercase());
1747 }
1748 tracing::trace!(
1749 "DEBUG PlanColumnSpec after with_primary_key({})/with_unique({}): primary_key={} unique={} check_expr={:?}",
1750 is_primary_key,
1751 has_unique_constraint,
1752 column.primary_key,
1753 column.unique,
1754 column.check_expr
1755 );
1756
1757 columns.push(column);
1758 }
1759
1760 if !constraints.is_empty() {
1762 let mut column_lookup: FxHashMap<String, usize> =
1763 FxHashMap::with_capacity_and_hasher(columns.len(), Default::default());
1764 for (idx, column) in columns.iter().enumerate() {
1765 column_lookup.insert(column.name.to_ascii_lowercase(), idx);
1766 }
1767
1768 for constraint in constraints {
1769 match constraint {
1770 TableConstraint::PrimaryKey {
1771 columns: constraint_columns,
1772 ..
1773 } => {
1774 if !primary_key_columns.is_empty() {
1775 return Err(Error::InvalidArgumentError(
1776 "multiple PRIMARY KEY constraints are not supported".into(),
1777 ));
1778 }
1779
1780 ensure_non_empty(&constraint_columns, || {
1781 "PRIMARY KEY requires at least one column".into()
1782 })?;
1783
1784 let mut pk_column_names: Vec<String> =
1785 Vec::with_capacity(constraint_columns.len());
1786
1787 for index_col in &constraint_columns {
1788 let column_ident = extract_index_column_name(
1789 index_col,
1790 "PRIMARY KEY",
1791 false, false, )?;
1794 pk_column_names.push(column_ident);
1795 }
1796
1797 ensure_unique_case_insensitive(
1798 pk_column_names.iter().map(|name| name.as_str()),
1799 |dup| format!("duplicate column '{}' in PRIMARY KEY constraint", dup),
1800 )?;
1801
1802 ensure_known_columns_case_insensitive(
1803 pk_column_names.iter().map(|name| name.as_str()),
1804 &column_names_lower,
1805 |unknown| {
1806 format!("unknown column '{}' in PRIMARY KEY constraint", unknown)
1807 },
1808 )?;
1809
1810 for column_ident in pk_column_names {
1811 let normalized = column_ident.to_ascii_lowercase();
1812 let idx = column_lookup.get(&normalized).copied().ok_or_else(|| {
1813 Error::InvalidArgumentError(format!(
1814 "unknown column '{}' in PRIMARY KEY constraint",
1815 column_ident
1816 ))
1817 })?;
1818
1819 let column = columns.get_mut(idx).expect("column index valid");
1820 column.primary_key = true;
1821 column.unique = true;
1822 column.nullable = false;
1823
1824 primary_key_columns.insert(normalized);
1825 }
1826 }
1827 TableConstraint::Unique {
1828 columns: constraint_columns,
1829 index_type,
1830 index_options,
1831 characteristics,
1832 nulls_distinct,
1833 name,
1834 ..
1835 } => {
1836 if !matches!(nulls_distinct, NullsDistinctOption::None) {
1837 return Err(Error::InvalidArgumentError(
1838 "UNIQUE constraints with NULLS DISTINCT/NOT DISTINCT are not supported yet".into(),
1839 ));
1840 }
1841
1842 if index_type.is_some() {
1843 return Err(Error::InvalidArgumentError(
1844 "UNIQUE constraints with index types are not supported yet".into(),
1845 ));
1846 }
1847
1848 if !index_options.is_empty() {
1849 return Err(Error::InvalidArgumentError(
1850 "UNIQUE constraints with index options are not supported yet"
1851 .into(),
1852 ));
1853 }
1854
1855 if characteristics.is_some() {
1856 return Err(Error::InvalidArgumentError(
1857 "UNIQUE constraint characteristics are not supported yet".into(),
1858 ));
1859 }
1860
1861 ensure_non_empty(&constraint_columns, || {
1862 "UNIQUE constraint requires at least one column".into()
1863 })?;
1864
1865 let mut unique_column_names: Vec<String> =
1866 Vec::with_capacity(constraint_columns.len());
1867
1868 for index_column in &constraint_columns {
1869 let column_ident = extract_index_column_name(
1870 index_column,
1871 "UNIQUE constraint",
1872 false, false, )?;
1875 unique_column_names.push(column_ident);
1876 }
1877
1878 ensure_unique_case_insensitive(
1879 unique_column_names.iter().map(|name| name.as_str()),
1880 |dup| format!("duplicate column '{}' in UNIQUE constraint", dup),
1881 )?;
1882
1883 ensure_known_columns_case_insensitive(
1884 unique_column_names.iter().map(|name| name.as_str()),
1885 &column_names_lower,
1886 |unknown| format!("unknown column '{}' in UNIQUE constraint", unknown),
1887 )?;
1888
1889 if unique_column_names.len() > 1 {
1890 multi_column_uniques.push(MultiColumnUniqueSpec {
1892 name: name.map(|n| n.value),
1893 columns: unique_column_names,
1894 });
1895 } else {
1896 let column_ident = unique_column_names
1898 .into_iter()
1899 .next()
1900 .expect("unique constraint checked for emptiness");
1901 let normalized = column_ident.to_ascii_lowercase();
1902 let idx = column_lookup.get(&normalized).copied().ok_or_else(|| {
1903 Error::InvalidArgumentError(format!(
1904 "unknown column '{}' in UNIQUE constraint",
1905 column_ident
1906 ))
1907 })?;
1908
1909 let column = columns
1910 .get_mut(idx)
1911 .expect("column index from lookup must be valid");
1912 column.unique = true;
1913 }
1914 }
1915 TableConstraint::ForeignKey {
1916 name,
1917 index_name,
1918 columns: fk_columns,
1919 foreign_table,
1920 referred_columns,
1921 on_delete,
1922 on_update,
1923 characteristics,
1924 ..
1925 } => {
1926 if index_name.is_some() {
1927 return Err(Error::InvalidArgumentError(
1928 "FOREIGN KEY index clauses are not supported yet".into(),
1929 ));
1930 }
1931
1932 let referencing_columns: Vec<String> =
1933 fk_columns.into_iter().map(|ident| ident.value).collect();
1934 let spec = self.build_foreign_key_spec(
1935 &display_name,
1936 &canonical_name,
1937 referencing_columns,
1938 &foreign_table,
1939 &referred_columns,
1940 on_delete,
1941 on_update,
1942 &characteristics,
1943 &column_names_lower,
1944 name.map(|ident| ident.value),
1945 )?;
1946
1947 foreign_keys.push(spec);
1948 }
1949 unsupported => {
1950 return Err(Error::InvalidArgumentError(format!(
1951 "table-level constraint {:?} is not supported",
1952 unsupported
1953 )));
1954 }
1955 }
1956 }
1957 }
1958
1959 let plan = CreateTablePlan {
1960 name: display_name,
1961 if_not_exists: stmt.if_not_exists,
1962 or_replace: stmt.or_replace,
1963 columns,
1964 source: None,
1965 namespace,
1966 foreign_keys,
1967 multi_column_uniques,
1968 };
1969 self.execute_plan_statement(PlanStatement::CreateTable(plan))
1970 }
1971
1972 fn handle_create_index(
1973 &self,
1974 stmt: sqlparser::ast::CreateIndex,
1975 ) -> SqlResult<RuntimeStatementResult<P>> {
1976 let sqlparser::ast::CreateIndex {
1977 name,
1978 table_name,
1979 using,
1980 columns,
1981 unique,
1982 concurrently,
1983 if_not_exists,
1984 include,
1985 nulls_distinct,
1986 with,
1987 predicate,
1988 index_options,
1989 alter_options,
1990 ..
1991 } = stmt;
1992
1993 if concurrently {
1994 return Err(Error::InvalidArgumentError(
1995 "CREATE INDEX CONCURRENTLY is not supported".into(),
1996 ));
1997 }
1998 if using.is_some() {
1999 return Err(Error::InvalidArgumentError(
2000 "CREATE INDEX USING clauses are not supported".into(),
2001 ));
2002 }
2003 if !include.is_empty() {
2004 return Err(Error::InvalidArgumentError(
2005 "CREATE INDEX INCLUDE columns are not supported".into(),
2006 ));
2007 }
2008 if nulls_distinct.is_some() {
2009 return Err(Error::InvalidArgumentError(
2010 "CREATE INDEX NULLS DISTINCT is not supported".into(),
2011 ));
2012 }
2013 if !with.is_empty() {
2014 return Err(Error::InvalidArgumentError(
2015 "CREATE INDEX WITH options are not supported".into(),
2016 ));
2017 }
2018 if predicate.is_some() {
2019 return Err(Error::InvalidArgumentError(
2020 "partial CREATE INDEX is not supported".into(),
2021 ));
2022 }
2023 if !index_options.is_empty() {
2024 return Err(Error::InvalidArgumentError(
2025 "CREATE INDEX options are not supported".into(),
2026 ));
2027 }
2028 if !alter_options.is_empty() {
2029 return Err(Error::InvalidArgumentError(
2030 "CREATE INDEX ALTER options are not supported".into(),
2031 ));
2032 }
2033 if columns.is_empty() {
2034 return Err(Error::InvalidArgumentError(
2035 "CREATE INDEX requires at least one column".into(),
2036 ));
2037 }
2038
2039 let (schema_name, base_table_name) = parse_schema_qualified_name(&table_name)?;
2040 if let Some(ref schema) = schema_name {
2041 let catalog = self.engine.context().table_catalog();
2042 if !catalog.schema_exists(schema) {
2043 return Err(Error::CatalogError(format!(
2044 "Schema '{}' does not exist",
2045 schema
2046 )));
2047 }
2048 }
2049
2050 let display_table_name = schema_name
2051 .as_ref()
2052 .map(|schema| format!("{}.{}", schema, base_table_name))
2053 .unwrap_or_else(|| base_table_name.clone());
2054 let canonical_table_name = display_table_name.to_ascii_lowercase();
2055
2056 let known_columns =
2057 self.collect_known_columns(&display_table_name, &canonical_table_name)?;
2058 let enforce_known_columns = !known_columns.is_empty();
2059
2060 let index_name = match name {
2061 Some(name_obj) => Some(Self::object_name_to_string(&name_obj)?),
2062 None => None,
2063 };
2064
2065 let mut index_columns: Vec<IndexColumnPlan> = Vec::with_capacity(columns.len());
2066 let mut seen_column_names: FxHashSet<String> = FxHashSet::default();
2067 for item in columns {
2068 if item.column.with_fill.is_some() {
2070 return Err(Error::InvalidArgumentError(
2071 "CREATE INDEX column WITH FILL is not supported".into(),
2072 ));
2073 }
2074
2075 let column_name = extract_index_column_name(
2076 &item,
2077 "CREATE INDEX",
2078 true, true, )?;
2081
2082 let order_expr = &item.column;
2084 let ascending = order_expr.options.asc.unwrap_or(true);
2085 let nulls_first = order_expr.options.nulls_first.unwrap_or(false);
2086
2087 let normalized = column_name.to_ascii_lowercase();
2088 if !seen_column_names.insert(normalized.clone()) {
2089 return Err(Error::InvalidArgumentError(format!(
2090 "duplicate column '{}' in CREATE INDEX",
2091 column_name
2092 )));
2093 }
2094
2095 if enforce_known_columns && !known_columns.contains(&normalized) {
2096 return Err(Error::InvalidArgumentError(format!(
2097 "column '{}' does not exist in table '{}'",
2098 column_name, display_table_name
2099 )));
2100 }
2101
2102 let column_plan = IndexColumnPlan::new(column_name).with_sort(ascending, nulls_first);
2103 index_columns.push(column_plan);
2104 }
2105
2106 let plan = CreateIndexPlan::new(display_table_name)
2107 .with_name(index_name)
2108 .with_unique(unique)
2109 .with_if_not_exists(if_not_exists)
2110 .with_columns(index_columns);
2111
2112 self.execute_plan_statement(PlanStatement::CreateIndex(plan))
2113 }
2114
2115 fn map_referential_action(
2116 action: Option<ReferentialAction>,
2117 kind: &str,
2118 ) -> SqlResult<ForeignKeyAction> {
2119 match action {
2120 None | Some(ReferentialAction::NoAction) => Ok(ForeignKeyAction::NoAction),
2121 Some(ReferentialAction::Restrict) => Ok(ForeignKeyAction::Restrict),
2122 Some(other) => Err(Error::InvalidArgumentError(format!(
2123 "FOREIGN KEY ON {kind} {:?} is not supported yet",
2124 other
2125 ))),
2126 }
2127 }
2128
2129 #[allow(clippy::too_many_arguments)]
2130 fn build_foreign_key_spec(
2131 &self,
2132 _referencing_display: &str,
2133 referencing_canonical: &str,
2134 referencing_columns: Vec<String>,
2135 foreign_table: &ObjectName,
2136 referenced_columns: &[Ident],
2137 on_delete: Option<ReferentialAction>,
2138 on_update: Option<ReferentialAction>,
2139 characteristics: &Option<ConstraintCharacteristics>,
2140 known_columns_lower: &FxHashSet<String>,
2141 name: Option<String>,
2142 ) -> SqlResult<ForeignKeySpec> {
2143 if characteristics.is_some() {
2144 return Err(Error::InvalidArgumentError(
2145 "FOREIGN KEY constraint characteristics are not supported yet".into(),
2146 ));
2147 }
2148
2149 ensure_non_empty(&referencing_columns, || {
2150 "FOREIGN KEY constraint requires at least one referencing column".into()
2151 })?;
2152 ensure_unique_case_insensitive(
2153 referencing_columns.iter().map(|name| name.as_str()),
2154 |dup| format!("duplicate column '{}' in FOREIGN KEY constraint", dup),
2155 )?;
2156 ensure_known_columns_case_insensitive(
2157 referencing_columns.iter().map(|name| name.as_str()),
2158 known_columns_lower,
2159 |unknown| format!("unknown column '{}' in FOREIGN KEY constraint", unknown),
2160 )?;
2161
2162 let referenced_columns_vec: Vec<String> = referenced_columns
2163 .iter()
2164 .map(|ident| ident.value.clone())
2165 .collect();
2166 ensure_unique_case_insensitive(
2167 referenced_columns_vec.iter().map(|name| name.as_str()),
2168 |dup| {
2169 format!(
2170 "duplicate referenced column '{}' in FOREIGN KEY constraint",
2171 dup
2172 )
2173 },
2174 )?;
2175
2176 if !referenced_columns_vec.is_empty()
2177 && referenced_columns_vec.len() != referencing_columns.len()
2178 {
2179 return Err(Error::InvalidArgumentError(
2180 "FOREIGN KEY referencing and referenced column counts must match".into(),
2181 ));
2182 }
2183
2184 let (referenced_display, referenced_canonical) = canonical_object_name(foreign_table)?;
2185
2186 let catalog = self.engine.context().table_catalog();
2188 if let Some(table_id) = catalog.table_id(&referenced_canonical) {
2189 let context = self.engine.context();
2190 if context.is_view(table_id)? {
2191 return Err(Error::CatalogError(format!(
2192 "Binder Error: cannot reference a VIEW with a FOREIGN KEY: {}",
2193 referenced_display
2194 )));
2195 }
2196 }
2197
2198 if referenced_canonical == referencing_canonical {
2199 ensure_known_columns_case_insensitive(
2200 referenced_columns_vec.iter().map(|name| name.as_str()),
2201 known_columns_lower,
2202 |unknown| {
2203 format!(
2204 "Binder Error: table '{}' does not have a column named '{}'",
2205 referenced_display, unknown
2206 )
2207 },
2208 )?;
2209 } else {
2210 let known_columns =
2211 self.collect_known_columns(&referenced_display, &referenced_canonical)?;
2212 if !known_columns.is_empty() {
2213 ensure_known_columns_case_insensitive(
2214 referenced_columns_vec.iter().map(|name| name.as_str()),
2215 &known_columns,
2216 |unknown| {
2217 format!(
2218 "Binder Error: table '{}' does not have a column named '{}'",
2219 referenced_display, unknown
2220 )
2221 },
2222 )?;
2223 }
2224 }
2225
2226 let on_delete_action = Self::map_referential_action(on_delete, "DELETE")?;
2227 let on_update_action = Self::map_referential_action(on_update, "UPDATE")?;
2228
2229 Ok(ForeignKeySpec {
2230 name,
2231 columns: referencing_columns,
2232 referenced_table: referenced_display,
2233 referenced_columns: referenced_columns_vec,
2234 on_delete: on_delete_action,
2235 on_update: on_update_action,
2236 })
2237 }
2238
2239 fn handle_create_schema(
2240 &self,
2241 schema_name: SchemaName,
2242 _if_not_exists: bool,
2243 with: Option<Vec<SqlOption>>,
2244 options: Option<Vec<SqlOption>>,
2245 default_collate_spec: Option<SqlExpr>,
2246 clone: Option<ObjectName>,
2247 ) -> SqlResult<RuntimeStatementResult<P>> {
2248 if clone.is_some() {
2249 return Err(Error::InvalidArgumentError(
2250 "CREATE SCHEMA ... CLONE is not supported".into(),
2251 ));
2252 }
2253 if with.as_ref().is_some_and(|opts| !opts.is_empty()) {
2254 return Err(Error::InvalidArgumentError(
2255 "CREATE SCHEMA ... WITH options are not supported".into(),
2256 ));
2257 }
2258 if options.as_ref().is_some_and(|opts| !opts.is_empty()) {
2259 return Err(Error::InvalidArgumentError(
2260 "CREATE SCHEMA options are not supported".into(),
2261 ));
2262 }
2263 if default_collate_spec.is_some() {
2264 return Err(Error::InvalidArgumentError(
2265 "CREATE SCHEMA DEFAULT COLLATE is not supported".into(),
2266 ));
2267 }
2268
2269 let schema_name = match schema_name {
2270 SchemaName::Simple(name) => name,
2271 _ => {
2272 return Err(Error::InvalidArgumentError(
2273 "CREATE SCHEMA authorization is not supported".into(),
2274 ));
2275 }
2276 };
2277
2278 let (display_name, canonical) = canonical_object_name(&schema_name)?;
2279 if display_name.is_empty() {
2280 return Err(Error::InvalidArgumentError(
2281 "schema name must not be empty".into(),
2282 ));
2283 }
2284
2285 let catalog = self.engine.context().table_catalog();
2287
2288 if _if_not_exists && catalog.schema_exists(&canonical) {
2289 return Ok(RuntimeStatementResult::NoOp);
2290 }
2291
2292 catalog.register_schema(&canonical).map_err(|err| {
2293 Error::CatalogError(format!(
2294 "Failed to create schema '{}': {}",
2295 display_name, err
2296 ))
2297 })?;
2298
2299 Ok(RuntimeStatementResult::NoOp)
2300 }
2301
2302 #[allow(clippy::too_many_arguments)]
2303 fn handle_create_view(
2304 &self,
2305 name: ObjectName,
2306 columns: Vec<sqlparser::ast::ViewColumnDef>,
2307 query: Box<sqlparser::ast::Query>,
2308 materialized: bool,
2309 or_replace: bool,
2310 or_alter: bool,
2311 _options: sqlparser::ast::CreateTableOptions,
2312 _cluster_by: Vec<sqlparser::ast::Ident>,
2313 _comment: Option<String>,
2314 _with_no_schema_binding: bool,
2315 if_not_exists: bool,
2316 temporary: bool,
2317 _to: Option<ObjectName>,
2318 _params: Option<sqlparser::ast::CreateViewParams>,
2319 _secure: bool,
2320 _name_before_not_exists: bool,
2321 ) -> SqlResult<RuntimeStatementResult<P>> {
2322 if materialized {
2324 return Err(Error::InvalidArgumentError(
2325 "MATERIALIZED VIEWS are not supported".into(),
2326 ));
2327 }
2328 if or_replace {
2329 return Err(Error::InvalidArgumentError(
2330 "CREATE OR REPLACE VIEW is not supported".into(),
2331 ));
2332 }
2333 if or_alter {
2334 return Err(Error::InvalidArgumentError(
2335 "CREATE OR ALTER VIEW is not supported".into(),
2336 ));
2337 }
2338
2339 let (schema_name, view_name) = parse_schema_qualified_name(&name)?;
2341
2342 if let Some(ref schema) = schema_name {
2344 let catalog = self.engine.context().table_catalog();
2345 if !catalog.schema_exists(schema) {
2346 return Err(Error::CatalogError(format!(
2347 "Schema '{}' does not exist",
2348 schema
2349 )));
2350 }
2351 }
2352
2353 let display_name = match &schema_name {
2355 Some(schema) => format!("{}.{}", schema, view_name),
2356 None => view_name.clone(),
2357 };
2358 let canonical_name = display_name.to_ascii_lowercase();
2359
2360 let catalog = self.engine.context().table_catalog();
2362 if catalog.table_exists(&canonical_name) {
2363 if if_not_exists {
2364 return Ok(RuntimeStatementResult::NoOp);
2365 }
2366 return Err(Error::CatalogError(format!(
2367 "Table or view '{}' already exists",
2368 display_name
2369 )));
2370 }
2371
2372 let column_names: Vec<String> = columns
2374 .into_iter()
2375 .map(|column| column.name.value)
2376 .collect();
2377
2378 if !column_names.is_empty() {
2379 ensure_unique_case_insensitive(column_names.iter().map(|name| name.as_str()), |dup| {
2380 format!("duplicate column name '{}' in CREATE VIEW column list", dup)
2381 })?;
2382 }
2383
2384 let mut query_ast = *query;
2385
2386 if !column_names.is_empty() {
2387 let select = match query_ast.body.as_mut() {
2388 sqlparser::ast::SetExpr::Select(select) => select,
2389 _ => {
2390 return Err(Error::InvalidArgumentError(
2391 "CREATE VIEW column list requires a SELECT query".into(),
2392 ));
2393 }
2394 };
2395
2396 for item in &select.projection {
2397 if matches!(
2398 item,
2399 SelectItem::Wildcard(_) | SelectItem::QualifiedWildcard(_, _)
2400 ) {
2401 return Err(Error::InvalidArgumentError(
2402 "CREATE VIEW column lists with wildcard projections are not supported yet"
2403 .into(),
2404 ));
2405 }
2406 }
2407
2408 if select.projection.len() != column_names.len() {
2409 return Err(Error::InvalidArgumentError(format!(
2410 "CREATE VIEW column list specifies {} column(s) but SELECT projection yields {}",
2411 column_names.len(),
2412 select.projection.len()
2413 )));
2414 }
2415
2416 for (item, column_name) in select.projection.iter_mut().zip(column_names.iter()) {
2417 match item {
2418 SelectItem::ExprWithAlias { alias, .. } => {
2419 alias.value = column_name.clone();
2420 }
2421 SelectItem::UnnamedExpr(expr) => {
2422 *item = SelectItem::ExprWithAlias {
2423 expr: expr.clone(),
2424 alias: Ident::new(column_name.clone()),
2425 };
2426 }
2427 _ => {
2428 return Err(Error::InvalidArgumentError(
2429 "CREATE VIEW column list requires simple SELECT projections".into(),
2430 ));
2431 }
2432 }
2433 }
2434 }
2435
2436 let select_plan = self.build_select_plan(query_ast.clone())?;
2438
2439 let view_definition = query_ast.to_string();
2441
2442 let namespace = if temporary {
2444 Some(TEMPORARY_NAMESPACE_ID.to_string())
2445 } else {
2446 None
2447 };
2448
2449 let plan = CreateViewPlan {
2450 name: display_name.clone(),
2451 if_not_exists,
2452 view_definition,
2453 select_plan: Box::new(select_plan),
2454 namespace,
2455 };
2456
2457 self.execute_plan_statement(PlanStatement::CreateView(plan))?;
2458
2459 tracing::debug!("Created view: {}", display_name);
2460 Ok(RuntimeStatementResult::NoOp)
2461 }
2462
2463 fn handle_create_domain(
2464 &self,
2465 create_domain: sqlparser::ast::CreateDomain,
2466 ) -> SqlResult<RuntimeStatementResult<P>> {
2467 use llkv_table::CustomTypeMeta;
2468 use std::time::{SystemTime, UNIX_EPOCH};
2469
2470 let type_name = create_domain.name.to_string();
2472
2473 let base_type_sql = create_domain.data_type.to_string();
2475
2476 self.engine
2478 .context()
2479 .register_type(type_name.clone(), create_domain.data_type.clone());
2480
2481 let context = self.engine.context();
2483 let catalog = llkv_table::SysCatalog::new(context.store());
2484
2485 let created_at_micros = SystemTime::now()
2486 .duration_since(UNIX_EPOCH)
2487 .unwrap_or_default()
2488 .as_micros() as u64;
2489
2490 let meta = CustomTypeMeta {
2491 name: type_name.clone(),
2492 base_type_sql,
2493 created_at_micros,
2494 };
2495
2496 catalog.put_custom_type_meta(&meta)?;
2497
2498 tracing::debug!("Created and persisted type alias: {}", type_name);
2499 Ok(RuntimeStatementResult::NoOp)
2500 }
2501
2502 fn handle_create_trigger(
2503 &self,
2504 create_trigger: CreateTrigger,
2505 ) -> SqlResult<RuntimeStatementResult<P>> {
2506 let CreateTrigger {
2507 or_alter,
2508 or_replace,
2509 is_constraint,
2510 name,
2511 period,
2512 period_before_table: _,
2513 events,
2514 table_name,
2515 referenced_table_name,
2516 referencing,
2517 trigger_object,
2518 include_each: _,
2519 condition,
2520 exec_body,
2521 statements_as,
2522 statements,
2523 characteristics,
2524 } = create_trigger;
2525
2526 if or_alter {
2527 return Err(Error::InvalidArgumentError(
2528 "CREATE OR ALTER TRIGGER is not supported".into(),
2529 ));
2530 }
2531
2532 if or_replace {
2533 return Err(Error::InvalidArgumentError(
2534 "CREATE OR REPLACE TRIGGER is not supported".into(),
2535 ));
2536 }
2537
2538 if is_constraint {
2539 return Err(Error::InvalidArgumentError(
2540 "CREATE TRIGGER ... CONSTRAINT is not supported".into(),
2541 ));
2542 }
2543
2544 if referenced_table_name.is_some() {
2545 return Err(Error::InvalidArgumentError(
2546 "CREATE TRIGGER referencing another table is not supported".into(),
2547 ));
2548 }
2549
2550 if !referencing.is_empty() {
2551 return Err(Error::InvalidArgumentError(
2552 "CREATE TRIGGER REFERENCING clauses are not supported".into(),
2553 ));
2554 }
2555
2556 if characteristics.is_some() {
2557 return Err(Error::InvalidArgumentError(
2558 "CREATE TRIGGER constraint characteristics are not supported".into(),
2559 ));
2560 }
2561
2562 if events.is_empty() {
2563 return Err(Error::InvalidArgumentError(
2564 "CREATE TRIGGER requires at least one event".into(),
2565 ));
2566 }
2567
2568 if events.len() != 1 {
2569 return Err(Error::InvalidArgumentError(
2570 "CREATE TRIGGER currently supports exactly one trigger event".into(),
2571 ));
2572 }
2573
2574 let timing = match period {
2575 TriggerPeriod::Before => TriggerTimingMeta::Before,
2576 TriggerPeriod::After | TriggerPeriod::For => TriggerTimingMeta::After,
2577 TriggerPeriod::InsteadOf => TriggerTimingMeta::InsteadOf,
2578 };
2579
2580 let event_meta = match events.into_iter().next().expect("checked length") {
2581 TriggerEvent::Insert => TriggerEventMeta::Insert,
2582 TriggerEvent::Delete => TriggerEventMeta::Delete,
2583 TriggerEvent::Update(columns) => TriggerEventMeta::Update {
2584 columns: columns
2585 .into_iter()
2586 .map(|ident| ident.value.to_ascii_lowercase())
2587 .collect(),
2588 },
2589 TriggerEvent::Truncate => {
2590 return Err(Error::InvalidArgumentError(
2591 "CREATE TRIGGER for TRUNCATE events is not supported".into(),
2592 ));
2593 }
2594 };
2595
2596 let (trigger_display_name, canonical_trigger_name) = canonical_object_name(&name)?;
2597 let (table_display_name, canonical_table_name) = canonical_object_name(&table_name)?;
2598
2599 let condition_sql = condition.map(|expr| expr.to_string());
2600
2601 let body_sql = if let Some(exec_body) = exec_body {
2602 format!("EXECUTE {exec_body}")
2603 } else if let Some(statements) = statements {
2604 let rendered = statements.to_string();
2605 if statements_as {
2606 format!("AS {rendered}")
2607 } else {
2608 rendered
2609 }
2610 } else {
2611 return Err(Error::InvalidArgumentError(
2612 "CREATE TRIGGER requires a trigger body".into(),
2613 ));
2614 };
2615
2616 let for_each_row = matches!(trigger_object, TriggerObject::Row);
2617
2618 self.engine.context().create_trigger(
2619 &trigger_display_name,
2620 &canonical_trigger_name,
2621 &table_display_name,
2622 &canonical_table_name,
2623 timing,
2624 event_meta,
2625 for_each_row,
2626 condition_sql,
2627 body_sql,
2628 false,
2629 )?;
2630
2631 Ok(RuntimeStatementResult::NoOp)
2632 }
2633
2634 fn handle_drop_domain(
2635 &self,
2636 drop_domain: sqlparser::ast::DropDomain,
2637 ) -> SqlResult<RuntimeStatementResult<P>> {
2638 let if_exists = drop_domain.if_exists;
2639 let type_name = drop_domain.name.to_string();
2640
2641 let result = self.engine.context().drop_type(&type_name);
2643
2644 if let Err(err) = result {
2645 if !if_exists {
2646 return Err(err);
2647 }
2648 } else {
2650 let context = self.engine.context();
2652 let catalog = llkv_table::SysCatalog::new(context.store());
2653 catalog.delete_custom_type_meta(&type_name)?;
2654
2655 tracing::debug!("Dropped and removed from catalog type alias: {}", type_name);
2656 }
2657
2658 Ok(RuntimeStatementResult::NoOp)
2659 }
2660
2661 fn handle_drop_trigger(
2662 &self,
2663 drop_trigger: DropTrigger,
2664 ) -> SqlResult<RuntimeStatementResult<P>> {
2665 let DropTrigger {
2666 if_exists,
2667 trigger_name,
2668 table_name,
2669 option,
2670 } = drop_trigger;
2671
2672 if option.is_some() {
2673 return Err(Error::InvalidArgumentError(
2674 "DROP TRIGGER CASCADE/RESTRICT options are not supported".into(),
2675 ));
2676 }
2677
2678 let (trigger_display_name, canonical_trigger_name) = canonical_object_name(&trigger_name)?;
2679
2680 let (table_display, table_canonical) = if let Some(table_name) = table_name {
2681 let (display, canonical) = canonical_object_name(&table_name)?;
2682 (Some(display), Some(canonical))
2683 } else {
2684 (None, None)
2685 };
2686
2687 let table_display_hint = table_display.as_deref();
2688 let table_canonical_hint = table_canonical.as_deref();
2689
2690 self.engine.context().drop_trigger(
2691 &trigger_display_name,
2692 &canonical_trigger_name,
2693 table_display_hint,
2694 table_canonical_hint,
2695 if_exists,
2696 )?;
2697
2698 Ok(RuntimeStatementResult::NoOp)
2699 }
2700
2701 fn try_handle_range_ctas(
2702 &self,
2703 display_name: &str,
2704 _canonical_name: &str,
2705 query: &Query,
2706 if_not_exists: bool,
2707 or_replace: bool,
2708 namespace: Option<String>,
2709 ) -> SqlResult<Option<RuntimeStatementResult<P>>> {
2710 let select = match query.body.as_ref() {
2711 SetExpr::Select(select) => select,
2712 _ => return Ok(None),
2713 };
2714 if select.from.len() != 1 {
2715 return Ok(None);
2716 }
2717 let table_with_joins = &select.from[0];
2718 if !table_with_joins.joins.is_empty() {
2719 return Ok(None);
2720 }
2721 let (range_size, range_alias) = match &table_with_joins.relation {
2722 TableFactor::Table {
2723 name,
2724 args: Some(args),
2725 alias,
2726 ..
2727 } => {
2728 let func_name = name.to_string().to_ascii_lowercase();
2729 if func_name != "range" {
2730 return Ok(None);
2731 }
2732 if args.args.len() != 1 {
2733 return Err(Error::InvalidArgumentError(
2734 "range table function expects a single argument".into(),
2735 ));
2736 }
2737 let size_expr = &args.args[0];
2738 let range_size = match size_expr {
2739 FunctionArg::Unnamed(FunctionArgExpr::Expr(SqlExpr::Value(value))) => {
2740 match &value.value {
2741 Value::Number(raw, _) => raw.parse::<i64>().map_err(|e| {
2742 Error::InvalidArgumentError(format!(
2743 "invalid range size literal {}: {}",
2744 raw, e
2745 ))
2746 })?,
2747 other => {
2748 return Err(Error::InvalidArgumentError(format!(
2749 "unsupported range size value: {:?}",
2750 other
2751 )));
2752 }
2753 }
2754 }
2755 _ => {
2756 return Err(Error::InvalidArgumentError(
2757 "unsupported range argument".into(),
2758 ));
2759 }
2760 };
2761 (range_size, alias.as_ref().map(|a| a.name.value.clone()))
2762 }
2763 _ => return Ok(None),
2764 };
2765
2766 if range_size < 0 {
2767 return Err(Error::InvalidArgumentError(
2768 "range size must be non-negative".into(),
2769 ));
2770 }
2771
2772 if select.projection.is_empty() {
2773 return Err(Error::InvalidArgumentError(
2774 "CREATE TABLE AS SELECT requires at least one projected column".into(),
2775 ));
2776 }
2777
2778 let mut column_specs = Vec::with_capacity(select.projection.len());
2779 let mut column_names = Vec::with_capacity(select.projection.len());
2780 let mut row_template = Vec::with_capacity(select.projection.len());
2781 for item in &select.projection {
2782 match item {
2783 SelectItem::ExprWithAlias { expr, alias } => {
2784 let (value, data_type) = match expr {
2785 SqlExpr::Value(value_with_span) => match &value_with_span.value {
2786 Value::Number(raw, _) => {
2787 let parsed = raw.parse::<i64>().map_err(|e| {
2788 Error::InvalidArgumentError(format!(
2789 "invalid numeric literal {}: {}",
2790 raw, e
2791 ))
2792 })?;
2793 (
2794 PlanValue::Integer(parsed),
2795 arrow::datatypes::DataType::Int64,
2796 )
2797 }
2798 Value::SingleQuotedString(s) => (
2799 PlanValue::String(s.clone()),
2800 arrow::datatypes::DataType::Utf8,
2801 ),
2802 other => {
2803 return Err(Error::InvalidArgumentError(format!(
2804 "unsupported SELECT expression in range CTAS: {:?}",
2805 other
2806 )));
2807 }
2808 },
2809 SqlExpr::Identifier(ident) => {
2810 let ident_lower = ident.value.to_ascii_lowercase();
2811 if range_alias
2812 .as_ref()
2813 .map(|a| a.eq_ignore_ascii_case(&ident_lower))
2814 .unwrap_or(false)
2815 || ident_lower == "range"
2816 {
2817 return Err(Error::InvalidArgumentError(
2818 "range() table function columns are not supported yet".into(),
2819 ));
2820 }
2821 return Err(Error::InvalidArgumentError(format!(
2822 "unsupported identifier '{}' in range CTAS projection",
2823 ident.value
2824 )));
2825 }
2826 other => {
2827 return Err(Error::InvalidArgumentError(format!(
2828 "unsupported SELECT expression in range CTAS: {:?}",
2829 other
2830 )));
2831 }
2832 };
2833 let column_name = alias.value.clone();
2834 column_specs.push(PlanColumnSpec::new(column_name.clone(), data_type, true));
2835 column_names.push(column_name);
2836 row_template.push(value);
2837 }
2838 other => {
2839 return Err(Error::InvalidArgumentError(format!(
2840 "unsupported projection {:?} in range CTAS",
2841 other
2842 )));
2843 }
2844 }
2845 }
2846
2847 let plan = CreateTablePlan {
2848 name: display_name.to_string(),
2849 if_not_exists,
2850 or_replace,
2851 columns: column_specs,
2852 source: None,
2853 namespace,
2854 foreign_keys: Vec::new(),
2855 multi_column_uniques: Vec::new(),
2856 };
2857 let create_result = self.execute_plan_statement(PlanStatement::CreateTable(plan))?;
2858
2859 let row_count = range_size
2860 .try_into()
2861 .map_err(|_| Error::InvalidArgumentError("range size exceeds usize".into()))?;
2862 if row_count > 0 {
2863 let rows = vec![row_template; row_count];
2864 let insert_plan = InsertPlan {
2865 table: display_name.to_string(),
2866 columns: column_names,
2867 source: InsertSource::Rows(rows),
2868 on_conflict: InsertConflictAction::None,
2869 };
2870 self.execute_plan_statement(PlanStatement::Insert(insert_plan))?;
2871 }
2872
2873 Ok(Some(create_result))
2874 }
2875
2876 fn try_handle_pragma_table_info(
2880 &self,
2881 query: &Query,
2882 ) -> SqlResult<Option<RuntimeStatementResult<P>>> {
2883 let select = match query.body.as_ref() {
2884 SetExpr::Select(select) => select,
2885 _ => return Ok(None),
2886 };
2887
2888 if select.from.len() != 1 {
2889 return Ok(None);
2890 }
2891
2892 let table_with_joins = &select.from[0];
2893 if !table_with_joins.joins.is_empty() {
2894 return Ok(None);
2895 }
2896
2897 let table_name = match &table_with_joins.relation {
2899 TableFactor::Table {
2900 name,
2901 args: Some(args),
2902 ..
2903 } => {
2904 let func_name = name.to_string().to_ascii_lowercase();
2905 if func_name != "pragma_table_info" {
2906 return Ok(None);
2907 }
2908
2909 if args.args.len() != 1 {
2911 return Err(Error::InvalidArgumentError(
2912 "pragma_table_info expects exactly one argument".into(),
2913 ));
2914 }
2915
2916 match &args.args[0] {
2917 FunctionArg::Unnamed(FunctionArgExpr::Expr(SqlExpr::Value(value))) => {
2918 match &value.value {
2919 Value::SingleQuotedString(s) => s.clone(),
2920 Value::DoubleQuotedString(s) => s.clone(),
2921 _ => {
2922 return Err(Error::InvalidArgumentError(
2923 "pragma_table_info argument must be a string".into(),
2924 ));
2925 }
2926 }
2927 }
2928 _ => {
2929 return Err(Error::InvalidArgumentError(
2930 "pragma_table_info argument must be a string literal".into(),
2931 ));
2932 }
2933 }
2934 }
2935 _ => return Ok(None),
2936 };
2937
2938 let context = self.engine.context();
2940 let (_, canonical_name) = llkv_table::canonical_table_name(&table_name)?;
2941 let columns = context.catalog().table_column_specs(&canonical_name)?;
2942
2943 use arrow::array::{BooleanArray, Int32Array, StringArray};
2945 use arrow::datatypes::{DataType, Field, Schema};
2946
2947 let mut cid_values = Vec::new();
2948 let mut name_values = Vec::new();
2949 let mut type_values = Vec::new();
2950 let mut notnull_values = Vec::new();
2951 let mut dflt_value_values: Vec<Option<String>> = Vec::new();
2952 let mut pk_values = Vec::new();
2953
2954 for (idx, col) in columns.iter().enumerate() {
2955 cid_values.push(idx as i32);
2956 name_values.push(col.name.clone());
2957 type_values.push(format!("{:?}", col.data_type)); notnull_values.push(!col.nullable);
2959 dflt_value_values.push(None); pk_values.push(col.primary_key);
2961 }
2962
2963 let schema = Arc::new(Schema::new(vec![
2964 Field::new("cid", DataType::Int32, false),
2965 Field::new("name", DataType::Utf8, false),
2966 Field::new("type", DataType::Utf8, false),
2967 Field::new("notnull", DataType::Boolean, false),
2968 Field::new("dflt_value", DataType::Utf8, true),
2969 Field::new("pk", DataType::Boolean, false),
2970 ]));
2971
2972 use arrow::array::ArrayRef;
2973 let mut batch = RecordBatch::try_new(
2974 Arc::clone(&schema),
2975 vec![
2976 Arc::new(Int32Array::from(cid_values)) as ArrayRef,
2977 Arc::new(StringArray::from(name_values)) as ArrayRef,
2978 Arc::new(StringArray::from(type_values)) as ArrayRef,
2979 Arc::new(BooleanArray::from(notnull_values)) as ArrayRef,
2980 Arc::new(StringArray::from(dflt_value_values)) as ArrayRef,
2981 Arc::new(BooleanArray::from(pk_values)) as ArrayRef,
2982 ],
2983 )
2984 .map_err(|e| Error::Internal(format!("failed to create pragma_table_info batch: {}", e)))?;
2985
2986 let projection_indices: Vec<usize> = select
2988 .projection
2989 .iter()
2990 .filter_map(|item| {
2991 match item {
2992 SelectItem::UnnamedExpr(SqlExpr::Identifier(ident)) => {
2993 schema.index_of(&ident.value).ok()
2994 }
2995 SelectItem::ExprWithAlias { expr, .. } => {
2996 if let SqlExpr::Identifier(ident) = expr {
2997 schema.index_of(&ident.value).ok()
2998 } else {
2999 None
3000 }
3001 }
3002 SelectItem::Wildcard(_) => None, _ => None,
3004 }
3005 })
3006 .collect();
3007
3008 let projected_schema;
3010 if !projection_indices.is_empty() {
3011 let projected_fields: Vec<Field> = projection_indices
3012 .iter()
3013 .map(|&idx| schema.field(idx).clone())
3014 .collect();
3015 projected_schema = Arc::new(Schema::new(projected_fields));
3016
3017 let projected_columns: Vec<ArrayRef> = projection_indices
3018 .iter()
3019 .map(|&idx| Arc::clone(batch.column(idx)))
3020 .collect();
3021
3022 batch = RecordBatch::try_new(Arc::clone(&projected_schema), projected_columns)
3023 .map_err(|e| Error::Internal(format!("failed to project columns: {}", e)))?;
3024 } else {
3025 projected_schema = schema;
3027 }
3028
3029 if let Some(order_by) = &query.order_by {
3031 use arrow::compute::SortColumn;
3032 use arrow::compute::lexsort_to_indices;
3033 use sqlparser::ast::OrderByKind;
3034
3035 let exprs = match &order_by.kind {
3036 OrderByKind::Expressions(exprs) => exprs,
3037 _ => {
3038 return Err(Error::InvalidArgumentError(
3039 "unsupported ORDER BY clause".into(),
3040 ));
3041 }
3042 };
3043
3044 let mut sort_columns = Vec::new();
3045 for order_expr in exprs {
3046 if let SqlExpr::Identifier(ident) = &order_expr.expr
3047 && let Ok(col_idx) = projected_schema.index_of(&ident.value)
3048 {
3049 let options = arrow::compute::SortOptions {
3050 descending: !order_expr.options.asc.unwrap_or(true),
3051 nulls_first: order_expr.options.nulls_first.unwrap_or(false),
3052 };
3053 sort_columns.push(SortColumn {
3054 values: Arc::clone(batch.column(col_idx)),
3055 options: Some(options),
3056 });
3057 }
3058 }
3059
3060 if !sort_columns.is_empty() {
3061 let indices = lexsort_to_indices(&sort_columns, None)
3062 .map_err(|e| Error::Internal(format!("failed to sort: {}", e)))?;
3063
3064 use arrow::compute::take;
3065 let sorted_columns: Result<Vec<ArrayRef>, _> = batch
3066 .columns()
3067 .iter()
3068 .map(|col| take(col.as_ref(), &indices, None))
3069 .collect();
3070
3071 batch = RecordBatch::try_new(
3072 Arc::clone(&projected_schema),
3073 sorted_columns
3074 .map_err(|e| Error::Internal(format!("failed to apply sort: {}", e)))?,
3075 )
3076 .map_err(|e| Error::Internal(format!("failed to create sorted batch: {}", e)))?;
3077 }
3078 }
3079
3080 let execution = SelectExecution::new_single_batch(
3081 table_name.clone(),
3082 Arc::clone(&projected_schema),
3083 batch,
3084 );
3085
3086 Ok(Some(RuntimeStatementResult::Select {
3087 table_name,
3088 schema: projected_schema,
3089 execution: Box::new(execution),
3090 }))
3091 }
3092
3093 fn handle_create_table_as(
3094 &self,
3095 display_name: String,
3096 _canonical_name: String,
3097 query: Query,
3098 if_not_exists: bool,
3099 or_replace: bool,
3100 namespace: Option<String>,
3101 ) -> SqlResult<RuntimeStatementResult<P>> {
3102 if let SetExpr::Select(select) = query.body.as_ref()
3105 && let Some((rows, column_names)) = extract_values_from_derived_table(&select.from)?
3106 {
3107 return self.handle_create_table_from_values(
3109 display_name,
3110 rows,
3111 column_names,
3112 if_not_exists,
3113 or_replace,
3114 namespace,
3115 );
3116 }
3117
3118 let select_plan = self.build_select_plan(query)?;
3120
3121 if select_plan.projections.is_empty() && select_plan.aggregates.is_empty() {
3122 return Err(Error::InvalidArgumentError(
3123 "CREATE TABLE AS SELECT requires at least one projected column".into(),
3124 ));
3125 }
3126
3127 let plan = CreateTablePlan {
3128 name: display_name,
3129 if_not_exists,
3130 or_replace,
3131 columns: Vec::new(),
3132 source: Some(CreateTableSource::Select {
3133 plan: Box::new(select_plan),
3134 }),
3135 namespace,
3136 foreign_keys: Vec::new(),
3137 multi_column_uniques: Vec::new(),
3138 };
3139 self.execute_plan_statement(PlanStatement::CreateTable(plan))
3140 }
3141
3142 fn handle_create_table_from_values(
3143 &self,
3144 display_name: String,
3145 rows: Vec<Vec<PlanValue>>,
3146 column_names: Vec<String>,
3147 if_not_exists: bool,
3148 or_replace: bool,
3149 namespace: Option<String>,
3150 ) -> SqlResult<RuntimeStatementResult<P>> {
3151 use arrow::array::{ArrayRef, Float64Builder, Int64Builder, StringBuilder};
3152 use arrow::datatypes::{DataType, Field, Schema};
3153 use arrow::record_batch::RecordBatch;
3154 use std::sync::Arc;
3155
3156 if rows.is_empty() {
3157 return Err(Error::InvalidArgumentError(
3158 "VALUES must have at least one row".into(),
3159 ));
3160 }
3161
3162 let num_cols = column_names.len();
3163
3164 let first_row = &rows[0];
3166 if first_row.len() != num_cols {
3167 return Err(Error::InvalidArgumentError(
3168 "VALUES row column count mismatch".into(),
3169 ));
3170 }
3171
3172 let mut fields = Vec::with_capacity(num_cols);
3173 let mut column_types = Vec::with_capacity(num_cols);
3174
3175 for (idx, value) in first_row.iter().enumerate() {
3176 let (data_type, nullable) = match value {
3177 PlanValue::Integer(_) => (DataType::Int64, false),
3178 PlanValue::Float(_) => (DataType::Float64, false),
3179 PlanValue::String(_) => (DataType::Utf8, false),
3180 PlanValue::Null => (DataType::Utf8, true), _ => {
3182 return Err(Error::InvalidArgumentError(format!(
3183 "unsupported value type in VALUES for column '{}'",
3184 column_names.get(idx).unwrap_or(&format!("column{}", idx))
3185 )));
3186 }
3187 };
3188
3189 column_types.push(data_type.clone());
3190 fields.push(Field::new(&column_names[idx], data_type, nullable));
3191 }
3192
3193 let schema = Arc::new(Schema::new(fields));
3194
3195 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(num_cols);
3197
3198 for col_idx in 0..num_cols {
3199 let col_type = &column_types[col_idx];
3200
3201 match col_type {
3202 DataType::Int64 => {
3203 let mut builder = Int64Builder::with_capacity(rows.len());
3204 for row in &rows {
3205 match &row[col_idx] {
3206 PlanValue::Integer(v) => builder.append_value(*v),
3207 PlanValue::Null => builder.append_null(),
3208 other => {
3209 return Err(Error::InvalidArgumentError(format!(
3210 "type mismatch in VALUES: expected Integer, got {:?}",
3211 other
3212 )));
3213 }
3214 }
3215 }
3216 arrays.push(Arc::new(builder.finish()) as ArrayRef);
3217 }
3218 DataType::Float64 => {
3219 let mut builder = Float64Builder::with_capacity(rows.len());
3220 for row in &rows {
3221 match &row[col_idx] {
3222 PlanValue::Float(v) => builder.append_value(*v),
3223 PlanValue::Null => builder.append_null(),
3224 other => {
3225 return Err(Error::InvalidArgumentError(format!(
3226 "type mismatch in VALUES: expected Float, got {:?}",
3227 other
3228 )));
3229 }
3230 }
3231 }
3232 arrays.push(Arc::new(builder.finish()) as ArrayRef);
3233 }
3234 DataType::Utf8 => {
3235 let mut builder = StringBuilder::with_capacity(rows.len(), 1024);
3236 for row in &rows {
3237 match &row[col_idx] {
3238 PlanValue::String(v) => builder.append_value(v),
3239 PlanValue::Null => builder.append_null(),
3240 other => {
3241 return Err(Error::InvalidArgumentError(format!(
3242 "type mismatch in VALUES: expected String, got {:?}",
3243 other
3244 )));
3245 }
3246 }
3247 }
3248 arrays.push(Arc::new(builder.finish()) as ArrayRef);
3249 }
3250 other => {
3251 return Err(Error::InvalidArgumentError(format!(
3252 "unsupported column type in VALUES: {:?}",
3253 other
3254 )));
3255 }
3256 }
3257 }
3258
3259 let batch = RecordBatch::try_new(Arc::clone(&schema), arrays).map_err(|e| {
3260 Error::Internal(format!("failed to create RecordBatch from VALUES: {}", e))
3261 })?;
3262
3263 let plan = CreateTablePlan {
3264 name: display_name.clone(),
3265 if_not_exists,
3266 or_replace,
3267 columns: Vec::new(),
3268 source: Some(CreateTableSource::Batches {
3269 schema: Arc::clone(&schema),
3270 batches: vec![batch],
3271 }),
3272 namespace,
3273 foreign_keys: Vec::new(),
3274 multi_column_uniques: Vec::new(),
3275 };
3276
3277 self.execute_plan_statement(PlanStatement::CreateTable(plan))
3278 }
3279
3280 fn handle_insert(&self, stmt: sqlparser::ast::Insert) -> SqlResult<RuntimeStatementResult<P>> {
3281 match self.prepare_insert(stmt)? {
3282 PreparedInsert::Values {
3283 table_name,
3284 columns,
3285 rows,
3286 on_conflict,
3287 } => {
3288 tracing::trace!(
3289 "DEBUG SQL handle_insert executing buffered-values insert for table={}",
3290 table_name
3291 );
3292 let plan = InsertPlan {
3293 table: table_name,
3294 columns,
3295 source: InsertSource::Rows(rows),
3296 on_conflict,
3297 };
3298 self.execute_plan_statement(PlanStatement::Insert(plan))
3299 }
3300 PreparedInsert::Immediate(plan) => {
3301 let table_name = plan.table.clone();
3302 tracing::trace!(
3303 "DEBUG SQL handle_insert executing immediate insert for table={}",
3304 table_name
3305 );
3306 self.execute_plan_statement(PlanStatement::Insert(plan))
3307 }
3308 }
3309 }
3310
3311 fn handle_update(
3312 &self,
3313 table: TableWithJoins,
3314 assignments: Vec<Assignment>,
3315 from: Option<UpdateTableFromKind>,
3316 selection: Option<SqlExpr>,
3317 returning: Option<Vec<SelectItem>>,
3318 ) -> SqlResult<RuntimeStatementResult<P>> {
3319 if from.is_some() {
3320 return Err(Error::InvalidArgumentError(
3321 "UPDATE ... FROM is not supported yet".into(),
3322 ));
3323 }
3324 if returning.is_some() {
3325 return Err(Error::InvalidArgumentError(
3326 "UPDATE ... RETURNING is not supported".into(),
3327 ));
3328 }
3329 if assignments.is_empty() {
3330 return Err(Error::InvalidArgumentError(
3331 "UPDATE requires at least one assignment".into(),
3332 ));
3333 }
3334
3335 let (display_name, canonical_name) = extract_single_table(std::slice::from_ref(&table))?;
3336
3337 if !self.engine.session().has_active_transaction()
3338 && self
3339 .engine
3340 .context()
3341 .is_table_marked_dropped(&canonical_name)
3342 {
3343 return Err(Error::TransactionContextError(
3344 DROPPED_TABLE_TRANSACTION_ERR.into(),
3345 ));
3346 }
3347
3348 let catalog = self.engine.context().table_catalog();
3349 let resolver = catalog.identifier_resolver();
3350 let table_id = catalog.table_id(&canonical_name);
3351
3352 let mut assignments_map: FxHashMap<String, (String, sqlparser::ast::Expr)> =
3355 FxHashMap::with_capacity_and_hasher(assignments.len(), FxBuildHasher);
3356 for assignment in assignments {
3357 let column_name = resolve_assignment_column_name(&assignment.target)?;
3358 let normalized = column_name.to_ascii_lowercase();
3359 assignments_map.insert(normalized, (column_name, assignment.value.clone()));
3361 }
3362
3363 let mut column_assignments = Vec::with_capacity(assignments_map.len());
3364 for (_normalized, (column_name, expr)) in assignments_map {
3365 let value = match SqlValue::try_from_expr(&expr) {
3366 Ok(literal) => AssignmentValue::Literal(PlanValue::from(literal)),
3367 Err(Error::InvalidArgumentError(msg))
3368 if msg.contains("unsupported literal expression") =>
3369 {
3370 let normalized_expr = self.materialize_in_subquery(expr.clone())?;
3371 let translated = translate_scalar_with_context(
3372 &resolver,
3373 IdentifierContext::new(table_id),
3374 &normalized_expr,
3375 )?;
3376 AssignmentValue::Expression(translated)
3377 }
3378 Err(err) => return Err(err),
3379 };
3380 column_assignments.push(ColumnAssignment {
3381 column: column_name,
3382 value,
3383 });
3384 }
3385
3386 let filter = match selection {
3387 Some(expr) => {
3388 let materialized_expr = self.materialize_in_subquery(expr)?;
3389 let mut subqueries = Vec::new();
3390 let predicate = translate_condition_with_context(
3391 self,
3392 &resolver,
3393 IdentifierContext::new(table_id),
3394 &materialized_expr,
3395 &[],
3396 &mut subqueries,
3397 None,
3398 )?;
3399 if subqueries.is_empty() {
3400 Some(predicate)
3401 } else {
3402 return Err(Error::InvalidArgumentError(
3403 "EXISTS subqueries are not supported in UPDATE WHERE clauses".into(),
3404 ));
3405 }
3406 }
3407 None => None,
3408 };
3409
3410 let plan = UpdatePlan {
3411 table: display_name.clone(),
3412 assignments: column_assignments,
3413 filter,
3414 };
3415 self.execute_plan_statement(PlanStatement::Update(plan))
3416 }
3417
3418 #[allow(clippy::collapsible_if)]
3419 fn handle_delete(&self, delete: Delete) -> SqlResult<RuntimeStatementResult<P>> {
3420 let Delete {
3421 tables,
3422 from,
3423 using,
3424 selection,
3425 returning,
3426 order_by,
3427 limit,
3428 } = delete;
3429
3430 if !tables.is_empty() {
3431 return Err(Error::InvalidArgumentError(
3432 "multi-table DELETE is not supported yet".into(),
3433 ));
3434 }
3435 if let Some(using_tables) = using {
3436 if !using_tables.is_empty() {
3437 return Err(Error::InvalidArgumentError(
3438 "DELETE ... USING is not supported yet".into(),
3439 ));
3440 }
3441 }
3442 if returning.is_some() {
3443 return Err(Error::InvalidArgumentError(
3444 "DELETE ... RETURNING is not supported".into(),
3445 ));
3446 }
3447 if !order_by.is_empty() {
3448 return Err(Error::InvalidArgumentError(
3449 "DELETE ... ORDER BY is not supported yet".into(),
3450 ));
3451 }
3452 if limit.is_some() {
3453 return Err(Error::InvalidArgumentError(
3454 "DELETE ... LIMIT is not supported yet".into(),
3455 ));
3456 }
3457
3458 let from_tables = match from {
3459 FromTable::WithFromKeyword(tables) | FromTable::WithoutKeyword(tables) => tables,
3460 };
3461 let (display_name, canonical_name) = extract_single_table(&from_tables)?;
3462
3463 if !self.engine.session().has_active_transaction()
3464 && self
3465 .engine
3466 .context()
3467 .is_table_marked_dropped(&canonical_name)
3468 {
3469 return Err(Error::TransactionContextError(
3470 DROPPED_TABLE_TRANSACTION_ERR.into(),
3471 ));
3472 }
3473
3474 let catalog = self.engine.context().table_catalog();
3475 let resolver = catalog.identifier_resolver();
3476 let table_id = catalog.table_id(&canonical_name);
3477
3478 let filter = if let Some(expr) = selection {
3479 let materialized_expr = self.materialize_in_subquery(expr)?;
3480 let mut subqueries = Vec::new();
3481 let predicate = translate_condition_with_context(
3482 self,
3483 &resolver,
3484 IdentifierContext::new(table_id),
3485 &materialized_expr,
3486 &[],
3487 &mut subqueries,
3488 None,
3489 )?;
3490 if !subqueries.is_empty() {
3491 return Err(Error::InvalidArgumentError(
3492 "EXISTS subqueries are not supported in DELETE WHERE clauses".into(),
3493 ));
3494 }
3495 Some(predicate)
3496 } else {
3497 None
3498 };
3499
3500 let plan = DeletePlan {
3501 table: display_name.clone(),
3502 filter,
3503 };
3504 self.execute_plan_statement(PlanStatement::Delete(plan))
3505 }
3506
3507 fn handle_truncate(
3508 &self,
3509 table_names: &[sqlparser::ast::TruncateTableTarget],
3510 partitions: &Option<Vec<SqlExpr>>,
3511 _table: bool, identity: &Option<sqlparser::ast::TruncateIdentityOption>,
3513 cascade: Option<sqlparser::ast::CascadeOption>,
3514 on_cluster: &Option<Ident>,
3515 ) -> SqlResult<RuntimeStatementResult<P>> {
3516 if table_names.len() > 1 {
3518 return Err(Error::InvalidArgumentError(
3519 "TRUNCATE with multiple tables is not supported yet".into(),
3520 ));
3521 }
3522 if partitions.is_some() {
3523 return Err(Error::InvalidArgumentError(
3524 "TRUNCATE ... PARTITION is not supported".into(),
3525 ));
3526 }
3527 if identity.is_some() {
3528 return Err(Error::InvalidArgumentError(
3529 "TRUNCATE ... RESTART/CONTINUE IDENTITY is not supported".into(),
3530 ));
3531 }
3532 use sqlparser::ast::CascadeOption;
3533 if matches!(cascade, Some(CascadeOption::Cascade)) {
3534 return Err(Error::InvalidArgumentError(
3535 "TRUNCATE ... CASCADE is not supported".into(),
3536 ));
3537 }
3538 if on_cluster.is_some() {
3539 return Err(Error::InvalidArgumentError(
3540 "TRUNCATE ... ON CLUSTER is not supported".into(),
3541 ));
3542 }
3543
3544 let table_name = if let Some(target) = table_names.first() {
3546 let table_obj = &target.name;
3548 let display_name = table_obj.to_string();
3549 let canonical_name = display_name.to_ascii_lowercase();
3550
3551 if !self.engine.session().has_active_transaction()
3553 && self
3554 .engine
3555 .context()
3556 .is_table_marked_dropped(&canonical_name)
3557 {
3558 return Err(Error::TransactionContextError(
3559 DROPPED_TABLE_TRANSACTION_ERR.into(),
3560 ));
3561 }
3562
3563 display_name
3564 } else {
3565 return Err(Error::InvalidArgumentError(
3566 "TRUNCATE requires a table name".into(),
3567 ));
3568 };
3569
3570 let plan = TruncatePlan {
3571 table: table_name.clone(),
3572 };
3573 self.execute_plan_statement(PlanStatement::Truncate(plan))
3574 }
3575
3576 #[allow(clippy::too_many_arguments)] fn handle_drop(
3578 &self,
3579 object_type: ObjectType,
3580 if_exists: bool,
3581 names: Vec<ObjectName>,
3582 cascade: bool,
3583 restrict: bool,
3584 purge: bool,
3585 temporary: bool,
3586 ) -> SqlResult<RuntimeStatementResult<P>> {
3587 if purge || temporary {
3588 return Err(Error::InvalidArgumentError(
3589 "DROP purge/temporary options are not supported".into(),
3590 ));
3591 }
3592
3593 match object_type {
3594 ObjectType::Table => {
3595 if cascade || restrict {
3596 return Err(Error::InvalidArgumentError(
3597 "DROP TABLE CASCADE/RESTRICT is not supported".into(),
3598 ));
3599 }
3600
3601 for name in names {
3602 let table_name = Self::object_name_to_string(&name)?;
3603 let mut plan = llkv_plan::DropTablePlan::new(table_name.clone());
3604 plan.if_exists = if_exists;
3605
3606 self.execute_plan_statement(llkv_plan::PlanStatement::DropTable(plan))
3607 .map_err(|err| Self::map_table_error(&table_name, err))?;
3608 }
3609
3610 Ok(RuntimeStatementResult::NoOp)
3611 }
3612 ObjectType::View => {
3613 if cascade || restrict {
3614 return Err(Error::InvalidArgumentError(
3615 "DROP VIEW CASCADE/RESTRICT is not supported".into(),
3616 ));
3617 }
3618
3619 for name in names {
3620 let view_name = Self::object_name_to_string(&name)?;
3621 let plan = llkv_plan::DropViewPlan::new(view_name).if_exists(if_exists);
3622 self.execute_plan_statement(llkv_plan::PlanStatement::DropView(plan))?;
3623 }
3624
3625 Ok(RuntimeStatementResult::NoOp)
3626 }
3627 ObjectType::Index => {
3628 if cascade || restrict {
3629 return Err(Error::InvalidArgumentError(
3630 "DROP INDEX CASCADE/RESTRICT is not supported".into(),
3631 ));
3632 }
3633
3634 for name in names {
3635 let index_name = Self::object_name_to_string(&name)?;
3636 let plan = llkv_plan::DropIndexPlan::new(index_name).if_exists(if_exists);
3637 self.execute_plan_statement(llkv_plan::PlanStatement::DropIndex(plan))?;
3638 }
3639
3640 Ok(RuntimeStatementResult::NoOp)
3641 }
3642 ObjectType::Schema => {
3643 if restrict {
3644 return Err(Error::InvalidArgumentError(
3645 "DROP SCHEMA RESTRICT is not supported".into(),
3646 ));
3647 }
3648
3649 let catalog = self.engine.context().table_catalog();
3650
3651 for name in names {
3652 let (display_name, canonical_name) = canonical_object_name(&name)?;
3653
3654 if !catalog.schema_exists(&canonical_name) {
3655 if if_exists {
3656 continue;
3657 }
3658 return Err(Error::CatalogError(format!(
3659 "Schema '{}' does not exist",
3660 display_name
3661 )));
3662 }
3663
3664 if cascade {
3665 let all_tables = catalog.table_names();
3667 let schema_prefix = format!("{}.", canonical_name);
3668
3669 for table in all_tables {
3670 if table.to_ascii_lowercase().starts_with(&schema_prefix) {
3671 let mut plan = llkv_plan::DropTablePlan::new(table.clone());
3672 plan.if_exists = false;
3673 self.execute_plan_statement(llkv_plan::PlanStatement::DropTable(
3674 plan,
3675 ))?;
3676 }
3677 }
3678 } else {
3679 let all_tables = catalog.table_names();
3681 let schema_prefix = format!("{}.", canonical_name);
3682 let has_tables = all_tables
3683 .iter()
3684 .any(|t| t.to_ascii_lowercase().starts_with(&schema_prefix));
3685
3686 if has_tables {
3687 return Err(Error::CatalogError(format!(
3688 "Schema '{}' is not empty. Use CASCADE to drop schema and all its tables",
3689 display_name
3690 )));
3691 }
3692 }
3693
3694 if !catalog.unregister_schema(&canonical_name) && !if_exists {
3696 return Err(Error::CatalogError(format!(
3697 "Schema '{}' does not exist",
3698 display_name
3699 )));
3700 }
3701 }
3702
3703 Ok(RuntimeStatementResult::NoOp)
3704 }
3705 _ => Err(Error::InvalidArgumentError(format!(
3706 "DROP {} is not supported",
3707 object_type
3708 ))),
3709 }
3710 }
3711
3712 fn handle_alter_table(
3713 &self,
3714 name: ObjectName,
3715 if_exists: bool,
3716 only: bool,
3717 operations: Vec<AlterTableOperation>,
3718 ) -> SqlResult<RuntimeStatementResult<P>> {
3719 if only {
3720 return Err(Error::InvalidArgumentError(
3721 "ALTER TABLE ONLY is not supported yet".into(),
3722 ));
3723 }
3724
3725 if operations.is_empty() {
3726 return Ok(RuntimeStatementResult::NoOp);
3727 }
3728
3729 if operations.len() != 1 {
3730 return Err(Error::InvalidArgumentError(
3731 "ALTER TABLE currently supports exactly one operation".into(),
3732 ));
3733 }
3734
3735 let operation = operations.into_iter().next().expect("checked length");
3736 match operation {
3737 AlterTableOperation::RenameTable { table_name } => {
3738 let new_name = table_name.to_string();
3739 self.handle_alter_table_rename(name, new_name, if_exists)
3740 }
3741 AlterTableOperation::RenameColumn {
3742 old_column_name,
3743 new_column_name,
3744 } => {
3745 let plan = llkv_plan::AlterTablePlan {
3746 table_name: name.to_string(),
3747 if_exists,
3748 operation: llkv_plan::AlterTableOperation::RenameColumn {
3749 old_column_name: old_column_name.to_string(),
3750 new_column_name: new_column_name.to_string(),
3751 },
3752 };
3753 self.execute_plan_statement(PlanStatement::AlterTable(plan))
3754 }
3755 AlterTableOperation::AlterColumn { column_name, op } => {
3756 if let AlterColumnOperation::SetDataType {
3758 data_type,
3759 using,
3760 had_set: _,
3761 } = op
3762 {
3763 if using.is_some() {
3764 return Err(Error::InvalidArgumentError(
3765 "ALTER COLUMN SET DATA TYPE USING clause is not yet supported".into(),
3766 ));
3767 }
3768
3769 let plan = llkv_plan::AlterTablePlan {
3770 table_name: name.to_string(),
3771 if_exists,
3772 operation: llkv_plan::AlterTableOperation::SetColumnDataType {
3773 column_name: column_name.to_string(),
3774 new_data_type: data_type.to_string(),
3775 },
3776 };
3777 self.execute_plan_statement(PlanStatement::AlterTable(plan))
3778 } else {
3779 Err(Error::InvalidArgumentError(format!(
3780 "unsupported ALTER COLUMN operation: {:?}",
3781 op
3782 )))
3783 }
3784 }
3785 AlterTableOperation::DropColumn {
3786 has_column_keyword: _,
3787 column_names,
3788 if_exists: column_if_exists,
3789 drop_behavior,
3790 } => {
3791 if column_names.len() != 1 {
3792 return Err(Error::InvalidArgumentError(
3793 "DROP COLUMN currently supports dropping one column at a time".into(),
3794 ));
3795 }
3796
3797 let column_name = column_names.into_iter().next().unwrap().to_string();
3798 let cascade = matches!(drop_behavior, Some(sqlparser::ast::DropBehavior::Cascade));
3799
3800 let plan = llkv_plan::AlterTablePlan {
3801 table_name: name.to_string(),
3802 if_exists,
3803 operation: llkv_plan::AlterTableOperation::DropColumn {
3804 column_name,
3805 if_exists: column_if_exists,
3806 cascade,
3807 },
3808 };
3809 self.execute_plan_statement(PlanStatement::AlterTable(plan))
3810 }
3811 other => Err(Error::InvalidArgumentError(format!(
3812 "unsupported ALTER TABLE operation: {:?}",
3813 other
3814 ))),
3815 }
3816 }
3817
3818 fn handle_alter_table_rename(
3819 &self,
3820 original_name: ObjectName,
3821 new_table_name: String,
3822 if_exists: bool,
3823 ) -> SqlResult<RuntimeStatementResult<P>> {
3824 let (schema_opt, table_name) = parse_schema_qualified_name(&original_name)?;
3825
3826 let new_table_name_clean = new_table_name.trim();
3827
3828 if new_table_name_clean.is_empty() {
3829 return Err(Error::InvalidArgumentError(
3830 "ALTER TABLE RENAME requires a non-empty table name".into(),
3831 ));
3832 }
3833
3834 let (raw_new_schema_opt, raw_new_table) =
3835 if let Some((schema_part, table_part)) = new_table_name_clean.split_once('.') {
3836 (
3837 Some(schema_part.trim().to_string()),
3838 table_part.trim().to_string(),
3839 )
3840 } else {
3841 (None, new_table_name_clean.to_string())
3842 };
3843
3844 if schema_opt.is_none() && raw_new_schema_opt.is_some() {
3845 return Err(Error::InvalidArgumentError(
3846 "ALTER TABLE RENAME cannot add a schema qualifier".into(),
3847 ));
3848 }
3849
3850 let new_table_trimmed = raw_new_table.trim_matches('"');
3851 if new_table_trimmed.is_empty() {
3852 return Err(Error::InvalidArgumentError(
3853 "ALTER TABLE RENAME requires a non-empty table name".into(),
3854 ));
3855 }
3856
3857 if let (Some(existing_schema), Some(new_schema_raw)) =
3858 (schema_opt.as_ref(), raw_new_schema_opt.as_ref())
3859 {
3860 let new_schema_trimmed = new_schema_raw.trim_matches('"');
3861 if !existing_schema.eq_ignore_ascii_case(new_schema_trimmed) {
3862 return Err(Error::InvalidArgumentError(
3863 "ALTER TABLE RENAME cannot change table schema".into(),
3864 ));
3865 }
3866 }
3867
3868 let new_table_display = raw_new_table;
3869 let new_schema_opt = raw_new_schema_opt;
3870
3871 fn join_schema_table(schema: &str, table: &str) -> String {
3872 let mut qualified = String::with_capacity(schema.len() + table.len() + 1);
3873 qualified.push_str(schema);
3874 qualified.push('.');
3875 qualified.push_str(table);
3876 qualified
3877 }
3878
3879 let current_display = schema_opt
3880 .as_ref()
3881 .map(|schema| join_schema_table(schema, &table_name))
3882 .unwrap_or_else(|| table_name.clone());
3883
3884 let new_display = if let Some(new_schema_raw) = new_schema_opt.clone() {
3885 join_schema_table(&new_schema_raw, &new_table_display)
3886 } else if let Some(schema) = schema_opt.as_ref() {
3887 join_schema_table(schema, &new_table_display)
3888 } else {
3889 new_table_display.clone()
3890 };
3891
3892 let plan = RenameTablePlan::new(¤t_display, &new_display).if_exists(if_exists);
3893
3894 match CatalogDdl::rename_table(self.engine.session(), plan) {
3895 Ok(()) => Ok(RuntimeStatementResult::NoOp),
3896 Err(err) => Err(Self::map_table_error(¤t_display, err)),
3897 }
3898 }
3899
3900 fn try_materialize_avg_subquery(&self, query: &Query) -> SqlResult<Option<SqlExpr>> {
3908 use sqlparser::ast::{
3909 DuplicateTreatment, FunctionArg, FunctionArgExpr, FunctionArguments, ObjectName,
3910 ObjectNamePart, SelectItem, SetExpr,
3911 };
3912
3913 let select = match query.body.as_ref() {
3914 SetExpr::Select(select) => select.as_ref(),
3915 _ => return Ok(None),
3916 };
3917
3918 if select.projection.len() != 1
3919 || select.distinct.is_some()
3920 || select.top.is_some()
3921 || select.value_table_mode.is_some()
3922 || select.having.is_some()
3923 || !group_by_is_empty(&select.group_by)
3924 || select.into.is_some()
3925 || !select.lateral_views.is_empty()
3926 {
3927 return Ok(None);
3928 }
3929
3930 let func = match &select.projection[0] {
3931 SelectItem::UnnamedExpr(SqlExpr::Function(func)) => func,
3932 _ => return Ok(None),
3933 };
3934
3935 if func.uses_odbc_syntax
3936 || func.filter.is_some()
3937 || func.null_treatment.is_some()
3938 || func.over.is_some()
3939 || !func.within_group.is_empty()
3940 {
3941 return Ok(None);
3942 }
3943
3944 let func_name = func.name.to_string().to_ascii_lowercase();
3945 if func_name != "avg" {
3946 return Ok(None);
3947 }
3948
3949 let args = match &func.args {
3950 FunctionArguments::List(list) => {
3951 if matches!(list.duplicate_treatment, Some(DuplicateTreatment::Distinct))
3952 || !list.clauses.is_empty()
3953 {
3954 return Ok(None);
3955 }
3956 &list.args
3957 }
3958 _ => return Ok(None),
3959 };
3960
3961 if args.len() != 1 {
3962 return Ok(None);
3963 }
3964
3965 match &args[0] {
3966 FunctionArg::Unnamed(FunctionArgExpr::Expr(_)) => {}
3967 _ => return Ok(None),
3968 };
3969
3970 let mut sum_query = query.clone();
3971 let mut count_query = query.clone();
3972
3973 let build_replacement = |target_query: &mut Query, name: &str| -> SqlResult<()> {
3974 let select = match target_query.body.as_mut() {
3975 SetExpr::Select(select) => select,
3976 _ => {
3977 return Err(Error::Internal(
3978 "expected SELECT query in AVG materialization".into(),
3979 ));
3980 }
3981 };
3982
3983 let mut replacement_func = func.clone();
3984 replacement_func.name = ObjectName(vec![ObjectNamePart::Identifier(Ident {
3985 value: name.to_string(),
3986 quote_style: None,
3987 span: Span::empty(),
3988 })]);
3989 select.projection = vec![SelectItem::UnnamedExpr(SqlExpr::Function(replacement_func))];
3990 Ok(())
3991 };
3992
3993 build_replacement(&mut sum_query, "sum")?;
3994 build_replacement(&mut count_query, "count")?;
3995
3996 let sum_value = self.execute_scalar_int64(sum_query)?;
3997 let count_value = self.execute_scalar_int64(count_query)?;
3998
3999 let Some(count_value) = count_value else {
4000 return Ok(Some(SqlExpr::Value(ValueWithSpan {
4001 value: Value::Null,
4002 span: Span::empty(),
4003 })));
4004 };
4005
4006 if count_value == 0 {
4007 return Ok(Some(SqlExpr::Value(ValueWithSpan {
4008 value: Value::Null,
4009 span: Span::empty(),
4010 })));
4011 }
4012
4013 let sum_value = match sum_value {
4014 Some(value) => value,
4015 None => {
4016 return Ok(Some(SqlExpr::Value(ValueWithSpan {
4017 value: Value::Null,
4018 span: Span::empty(),
4019 })));
4020 }
4021 };
4022
4023 let avg = (sum_value as f64) / (count_value as f64);
4024 let value = ValueWithSpan {
4025 value: Value::Number(avg.to_string(), false),
4026 span: Span::empty(),
4027 };
4028 Ok(Some(SqlExpr::Value(value)))
4029 }
4030
4031 fn execute_scalar_int64(&self, query: Query) -> SqlResult<Option<i64>> {
4032 let result = self.handle_query(query)?;
4033 let execution = match result {
4034 RuntimeStatementResult::Select { execution, .. } => execution,
4035 _ => {
4036 return Err(Error::InvalidArgumentError(
4037 "scalar aggregate must be a SELECT statement".into(),
4038 ));
4039 }
4040 };
4041
4042 let batches = execution.collect()?;
4043 let mut captured: Option<Option<i64>> = None;
4044
4045 for batch in batches {
4046 if batch.num_columns() == 0 {
4047 continue;
4048 }
4049 if batch.num_columns() != 1 {
4050 return Err(Error::InvalidArgumentError(
4051 "scalar aggregate must return exactly one column".into(),
4052 ));
4053 }
4054
4055 let array = batch.column(0);
4056 let values = array
4057 .as_any()
4058 .downcast_ref::<arrow::array::Int64Array>()
4059 .ok_or_else(|| {
4060 Error::InvalidArgumentError(
4061 "scalar aggregate result must be an INT64 value".into(),
4062 )
4063 })?;
4064
4065 for idx in 0..values.len() {
4066 if captured.is_some() {
4067 return Err(Error::InvalidArgumentError(
4068 "scalar aggregate returned more than one row".into(),
4069 ));
4070 }
4071 if values.is_null(idx) {
4072 captured = Some(None);
4073 } else {
4074 captured = Some(Some(values.value(idx)));
4075 }
4076 }
4077 }
4078
4079 Ok(captured.unwrap_or(None))
4080 }
4081
4082 fn materialize_scalar_subquery(&self, subquery: Query) -> SqlResult<SqlExpr> {
4083 if let Some(avg_literal) = self.try_materialize_avg_subquery(&subquery)? {
4084 return Ok(avg_literal);
4085 }
4086
4087 let result = self.handle_query(subquery)?;
4088 let execution = match result {
4089 RuntimeStatementResult::Select { execution, .. } => execution,
4090 _ => {
4091 return Err(Error::InvalidArgumentError(
4092 "scalar subquery must be a SELECT statement".into(),
4093 ));
4094 }
4095 };
4096
4097 let batches = execution.collect()?;
4098 let mut captured_value: Option<ValueWithSpan> = None;
4099
4100 for batch in batches {
4101 if batch.num_columns() == 0 {
4102 continue;
4103 }
4104 if batch.num_columns() != 1 {
4105 return Err(Error::InvalidArgumentError(
4106 "scalar subquery must return exactly one column".into(),
4107 ));
4108 }
4109
4110 let column = batch.column(0);
4111 for row_idx in 0..batch.num_rows() {
4112 if captured_value.is_some() {
4113 return Err(Error::InvalidArgumentError(
4114 "scalar subquery returned more than one row".into(),
4115 ));
4116 }
4117
4118 let value = if column.is_null(row_idx) {
4119 Value::Null
4120 } else {
4121 use arrow::array::{BooleanArray, Float64Array, Int64Array, StringArray};
4122 match column.data_type() {
4123 arrow::datatypes::DataType::Int64 => {
4124 let array =
4125 column
4126 .as_any()
4127 .downcast_ref::<Int64Array>()
4128 .ok_or_else(|| {
4129 Error::Internal(
4130 "expected Int64 array for scalar subquery".into(),
4131 )
4132 })?;
4133 Value::Number(array.value(row_idx).to_string(), false)
4134 }
4135 arrow::datatypes::DataType::Float64 => {
4136 let array = column.as_any().downcast_ref::<Float64Array>().ok_or_else(
4137 || {
4138 Error::Internal(
4139 "expected Float64 array for scalar subquery".into(),
4140 )
4141 },
4142 )?;
4143 Value::Number(array.value(row_idx).to_string(), false)
4144 }
4145 arrow::datatypes::DataType::Utf8 => {
4146 let array =
4147 column
4148 .as_any()
4149 .downcast_ref::<StringArray>()
4150 .ok_or_else(|| {
4151 Error::Internal(
4152 "expected String array for scalar subquery".into(),
4153 )
4154 })?;
4155 Value::SingleQuotedString(array.value(row_idx).to_string())
4156 }
4157 arrow::datatypes::DataType::Boolean => {
4158 let array = column.as_any().downcast_ref::<BooleanArray>().ok_or_else(
4159 || {
4160 Error::Internal(
4161 "expected Boolean array for scalar subquery".into(),
4162 )
4163 },
4164 )?;
4165 Value::Boolean(array.value(row_idx))
4166 }
4167 other => {
4168 return Err(Error::InvalidArgumentError(format!(
4169 "unsupported data type in scalar subquery result: {other:?}"
4170 )));
4171 }
4172 }
4173 };
4174
4175 captured_value = Some(ValueWithSpan {
4176 value,
4177 span: Span::empty(),
4178 });
4179 }
4180 }
4181
4182 let final_value = captured_value.unwrap_or(ValueWithSpan {
4183 value: Value::Null,
4184 span: Span::empty(),
4185 });
4186 Ok(SqlExpr::Value(final_value))
4187 }
4188
4189 fn materialize_in_subquery(&self, root_expr: SqlExpr) -> SqlResult<SqlExpr> {
4190 enum WorkItem {
4192 Process(Box<SqlExpr>),
4193 BuildBinaryOp {
4194 op: BinaryOperator,
4195 left: Box<SqlExpr>,
4196 right_done: bool,
4197 },
4198 BuildUnaryOp {
4199 op: UnaryOperator,
4200 },
4201 BuildNested,
4202 BuildIsNull,
4203 BuildIsNotNull,
4204 FinishBetween {
4205 negated: bool,
4206 },
4207 }
4208
4209 let mut work_stack: Vec<WorkItem> = vec![WorkItem::Process(Box::new(root_expr))];
4210 let mut result_stack: Vec<SqlExpr> = Vec::new();
4211
4212 while let Some(item) = work_stack.pop() {
4213 match item {
4214 WorkItem::Process(expr) => {
4215 match *expr {
4216 SqlExpr::InSubquery {
4217 expr: left_expr,
4218 subquery,
4219 negated,
4220 } => {
4221 let result = self.handle_query(*subquery)?;
4223
4224 let values = match result {
4226 RuntimeStatementResult::Select { execution, .. } => {
4227 let batches = execution.collect()?;
4228 let mut collected_values = Vec::new();
4229
4230 for batch in batches {
4231 if batch.num_columns() == 0 {
4232 continue;
4233 }
4234 if batch.num_columns() > 1 {
4235 return Err(Error::InvalidArgumentError(format!(
4236 "IN subquery must return exactly one column, got {}",
4237 batch.num_columns()
4238 )));
4239 }
4240 let column = batch.column(0);
4241
4242 for row_idx in 0..column.len() {
4243 use arrow::datatypes::DataType;
4244 let value = if column.is_null(row_idx) {
4245 Value::Null
4246 } else {
4247 match column.data_type() {
4248 DataType::Int64 => {
4249 let arr = column
4250 .as_any()
4251 .downcast_ref::<arrow::array::Int64Array>()
4252 .unwrap();
4253 Value::Number(
4254 arr.value(row_idx).to_string(),
4255 false,
4256 )
4257 }
4258 DataType::Float64 => {
4259 let arr = column
4260 .as_any()
4261 .downcast_ref::<arrow::array::Float64Array>()
4262 .unwrap();
4263 Value::Number(
4264 arr.value(row_idx).to_string(),
4265 false,
4266 )
4267 }
4268 DataType::Utf8 => {
4269 let arr = column
4270 .as_any()
4271 .downcast_ref::<arrow::array::StringArray>()
4272 .unwrap();
4273 Value::SingleQuotedString(
4274 arr.value(row_idx).to_string(),
4275 )
4276 }
4277 DataType::Boolean => {
4278 let arr = column
4279 .as_any()
4280 .downcast_ref::<arrow::array::BooleanArray>()
4281 .unwrap();
4282 Value::Boolean(arr.value(row_idx))
4283 }
4284 other => {
4285 return Err(Error::InvalidArgumentError(
4286 format!(
4287 "unsupported data type in IN subquery: {other:?}"
4288 ),
4289 ));
4290 }
4291 }
4292 };
4293 collected_values.push(ValueWithSpan {
4294 value,
4295 span: Span::empty(),
4296 });
4297 }
4298 }
4299
4300 collected_values
4301 }
4302 _ => {
4303 return Err(Error::InvalidArgumentError(
4304 "IN subquery must be a SELECT statement".into(),
4305 ));
4306 }
4307 };
4308
4309 result_stack.push(SqlExpr::InList {
4311 expr: left_expr,
4312 list: values.into_iter().map(SqlExpr::Value).collect(),
4313 negated,
4314 });
4315 }
4316 SqlExpr::Subquery(subquery) => {
4317 let scalar_expr = self.materialize_scalar_subquery(*subquery)?;
4318 result_stack.push(scalar_expr);
4319 }
4320 SqlExpr::Case {
4321 case_token,
4322 end_token,
4323 operand,
4324 conditions,
4325 else_result,
4326 } => {
4327 let new_operand = match operand {
4328 Some(expr) => Some(Box::new(self.materialize_in_subquery(*expr)?)),
4329 None => None,
4330 };
4331 let mut new_conditions = Vec::with_capacity(conditions.len());
4332 for branch in conditions {
4333 let condition = self.materialize_in_subquery(branch.condition)?;
4334 let result = self.materialize_in_subquery(branch.result)?;
4335 new_conditions.push(sqlparser::ast::CaseWhen { condition, result });
4336 }
4337 let new_else = match else_result {
4338 Some(expr) => Some(Box::new(self.materialize_in_subquery(*expr)?)),
4339 None => None,
4340 };
4341 result_stack.push(SqlExpr::Case {
4342 case_token,
4343 end_token,
4344 operand: new_operand,
4345 conditions: new_conditions,
4346 else_result: new_else,
4347 });
4348 }
4349 SqlExpr::BinaryOp { left, op, right } => {
4350 work_stack.push(WorkItem::BuildBinaryOp {
4355 op,
4356 left: left.clone(),
4357 right_done: false,
4358 });
4359 work_stack.push(WorkItem::Process(left));
4363 work_stack.push(WorkItem::Process(right));
4364 }
4365 SqlExpr::UnaryOp { op, expr } => {
4366 work_stack.push(WorkItem::BuildUnaryOp { op });
4367 work_stack.push(WorkItem::Process(expr));
4368 }
4369 SqlExpr::Nested(inner) => {
4370 work_stack.push(WorkItem::BuildNested);
4371 work_stack.push(WorkItem::Process(inner));
4372 }
4373 SqlExpr::IsNull(inner) => {
4374 work_stack.push(WorkItem::BuildIsNull);
4375 work_stack.push(WorkItem::Process(inner));
4376 }
4377 SqlExpr::IsNotNull(inner) => {
4378 work_stack.push(WorkItem::BuildIsNotNull);
4379 work_stack.push(WorkItem::Process(inner));
4380 }
4381 SqlExpr::Between {
4382 expr,
4383 negated,
4384 low,
4385 high,
4386 } => {
4387 work_stack.push(WorkItem::FinishBetween { negated });
4388 work_stack.push(WorkItem::Process(high));
4389 work_stack.push(WorkItem::Process(low));
4390 work_stack.push(WorkItem::Process(expr));
4391 }
4392 other => {
4394 result_stack.push(other);
4395 }
4396 }
4397 }
4398 WorkItem::BuildBinaryOp {
4399 op,
4400 left,
4401 right_done,
4402 } => {
4403 if !right_done {
4404 let left_result = result_stack.pop().unwrap();
4406 work_stack.push(WorkItem::BuildBinaryOp {
4407 op,
4408 left: Box::new(left_result),
4409 right_done: true,
4410 });
4411 } else {
4412 let right_result = result_stack.pop().unwrap();
4414 let left_result = *left;
4415 result_stack.push(SqlExpr::BinaryOp {
4416 left: Box::new(left_result),
4417 op,
4418 right: Box::new(right_result),
4419 });
4420 }
4421 }
4422 WorkItem::BuildUnaryOp { op } => {
4423 let inner = result_stack.pop().unwrap();
4424 result_stack.push(SqlExpr::UnaryOp {
4425 op,
4426 expr: Box::new(inner),
4427 });
4428 }
4429 WorkItem::BuildNested => {
4430 let inner = result_stack.pop().unwrap();
4431 result_stack.push(SqlExpr::Nested(Box::new(inner)));
4432 }
4433 WorkItem::BuildIsNull => {
4434 let inner = result_stack.pop().unwrap();
4435 result_stack.push(SqlExpr::IsNull(Box::new(inner)));
4436 }
4437 WorkItem::BuildIsNotNull => {
4438 let inner = result_stack.pop().unwrap();
4439 result_stack.push(SqlExpr::IsNotNull(Box::new(inner)));
4440 }
4441 WorkItem::FinishBetween { negated } => {
4442 let high_result = result_stack.pop().unwrap();
4443 let low_result = result_stack.pop().unwrap();
4444 let expr_result = result_stack.pop().unwrap();
4445 result_stack.push(SqlExpr::Between {
4446 expr: Box::new(expr_result),
4447 negated,
4448 low: Box::new(low_result),
4449 high: Box::new(high_result),
4450 });
4451 }
4452 }
4453 }
4454
4455 Ok(result_stack
4457 .pop()
4458 .expect("result stack should have exactly one item"))
4459 }
4460
4461 fn handle_query(&self, query: Query) -> SqlResult<RuntimeStatementResult<P>> {
4462 let mut visited_views = FxHashSet::default();
4463 self.execute_query_with_view_support(query, &mut visited_views)
4464 }
4465
4466 fn execute_query_with_view_support(
4467 &self,
4468 query: Query,
4469 visited_views: &mut FxHashSet<String>,
4470 ) -> SqlResult<RuntimeStatementResult<P>> {
4471 if let Some(result) = self.try_execute_simple_view_select(&query, visited_views)? {
4472 return Ok(result);
4473 }
4474
4475 if let Some(result) = self.try_execute_view_set_operation(&query, visited_views)? {
4476 return Ok(result);
4477 }
4478
4479 if let Some(result) = self.try_execute_simple_derived_select(&query, visited_views)? {
4480 return Ok(result);
4481 }
4482
4483 if let Some(result) = self.try_handle_pragma_table_info(&query)? {
4485 return Ok(result);
4486 }
4487
4488 let select_plan = self.build_select_plan(query)?;
4489 self.execute_plan_statement(PlanStatement::Select(Box::new(select_plan)))
4490 }
4491
4492 fn try_execute_simple_view_select(
4493 &self,
4494 query: &Query,
4495 visited_views: &mut FxHashSet<String>,
4496 ) -> SqlResult<Option<RuntimeStatementResult<P>>> {
4497 use sqlparser::ast::SetExpr;
4498
4499 if query.with.is_some() || query.order_by.is_some() || query.limit_clause.is_some() {
4501 return Ok(None);
4502 }
4503
4504 let select = match query.body.as_ref() {
4505 SetExpr::Select(select) => select,
4506 _ => return Ok(None),
4507 };
4508
4509 if select.distinct.is_some()
4510 || select.selection.is_some()
4511 || !group_by_is_empty(&select.group_by)
4512 || select.having.is_some()
4513 || !select.cluster_by.is_empty()
4514 || !select.distribute_by.is_empty()
4515 || !select.sort_by.is_empty()
4516 || select.top.is_some()
4517 || select.value_table_mode.is_some()
4518 || !select.named_window.is_empty()
4519 || select.qualify.is_some()
4520 || select.connect_by.is_some()
4521 {
4522 return Ok(None);
4523 }
4524
4525 if select.from.len() != 1 {
4526 return Ok(None);
4527 }
4528
4529 let table_with_joins = &select.from[0];
4530 if table_with_joins_has_join(table_with_joins) {
4531 return Ok(None);
4532 }
4533
4534 let (view_display_name, view_canonical_name, table_alias) = match &table_with_joins.relation
4535 {
4536 TableFactor::Table { name, alias, .. } => {
4537 let (display, canonical) = canonical_object_name(name)?;
4538 let catalog = self.engine.context().table_catalog();
4539 let Some(table_id) = catalog.table_id(&canonical) else {
4540 return Ok(None);
4541 };
4542 if !self.engine.context().is_view(table_id)? {
4543 return Ok(None);
4544 }
4545 let alias_name = alias.as_ref().map(|a| a.name.value.clone());
4546 (display, canonical, alias_name)
4547 }
4548 _ => return Ok(None),
4549 };
4550
4551 enum ProjectionKind {
4553 All,
4554 Columns(Vec<(String, String)>), }
4556
4557 let projection = if select
4558 .projection
4559 .iter()
4560 .any(|item| matches!(item, SelectItem::Wildcard(_)))
4561 {
4562 if select.projection.len() != 1 {
4563 return Ok(None);
4564 }
4565 ProjectionKind::All
4566 } else {
4567 let mut columns = Vec::with_capacity(select.projection.len());
4568 for item in &select.projection {
4569 match item {
4570 SelectItem::UnnamedExpr(SqlExpr::Identifier(ident)) => {
4571 let name = ident.value.clone();
4572 columns.push((name.clone(), name));
4573 }
4574 SelectItem::ExprWithAlias { expr, alias } => {
4575 let source = match expr {
4576 SqlExpr::Identifier(ident) => ident.value.clone(),
4577 SqlExpr::CompoundIdentifier(idents) => {
4578 if idents.is_empty() {
4579 return Ok(None);
4580 }
4581 idents.last().unwrap().value.clone()
4582 }
4583 _ => return Ok(None),
4584 };
4585 columns.push((source, alias.value.clone()));
4586 }
4587 SelectItem::UnnamedExpr(SqlExpr::CompoundIdentifier(parts)) => {
4588 if parts.is_empty() {
4589 return Ok(None);
4590 }
4591 if parts.len() == 2 {
4593 let qualifier = parts[0].value.to_ascii_lowercase();
4594 if let Some(ref alias_name) = table_alias {
4595 if qualifier != alias_name.to_ascii_lowercase() {
4596 return Ok(None);
4597 }
4598 } else if qualifier != view_display_name.to_ascii_lowercase() {
4599 return Ok(None);
4600 }
4601 } else if parts.len() != 1 {
4602 return Ok(None);
4603 }
4604 let column_name = parts.last().unwrap().value.clone();
4605 columns.push((column_name.clone(), column_name));
4606 }
4607 _ => return Ok(None),
4608 }
4609 }
4610 ProjectionKind::Columns(columns)
4611 };
4612
4613 let context = self.engine.context();
4614 let definition = context
4615 .view_definition(&view_canonical_name)?
4616 .ok_or_else(|| {
4617 Error::CatalogError(format!(
4618 "Binder Error: view '{}' does not have a stored definition",
4619 view_display_name
4620 ))
4621 })?;
4622
4623 if !visited_views.insert(view_canonical_name.clone()) {
4624 return Err(Error::CatalogError(format!(
4625 "Binder Error: cyclic view reference involving '{}'",
4626 view_display_name
4627 )));
4628 }
4629
4630 let view_query = Self::parse_view_query(&definition)?;
4631 let view_result = self.execute_query_with_view_support(view_query, visited_views);
4632 visited_views.remove(&view_canonical_name);
4633 let view_result = view_result?;
4634
4635 let RuntimeStatementResult::Select {
4636 execution: view_execution,
4637 schema: view_schema,
4638 ..
4639 } = view_result
4640 else {
4641 return Err(Error::InvalidArgumentError(format!(
4642 "view '{}' definition did not produce a SELECT result",
4643 view_display_name
4644 )));
4645 };
4646
4647 match projection {
4648 ProjectionKind::All => {
4649 let select_result = RuntimeStatementResult::Select {
4651 execution: view_execution,
4652 table_name: view_display_name,
4653 schema: view_schema,
4654 };
4655 Ok(Some(select_result))
4656 }
4657 ProjectionKind::Columns(columns) => {
4658 let exec = *view_execution;
4659 let batches = exec.collect()?;
4660 let view_fields = view_schema.fields();
4661 let mut name_to_index =
4662 FxHashMap::with_capacity_and_hasher(view_fields.len(), Default::default());
4663 for (idx, field) in view_fields.iter().enumerate() {
4664 name_to_index.insert(field.name().to_ascii_lowercase(), idx);
4665 }
4666
4667 let mut column_indices = Vec::with_capacity(columns.len());
4668 let mut projected_fields = Vec::with_capacity(columns.len());
4669
4670 for (source, output) in columns {
4671 let lookup = source.to_ascii_lowercase();
4672 let Some(&idx) = name_to_index.get(&lookup) else {
4673 return Err(Error::InvalidArgumentError(format!(
4674 "Binder Error: view '{}' does not have a column named '{}'",
4675 view_display_name, source
4676 )));
4677 };
4678 column_indices.push(idx);
4679 let origin_field = view_fields[idx].clone();
4680 let projected_field = Field::new(
4681 &output,
4682 origin_field.data_type().clone(),
4683 origin_field.is_nullable(),
4684 )
4685 .with_metadata(origin_field.metadata().clone());
4686 projected_fields.push(projected_field);
4687 }
4688
4689 let projected_schema = Arc::new(Schema::new(projected_fields));
4690
4691 let mut projected_batches = Vec::with_capacity(batches.len());
4692 for batch in batches {
4693 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(column_indices.len());
4694 for idx in &column_indices {
4695 arrays.push(Arc::clone(batch.column(*idx)));
4696 }
4697 let projected = RecordBatch::try_new(Arc::clone(&projected_schema), arrays)?;
4698 projected_batches.push(projected);
4699 }
4700
4701 let combined_batch = if projected_batches.is_empty() {
4702 RecordBatch::new_empty(Arc::clone(&projected_schema))
4703 } else if projected_batches.len() == 1 {
4704 projected_batches.remove(0)
4705 } else {
4706 concat_batches(&projected_schema, projected_batches.iter())?
4707 };
4708
4709 let select_execution = SelectExecution::from_batch(
4710 view_display_name.clone(),
4711 Arc::clone(&projected_schema),
4712 combined_batch,
4713 );
4714
4715 Ok(Some(RuntimeStatementResult::Select {
4716 execution: Box::new(select_execution),
4717 table_name: view_display_name,
4718 schema: projected_schema,
4719 }))
4720 }
4721 }
4722 }
4723
4724 fn try_execute_simple_derived_select(
4725 &self,
4726 query: &Query,
4727 visited_views: &mut FxHashSet<String>,
4728 ) -> SqlResult<Option<RuntimeStatementResult<P>>> {
4729 use sqlparser::ast::{Expr as SqlExpr, SelectItem, SetExpr};
4730
4731 if query.with.is_some() || query.order_by.is_some() || query.limit_clause.is_some() {
4733 return Ok(None);
4734 }
4735 if query.fetch.is_some() {
4736 return Ok(None);
4737 }
4738
4739 let select = match query.body.as_ref() {
4740 SetExpr::Select(select) => select,
4741 _ => return Ok(None),
4742 };
4743
4744 if select.from.len() != 1 {
4745 return Ok(None);
4746 }
4747
4748 let table_with_joins = &select.from[0];
4749 let (subquery, alias, lateral) = match &table_with_joins.relation {
4750 TableFactor::Derived {
4751 subquery,
4752 alias,
4753 lateral,
4754 ..
4755 } => (subquery, alias.as_ref(), *lateral),
4756 _ => return Ok(None),
4757 };
4758
4759 if table_with_joins_has_join(table_with_joins) {
4760 return Err(Error::InvalidArgumentError(
4761 "Binder Error: derived table queries with JOINs are not supported yet".into(),
4762 ));
4763 }
4764
4765 if lateral {
4766 return Err(Error::InvalidArgumentError(
4767 "Binder Error: LATERAL derived tables are not supported yet".into(),
4768 ));
4769 }
4770 if select.distinct.is_some()
4771 || select.selection.is_some()
4772 || !group_by_is_empty(&select.group_by)
4773 || select.having.is_some()
4774 || !select.cluster_by.is_empty()
4775 || !select.distribute_by.is_empty()
4776 || !select.sort_by.is_empty()
4777 || select.top.is_some()
4778 || select.value_table_mode.is_some()
4779 || !select.named_window.is_empty()
4780 || select.qualify.is_some()
4781 || select.connect_by.is_some()
4782 {
4783 return Err(Error::InvalidArgumentError(
4784 "Binder Error: advanced clauses are not supported for derived table queries yet"
4785 .into(),
4786 ));
4787 }
4788
4789 let inner_query = *subquery.clone();
4790 let inner_result = self.execute_query_with_view_support(inner_query, visited_views)?;
4791 let (inner_exec, inner_schema, inner_table_name) =
4792 self.extract_select_result(inner_result)?;
4793
4794 let alias_name = alias.map(|a| a.name.value.clone());
4795 let alias_columns = alias.and_then(|a| {
4796 if a.columns.is_empty() {
4797 None
4798 } else {
4799 Some(
4800 a.columns
4801 .iter()
4802 .map(|col| col.name.value.clone())
4803 .collect::<Vec<_>>(),
4804 )
4805 }
4806 });
4807
4808 if let Some(ref columns) = alias_columns
4809 && columns.len() != inner_schema.fields().len()
4810 {
4811 return Err(Error::InvalidArgumentError(
4812 "Binder Error: derived table column alias count must match projection".into(),
4813 ));
4814 }
4815
4816 let alias_lower = alias_name.as_ref().map(|name| name.to_ascii_lowercase());
4817 let inner_lower = inner_table_name.to_ascii_lowercase();
4818
4819 enum DerivedProjection {
4820 All,
4821 Columns(Vec<(String, String)>),
4822 }
4823
4824 let resolve_compound_identifier = |parts: &[Ident]| -> SqlResult<String> {
4825 if parts.is_empty() {
4826 return Err(Error::InvalidArgumentError(
4827 "Binder Error: empty identifier in derived table projection".into(),
4828 ));
4829 }
4830 if parts.len() == 1 {
4831 return Ok(parts[0].value.clone());
4832 }
4833 if parts.len() == 2 {
4834 let qualifier_lower = parts[0].value.to_ascii_lowercase();
4835 let qualifier_display = parts[0].value.clone();
4836 if let Some(ref alias_lower) = alias_lower {
4837 if qualifier_lower != *alias_lower {
4838 return Err(Error::InvalidArgumentError(format!(
4839 "Binder Error: derived table qualifier '{}' does not match alias '{}'",
4840 qualifier_display,
4841 alias_name.as_deref().unwrap_or(""),
4842 )));
4843 }
4844 } else if qualifier_lower != inner_lower {
4845 return Err(Error::InvalidArgumentError(format!(
4846 "Binder Error: derived table qualifier '{}' does not match subquery name '{}'",
4847 qualifier_display, inner_table_name
4848 )));
4849 }
4850 return Ok(parts[1].value.clone());
4851 }
4852 Err(Error::InvalidArgumentError(
4853 "Binder Error: multi-part qualified identifiers are not supported for derived tables yet"
4854 .into(),
4855 ))
4856 };
4857
4858 let build_projection_columns = |items: &[SelectItem]| -> SqlResult<Vec<(String, String)>> {
4859 let mut columns = Vec::with_capacity(items.len());
4860 for item in items {
4861 match item {
4862 SelectItem::UnnamedExpr(SqlExpr::Identifier(ident)) => {
4863 let name = ident.value.clone();
4864 columns.push((name.clone(), name));
4865 }
4866 SelectItem::ExprWithAlias { expr, alias } => {
4867 let source = match expr {
4868 SqlExpr::Identifier(ident) => ident.value.clone(),
4869 SqlExpr::CompoundIdentifier(parts) => {
4870 resolve_compound_identifier(parts)?
4871 }
4872 _ => {
4873 return Err(Error::InvalidArgumentError(
4874 "Binder Error: complex expressions in derived table projections are not supported yet"
4875 .into(),
4876 ));
4877 }
4878 };
4879 columns.push((source, alias.value.clone()))
4880 }
4881 SelectItem::UnnamedExpr(SqlExpr::CompoundIdentifier(parts)) => {
4882 let column = resolve_compound_identifier(parts)?;
4883 columns.push((column.clone(), column));
4884 }
4885 other => {
4886 return Err(Error::InvalidArgumentError(format!(
4887 "Binder Error: unsupported derived table projection {:?}",
4888 other
4889 )));
4890 }
4891 }
4892 }
4893 Ok(columns)
4894 };
4895
4896 let projection = if select.projection.len() == 1 {
4897 match &select.projection[0] {
4898 SelectItem::Wildcard(_) => DerivedProjection::All,
4899 SelectItem::QualifiedWildcard(kind, _) => match kind {
4900 SelectItemQualifiedWildcardKind::ObjectName(name) => {
4901 let qualifier = Self::object_name_to_string(name)?;
4902 let qualifier_lower = qualifier.to_ascii_lowercase();
4903 if let Some(ref alias_lower) = alias_lower {
4904 if qualifier_lower != *alias_lower {
4905 return Err(Error::InvalidArgumentError(format!(
4906 "Binder Error: derived table qualifier '{}' does not match alias '{}'",
4907 qualifier,
4908 alias_name.as_deref().unwrap_or(""),
4909 )));
4910 }
4911 } else if qualifier_lower != inner_lower {
4912 return Err(Error::InvalidArgumentError(format!(
4913 "Binder Error: derived table qualifier '{}' does not match subquery name '{}'",
4914 qualifier, inner_table_name
4915 )));
4916 }
4917 DerivedProjection::All
4918 }
4919 SelectItemQualifiedWildcardKind::Expr(_) => {
4920 return Err(Error::InvalidArgumentError(
4921 "Binder Error: expression-qualified wildcards are not supported for derived tables yet"
4922 .into(),
4923 ));
4924 }
4925 },
4926 _ => DerivedProjection::Columns(build_projection_columns(&select.projection)?),
4927 }
4928 } else {
4929 if select.projection.iter().any(|item| {
4930 matches!(
4931 item,
4932 SelectItem::Wildcard(_) | SelectItem::QualifiedWildcard(_, _)
4933 )
4934 }) {
4935 return Err(Error::InvalidArgumentError(
4936 "Binder Error: derived table projections cannot mix wildcards with explicit columns"
4937 .into(),
4938 ));
4939 }
4940 DerivedProjection::Columns(build_projection_columns(&select.projection)?)
4941 };
4942
4943 let mut batches = inner_exec.collect()?;
4944 let output_table_name = alias_name.clone().unwrap_or(inner_table_name.clone());
4945
4946 let mut name_to_index = FxHashMap::default();
4947 for (idx, field) in inner_schema.fields().iter().enumerate() {
4948 name_to_index.insert(field.name().to_ascii_lowercase(), idx);
4949 }
4950 if let Some(ref columns) = alias_columns {
4951 for (idx, alias_col) in columns.iter().enumerate() {
4952 name_to_index.insert(alias_col.to_ascii_lowercase(), idx);
4953 }
4954 }
4955
4956 let mut build_projected_result = |column_mappings: Vec<(String, String)>| -> SqlResult<_> {
4957 let mut column_indices = Vec::with_capacity(column_mappings.len());
4958 let mut projected_fields = Vec::with_capacity(column_mappings.len());
4959
4960 for (source, output) in column_mappings {
4961 let key = source.to_ascii_lowercase();
4962 let Some(&idx) = name_to_index.get(&key) else {
4963 return Err(Error::InvalidArgumentError(format!(
4964 "Binder Error: derived table does not provide a column named '{}'",
4965 source
4966 )));
4967 };
4968 column_indices.push(idx);
4969 let origin_field = inner_schema.field(idx).clone();
4970 let projected_field = Field::new(
4971 &output,
4972 origin_field.data_type().clone(),
4973 origin_field.is_nullable(),
4974 )
4975 .with_metadata(origin_field.metadata().clone());
4976 projected_fields.push(projected_field);
4977 }
4978
4979 let projected_schema = Arc::new(Schema::new(projected_fields));
4980 let mut projected_batches = Vec::with_capacity(batches.len());
4981 for batch in batches.drain(..) {
4982 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(column_indices.len());
4983 for idx in &column_indices {
4984 arrays.push(Arc::clone(batch.column(*idx)));
4985 }
4986 let projected = RecordBatch::try_new(Arc::clone(&projected_schema), arrays)
4987 .map_err(|err| {
4988 Error::Internal(format!(
4989 "failed to construct derived table projection batch: {err}"
4990 ))
4991 })?;
4992 projected_batches.push(projected);
4993 }
4994
4995 let combined_batch = if projected_batches.is_empty() {
4996 RecordBatch::new_empty(Arc::clone(&projected_schema))
4997 } else if projected_batches.len() == 1 {
4998 projected_batches.remove(0)
4999 } else {
5000 concat_batches(&projected_schema, projected_batches.iter()).map_err(|err| {
5001 Error::Internal(format!(
5002 "failed to concatenate derived table batches: {err}"
5003 ))
5004 })?
5005 };
5006
5007 Ok((projected_schema, combined_batch))
5008 };
5009
5010 let (final_schema, combined_batch) = match projection {
5011 DerivedProjection::All => {
5012 if let Some(columns) = alias_columns {
5013 let mappings = columns
5014 .iter()
5015 .map(|name| (name.clone(), name.clone()))
5016 .collect::<Vec<_>>();
5017 build_projected_result(mappings)?
5018 } else {
5019 let schema = Arc::clone(&inner_schema);
5020 let combined = if batches.is_empty() {
5021 RecordBatch::new_empty(Arc::clone(&schema))
5022 } else if batches.len() == 1 {
5023 batches.remove(0)
5024 } else {
5025 concat_batches(&schema, batches.iter()).map_err(|err| {
5026 Error::Internal(format!(
5027 "failed to concatenate derived table batches: {err}"
5028 ))
5029 })?
5030 };
5031 (schema, combined)
5032 }
5033 }
5034 DerivedProjection::Columns(mappings) => build_projected_result(mappings)?,
5035 };
5036
5037 let execution = SelectExecution::from_batch(
5038 output_table_name.clone(),
5039 Arc::clone(&final_schema),
5040 combined_batch,
5041 );
5042
5043 Ok(Some(RuntimeStatementResult::Select {
5044 execution: Box::new(execution),
5045 table_name: output_table_name,
5046 schema: final_schema,
5047 }))
5048 }
5049
5050 fn try_execute_view_set_operation(
5051 &self,
5052 query: &Query,
5053 visited_views: &mut FxHashSet<String>,
5054 ) -> SqlResult<Option<RuntimeStatementResult<P>>> {
5055 if !matches!(query.body.as_ref(), SetExpr::SetOperation { .. }) {
5056 return Ok(None);
5057 }
5058
5059 if query.with.is_some()
5060 || query.order_by.is_some()
5061 || query.limit_clause.is_some()
5062 || query.fetch.is_some()
5063 {
5064 return Ok(None);
5065 }
5066
5067 if !self.set_expr_contains_view(query.body.as_ref())? {
5068 return Ok(None);
5069 }
5070
5071 let result = self.evaluate_set_expr(query.body.as_ref(), visited_views)?;
5072 Ok(Some(result))
5073 }
5074
5075 fn evaluate_set_expr(
5076 &self,
5077 expr: &SetExpr,
5078 visited_views: &mut FxHashSet<String>,
5079 ) -> SqlResult<RuntimeStatementResult<P>> {
5080 match expr {
5081 SetExpr::SetOperation {
5082 left,
5083 right,
5084 op,
5085 set_quantifier,
5086 } => {
5087 let left_result = self.evaluate_set_expr(left.as_ref(), visited_views)?;
5088 let right_result = self.evaluate_set_expr(right.as_ref(), visited_views)?;
5089 self.combine_set_results(left_result, right_result, *op, *set_quantifier)
5090 }
5091 SetExpr::Query(subquery) => {
5092 self.execute_query_with_view_support(*subquery.clone(), visited_views)
5093 }
5094 _ => self.execute_setexpr_query(expr, visited_views),
5095 }
5096 }
5097
5098 fn execute_setexpr_query(
5099 &self,
5100 expr: &SetExpr,
5101 visited_views: &mut FxHashSet<String>,
5102 ) -> SqlResult<RuntimeStatementResult<P>> {
5103 let sql = expr.to_string();
5104 let dialect = GenericDialect {};
5105 let statements = parse_sql_with_recursion_limit(&dialect, &sql).map_err(|err| {
5106 Error::InvalidArgumentError(format!(
5107 "failed to parse expanded view query '{sql}': {err}"
5108 ))
5109 })?;
5110
5111 let mut iter = statements.into_iter();
5112 let statement = iter.next().ok_or_else(|| {
5113 Error::InvalidArgumentError("expanded view query did not produce a statement".into())
5114 })?;
5115 if iter.next().is_some() {
5116 return Err(Error::InvalidArgumentError(
5117 "expanded view query produced multiple statements".into(),
5118 ));
5119 }
5120
5121 let query = match statement {
5122 Statement::Query(q) => *q,
5123 other => {
5124 return Err(Error::InvalidArgumentError(format!(
5125 "expanded view query did not produce a SELECT statement: {other:?}"
5126 )));
5127 }
5128 };
5129
5130 self.execute_query_with_view_support(query, visited_views)
5131 }
5132
5133 fn combine_set_results(
5134 &self,
5135 left: RuntimeStatementResult<P>,
5136 right: RuntimeStatementResult<P>,
5137 op: SetOperator,
5138 quantifier: SetQuantifier,
5139 ) -> SqlResult<RuntimeStatementResult<P>> {
5140 match op {
5141 SetOperator::Union => self.union_select_results(left, right, quantifier),
5142 other => Err(Error::InvalidArgumentError(format!(
5143 "Binder Error: unsupported set operator {other:?} in view query"
5144 ))),
5145 }
5146 }
5147
5148 fn union_select_results(
5149 &self,
5150 left: RuntimeStatementResult<P>,
5151 right: RuntimeStatementResult<P>,
5152 quantifier: SetQuantifier,
5153 ) -> SqlResult<RuntimeStatementResult<P>> {
5154 let (left_exec, left_schema, left_name) = self.extract_select_result(left)?;
5155 let (right_exec, right_schema, _) = self.extract_select_result(right)?;
5156
5157 self.ensure_schemas_compatible(&left_schema, &right_schema)?;
5158
5159 let mut batches = Vec::new();
5160 batches.extend(left_exec.collect()?);
5161 batches.extend(right_exec.collect()?);
5162
5163 let mut combined_batch = if batches.is_empty() {
5164 RecordBatch::new_empty(Arc::clone(&left_schema))
5165 } else if batches.len() == 1 {
5166 batches.pop().expect("length checked above")
5167 } else {
5168 concat_batches(&left_schema, batches.iter()).map_err(|err| {
5169 Error::Internal(format!("failed to concatenate UNION batches: {err}"))
5170 })?
5171 };
5172
5173 if matches!(quantifier, SetQuantifier::Distinct) {
5174 combined_batch = self.distinct_batch(&left_schema, combined_batch)?;
5175 }
5176
5177 let execution = SelectExecution::from_batch(
5178 left_name.clone(),
5179 Arc::clone(&left_schema),
5180 combined_batch,
5181 );
5182
5183 Ok(RuntimeStatementResult::Select {
5184 execution: Box::new(execution),
5185 table_name: left_name,
5186 schema: left_schema,
5187 })
5188 }
5189
5190 fn extract_select_result(
5191 &self,
5192 result: RuntimeStatementResult<P>,
5193 ) -> SqlResult<(SelectExecution<P>, Arc<Schema>, String)> {
5194 match result {
5195 RuntimeStatementResult::Select {
5196 execution,
5197 schema,
5198 table_name,
5199 } => Ok((*execution, schema, table_name)),
5200 _ => Err(Error::InvalidArgumentError(
5201 "expected SELECT result while evaluating set operation".into(),
5202 )),
5203 }
5204 }
5205
5206 fn ensure_schemas_compatible(&self, left: &Arc<Schema>, right: &Arc<Schema>) -> SqlResult<()> {
5207 if left.fields().len() != right.fields().len() {
5208 return Err(Error::InvalidArgumentError(
5209 "Binder Error: UNION inputs project different column counts".into(),
5210 ));
5211 }
5212
5213 for (idx, (l_field, r_field)) in left.fields().iter().zip(right.fields().iter()).enumerate()
5214 {
5215 if l_field.data_type() != r_field.data_type() {
5216 return Err(Error::InvalidArgumentError(format!(
5217 "Binder Error: UNION column {} type mismatch ({:?} vs {:?})",
5218 idx + 1,
5219 l_field.data_type(),
5220 r_field.data_type()
5221 )));
5222 }
5223 }
5224
5225 Ok(())
5226 }
5227
5228 fn distinct_batch(&self, schema: &Arc<Schema>, batch: RecordBatch) -> SqlResult<RecordBatch> {
5229 if batch.num_rows() <= 1 {
5230 return Ok(batch);
5231 }
5232
5233 let sort_fields: Vec<SortField> = schema
5234 .fields()
5235 .iter()
5236 .map(|field| SortField::new(field.data_type().clone()))
5237 .collect();
5238
5239 let converter = RowConverter::new(sort_fields)
5240 .map_err(|err| Error::Internal(format!("failed to initialize row converter: {err}")))?;
5241 let rows = converter
5242 .convert_columns(batch.columns())
5243 .map_err(|err| Error::Internal(format!("failed to row-encode union result: {err}")))?;
5244
5245 let mut seen = FxHashSet::default();
5246 let mut indices = Vec::new();
5247 let mut has_duplicates = false;
5248 for (idx, row) in rows.iter().enumerate() {
5249 if seen.insert(row) {
5250 indices.push(idx as u32);
5251 } else {
5252 has_duplicates = true;
5253 }
5254 }
5255
5256 if !has_duplicates {
5257 return Ok(batch);
5258 }
5259
5260 let index_array = UInt32Array::from(indices);
5261 let mut columns = Vec::with_capacity(batch.num_columns());
5262 for column in batch.columns() {
5263 let taken = take(column.as_ref(), &index_array, None).map_err(|err| {
5264 Error::Internal(format!("failed to materialize DISTINCT rows: {err}"))
5265 })?;
5266 columns.push(taken);
5267 }
5268
5269 RecordBatch::try_new(Arc::clone(schema), columns)
5270 .map_err(|err| Error::Internal(format!("failed to build DISTINCT RecordBatch: {err}")))
5271 }
5272
5273 fn set_expr_contains_view(&self, expr: &SetExpr) -> SqlResult<bool> {
5274 match expr {
5275 SetExpr::Select(select) => self.select_contains_view(select.as_ref()),
5276 SetExpr::Query(query) => self.set_expr_contains_view(&query.body),
5277 SetExpr::SetOperation { left, right, .. } => Ok(self
5278 .set_expr_contains_view(left.as_ref())?
5279 || self.set_expr_contains_view(right.as_ref())?),
5280 _ => Ok(false),
5281 }
5282 }
5283
5284 fn select_contains_view(&self, select: &Select) -> SqlResult<bool> {
5285 for from_item in &select.from {
5286 if self.table_with_joins_contains_view(from_item)? {
5287 return Ok(true);
5288 }
5289 }
5290 Ok(false)
5291 }
5292
5293 fn table_with_joins_contains_view(&self, table: &TableWithJoins) -> SqlResult<bool> {
5294 if self.table_factor_contains_view(&table.relation)? {
5295 return Ok(true);
5296 }
5297
5298 for join in &table.joins {
5299 if self.table_factor_contains_view(&join.relation)? {
5300 return Ok(true);
5301 }
5302 }
5303
5304 Ok(false)
5305 }
5306
5307 fn table_factor_contains_view(&self, factor: &TableFactor) -> SqlResult<bool> {
5308 match factor {
5309 TableFactor::Table { name, .. } => {
5310 let (_, canonical) = canonical_object_name(name)?;
5311 let catalog = self.engine.context().table_catalog();
5312 let Some(table_id) = catalog.table_id(&canonical) else {
5313 return Ok(false);
5314 };
5315 self.engine.context().is_view(table_id)
5316 }
5317 TableFactor::Derived { subquery, .. } => self.set_expr_contains_view(&subquery.body),
5318 TableFactor::NestedJoin {
5319 table_with_joins, ..
5320 } => self.table_with_joins_contains_view(table_with_joins),
5321 _ => Ok(false),
5322 }
5323 }
5324
5325 fn build_select_plan(&self, query: Query) -> SqlResult<SelectPlan> {
5326 if self.engine.session().has_active_transaction() && self.engine.session().is_aborted() {
5327 return Err(Error::TransactionContextError(
5328 "TransactionContext Error: transaction is aborted".into(),
5329 ));
5330 }
5331
5332 validate_simple_query(&query)?;
5333 let catalog = self.engine.context().table_catalog();
5334 let resolver = catalog.identifier_resolver();
5335
5336 let (mut select_plan, select_context) =
5337 self.translate_query_body(query.body.as_ref(), &resolver)?;
5338 if let Some(order_by) = &query.order_by {
5339 if !select_plan.aggregates.is_empty() {
5340 return Err(Error::InvalidArgumentError(
5341 "ORDER BY is not supported for aggregate queries".into(),
5342 ));
5343 }
5344 let order_plan = self.translate_order_by(&resolver, select_context, order_by)?;
5345 select_plan = select_plan.with_order_by(order_plan);
5346 }
5347 Ok(select_plan)
5348 }
5349
5350 fn build_select_plan_internal(
5359 &self,
5360 query: Query,
5361 resolver: &IdentifierResolver<'_>,
5362 outer_scopes: &[IdentifierContext],
5363 subqueries: &mut Vec<llkv_plan::FilterSubquery>,
5364 correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
5365 ) -> SqlResult<SelectPlan> {
5366 if self.engine.session().has_active_transaction() && self.engine.session().is_aborted() {
5367 return Err(Error::TransactionContextError(
5368 "TransactionContext Error: transaction is aborted".into(),
5369 ));
5370 }
5371
5372 validate_simple_query(&query)?;
5373
5374 let (mut select_plan, select_context) = self.translate_query_body_internal(
5375 query.body.as_ref(),
5376 resolver,
5377 outer_scopes,
5378 subqueries,
5379 correlated_tracker,
5380 )?;
5381 if let Some(order_by) = &query.order_by {
5382 if !select_plan.aggregates.is_empty() {
5383 return Err(Error::InvalidArgumentError(
5384 "ORDER BY is not supported for aggregate queries".into(),
5385 ));
5386 }
5387 let order_plan = self.translate_order_by(resolver, select_context, order_by)?;
5388 select_plan = select_plan.with_order_by(order_plan);
5389 }
5390 Ok(select_plan)
5391 }
5392
5393 fn translate_select(
5394 &self,
5395 select: &Select,
5396 resolver: &IdentifierResolver<'_>,
5397 ) -> SqlResult<(SelectPlan, IdentifierContext)> {
5398 let mut subqueries = Vec::new();
5399 let result =
5400 self.translate_select_internal(select, resolver, &[], &mut subqueries, None)?;
5401 if !subqueries.is_empty() {
5402 return Err(Error::Internal(
5403 "translate_select: unexpected subqueries from non-correlated translation".into(),
5404 ));
5405 }
5406 Ok(result)
5407 }
5408
5409 fn translate_query_body(
5410 &self,
5411 body: &SetExpr,
5412 resolver: &IdentifierResolver<'_>,
5413 ) -> SqlResult<(SelectPlan, IdentifierContext)> {
5414 let mut subqueries = Vec::new();
5415 let result =
5416 self.translate_query_body_internal(body, resolver, &[], &mut subqueries, None)?;
5417 if !subqueries.is_empty() {
5418 return Err(Error::Internal(
5419 "translate_query_body: unexpected subqueries from non-correlated translation"
5420 .into(),
5421 ));
5422 }
5423 Ok(result)
5424 }
5425
5426 fn translate_query_body_internal(
5427 &self,
5428 body: &SetExpr,
5429 resolver: &IdentifierResolver<'_>,
5430 outer_scopes: &[IdentifierContext],
5431 subqueries: &mut Vec<llkv_plan::FilterSubquery>,
5432 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
5433 ) -> SqlResult<(SelectPlan, IdentifierContext)> {
5434 match body {
5435 SetExpr::Select(select) => self.translate_select_internal(
5436 select.as_ref(),
5437 resolver,
5438 outer_scopes,
5439 subqueries,
5440 correlated_tracker,
5441 ),
5442 SetExpr::Query(query) => self.translate_query_body_internal(
5443 &query.body,
5444 resolver,
5445 outer_scopes,
5446 subqueries,
5447 correlated_tracker,
5448 ),
5449 SetExpr::SetOperation {
5450 left,
5451 right,
5452 op,
5453 set_quantifier,
5454 } => {
5455 let left_tracker = correlated_tracker.reborrow();
5456 let (left_plan, left_context) = self.translate_query_body_internal(
5457 left.as_ref(),
5458 resolver,
5459 outer_scopes,
5460 subqueries,
5461 left_tracker,
5462 )?;
5463
5464 let right_tracker = correlated_tracker.reborrow();
5465 let (right_plan, _) = self.translate_query_body_internal(
5466 right.as_ref(),
5467 resolver,
5468 outer_scopes,
5469 subqueries,
5470 right_tracker,
5471 )?;
5472
5473 let operator = match op {
5474 sqlparser::ast::SetOperator::Union => llkv_plan::CompoundOperator::Union,
5475 sqlparser::ast::SetOperator::Intersect => {
5476 llkv_plan::CompoundOperator::Intersect
5477 }
5478 sqlparser::ast::SetOperator::Except | sqlparser::ast::SetOperator::Minus => {
5479 llkv_plan::CompoundOperator::Except
5480 }
5481 };
5482
5483 let quantifier = match set_quantifier {
5484 SetQuantifier::All => llkv_plan::CompoundQuantifier::All,
5485 _ => llkv_plan::CompoundQuantifier::Distinct,
5486 };
5487
5488 let mut compound = if let Some(existing) = left_plan.compound {
5489 existing
5490 } else {
5491 llkv_plan::CompoundSelectPlan::new(left_plan)
5492 };
5493 compound.push_operation(operator, quantifier, right_plan);
5494
5495 let result_plan = SelectPlan::new("").with_compound(compound);
5496
5497 Ok((result_plan, left_context))
5498 }
5499 other => Err(Error::InvalidArgumentError(format!(
5500 "unsupported query expression: {other:?}"
5501 ))),
5502 }
5503 }
5504
5505 fn translate_select_internal(
5506 &self,
5507 select: &Select,
5508 resolver: &IdentifierResolver<'_>,
5509 outer_scopes: &[IdentifierContext],
5510 subqueries: &mut Vec<llkv_plan::FilterSubquery>,
5511 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
5512 ) -> SqlResult<(SelectPlan, IdentifierContext)> {
5513 let mut distinct = match &select.distinct {
5514 None => false,
5515 Some(Distinct::Distinct) => true,
5516 Some(Distinct::On(_)) => {
5517 return Err(Error::InvalidArgumentError(
5518 "SELECT DISTINCT ON is not supported".into(),
5519 ));
5520 }
5521 };
5522 if matches!(
5523 select.value_table_mode,
5524 Some(
5525 sqlparser::ast::ValueTableMode::DistinctAsStruct
5526 | sqlparser::ast::ValueTableMode::DistinctAsValue
5527 )
5528 ) {
5529 distinct = true;
5530 }
5531 if select.top.is_some() {
5532 return Err(Error::InvalidArgumentError(
5533 "SELECT TOP is not supported".into(),
5534 ));
5535 }
5536 if select.exclude.is_some() {
5537 return Err(Error::InvalidArgumentError(
5538 "SELECT EXCLUDE is not supported".into(),
5539 ));
5540 }
5541 if select.into.is_some() {
5542 return Err(Error::InvalidArgumentError(
5543 "SELECT INTO is not supported".into(),
5544 ));
5545 }
5546 if !select.lateral_views.is_empty() {
5547 return Err(Error::InvalidArgumentError(
5548 "LATERAL VIEW is not supported".into(),
5549 ));
5550 }
5551 if select.prewhere.is_some() {
5552 return Err(Error::InvalidArgumentError(
5553 "PREWHERE is not supported".into(),
5554 ));
5555 }
5556 if !select.cluster_by.is_empty()
5557 || !select.distribute_by.is_empty()
5558 || !select.sort_by.is_empty()
5559 {
5560 return Err(Error::InvalidArgumentError(
5561 "CLUSTER/DISTRIBUTE/SORT BY clauses are not supported".into(),
5562 ));
5563 }
5564 if !select.named_window.is_empty()
5565 || select.qualify.is_some()
5566 || select.connect_by.is_some()
5567 {
5568 return Err(Error::InvalidArgumentError(
5569 "advanced SELECT clauses are not supported".into(),
5570 ));
5571 }
5572
5573 let table_alias = select
5574 .from
5575 .first()
5576 .and_then(|table_with_joins| match &table_with_joins.relation {
5577 TableFactor::Table { alias, .. } => alias.as_ref().map(|a| a.name.value.clone()),
5578 _ => None,
5579 });
5580
5581 let has_joins = select.from.iter().any(table_with_joins_has_join);
5582 let mut join_conditions: Vec<Option<SqlExpr>> = Vec::new();
5583 let mut scalar_subqueries: Vec<llkv_plan::ScalarSubquery> = Vec::new();
5584 let catalog = self.engine.context().table_catalog();
5586 let has_group_by = !group_by_is_empty(&select.group_by);
5587 let (mut plan, id_context) = if select.from.is_empty() {
5588 if has_group_by {
5589 return Err(Error::InvalidArgumentError(
5590 "GROUP BY requires a FROM clause".into(),
5591 ));
5592 }
5593 let mut p = SelectPlan::new("");
5595 let projections = self.build_projection_list(
5596 resolver,
5597 IdentifierContext::new(None),
5598 &select.projection,
5599 outer_scopes,
5600 &mut scalar_subqueries,
5601 correlated_tracker.reborrow(),
5602 )?;
5603 p = p.with_projections(projections);
5604 (p, IdentifierContext::new(None))
5605 } else if select.from.len() == 1 && !has_joins {
5606 let (display_name, canonical_name) = extract_single_table(&select.from)?;
5608 let table_id = catalog.table_id(&canonical_name);
5609 let mut p = SelectPlan::new(display_name.clone());
5610 let single_table_context =
5611 IdentifierContext::new(table_id).with_table_alias(table_alias.clone());
5612 if let Some(alias) = table_alias.as_ref() {
5613 validate_projection_alias_qualifiers(&select.projection, alias)?;
5614 }
5615 if !has_group_by
5616 && let Some(aggregates) = self.detect_simple_aggregates(&select.projection)?
5617 {
5618 p = p.with_aggregates(aggregates);
5619 } else {
5620 let projections = self.build_projection_list(
5621 resolver,
5622 single_table_context.clone(),
5623 &select.projection,
5624 outer_scopes,
5625 &mut scalar_subqueries,
5626 correlated_tracker.reborrow(),
5627 )?;
5628 p = p.with_projections(projections);
5629 }
5630 (p, single_table_context)
5631 } else {
5632 let (tables, join_metadata, extracted_filters) = extract_tables(&select.from)?;
5634 join_conditions = extracted_filters;
5635 let mut p = SelectPlan::with_tables(tables).with_joins(join_metadata);
5636 let projections = self.build_projection_list(
5639 resolver,
5640 IdentifierContext::new(None),
5641 &select.projection,
5642 outer_scopes,
5643 &mut scalar_subqueries,
5644 correlated_tracker.reborrow(),
5645 )?;
5646 p = p.with_projections(projections);
5647 (p, IdentifierContext::new(None))
5648 };
5649
5650 let mut filter_components: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
5651 let mut all_subqueries = Vec::new();
5652
5653 if let Some(expr) = &select.selection {
5654 let materialized_expr = self.materialize_in_subquery(expr.clone())?;
5655 filter_components.push(translate_condition_with_context(
5656 self,
5657 resolver,
5658 id_context.clone(),
5659 &materialized_expr,
5660 outer_scopes,
5661 &mut all_subqueries,
5662 correlated_tracker.reborrow(),
5663 )?);
5664 }
5665
5666 for (idx, join_expr_opt) in join_conditions.iter().enumerate() {
5670 let Some(join_expr) = join_expr_opt else {
5671 continue;
5672 };
5673
5674 let materialized_expr = self.materialize_in_subquery(join_expr.clone())?;
5675 let translated = translate_condition_with_context(
5676 self,
5677 resolver,
5678 id_context.clone(),
5679 &materialized_expr,
5680 outer_scopes,
5681 &mut all_subqueries,
5682 correlated_tracker.reborrow(),
5683 )?;
5684
5685 let is_left_join = plan
5686 .joins
5687 .get(idx)
5688 .map(|j| j.join_type == llkv_plan::JoinPlan::Left)
5689 .unwrap_or(false);
5690
5691 if let Some(join_meta) = plan.joins.get_mut(idx) {
5692 join_meta.on_condition = Some(translated.clone());
5693 }
5694
5695 if !is_left_join {
5696 filter_components.push(translated);
5697 }
5698 }
5699
5700 let having_expr = if let Some(having) = &select.having {
5701 let materialized_expr = self.materialize_in_subquery(having.clone())?;
5702 let translated = translate_condition_with_context(
5703 self,
5704 resolver,
5705 id_context.clone(),
5706 &materialized_expr,
5707 outer_scopes,
5708 &mut all_subqueries,
5709 correlated_tracker.reborrow(),
5710 )?;
5711 Some(translated)
5712 } else {
5713 None
5714 };
5715
5716 subqueries.append(&mut all_subqueries);
5717
5718 let filter = match filter_components.len() {
5719 0 => None,
5720 1 if subqueries.is_empty() => Some(llkv_plan::SelectFilter {
5721 predicate: filter_components.into_iter().next().unwrap(),
5722 subqueries: Vec::new(),
5723 }),
5724 1 => Some(llkv_plan::SelectFilter {
5725 predicate: filter_components.into_iter().next().unwrap(),
5726 subqueries: std::mem::take(subqueries),
5727 }),
5728 _ => Some(llkv_plan::SelectFilter {
5729 predicate: llkv_expr::expr::Expr::And(filter_components),
5730 subqueries: std::mem::take(subqueries),
5731 }),
5732 };
5733 plan = plan.with_filter(filter);
5734 plan = plan.with_having(having_expr);
5735 plan = plan.with_scalar_subqueries(std::mem::take(&mut scalar_subqueries));
5736 plan = plan.with_distinct(distinct);
5737
5738 let group_by_columns = if has_group_by {
5739 self.translate_group_by_columns(resolver, id_context.clone(), &select.group_by)?
5740 } else {
5741 Vec::new()
5742 };
5743 plan = plan.with_group_by(group_by_columns);
5744
5745 let value_mode = select.value_table_mode.map(convert_value_table_mode);
5746 plan = plan.with_value_table_mode(value_mode);
5747 Ok((plan, id_context))
5748 }
5749
5750 fn translate_order_by(
5751 &self,
5752 resolver: &IdentifierResolver<'_>,
5753 id_context: IdentifierContext,
5754 order_by: &OrderBy,
5755 ) -> SqlResult<Vec<OrderByPlan>> {
5756 let exprs = match &order_by.kind {
5757 OrderByKind::Expressions(exprs) => exprs,
5758 _ => {
5759 return Err(Error::InvalidArgumentError(
5760 "unsupported ORDER BY clause".into(),
5761 ));
5762 }
5763 };
5764
5765 let base_nulls_first = self.default_nulls_first.load(AtomicOrdering::Relaxed);
5766
5767 let mut plans = Vec::with_capacity(exprs.len());
5768 for order_expr in exprs {
5769 let ascending = order_expr.options.asc.unwrap_or(true);
5770 let default_nulls_first_for_direction = if ascending {
5771 base_nulls_first
5772 } else {
5773 !base_nulls_first
5774 };
5775 let nulls_first = order_expr
5776 .options
5777 .nulls_first
5778 .unwrap_or(default_nulls_first_for_direction);
5779
5780 if let SqlExpr::Identifier(ident) = &order_expr.expr
5781 && ident.value.eq_ignore_ascii_case("ALL")
5782 && ident.quote_style.is_none()
5783 {
5784 plans.push(OrderByPlan {
5785 target: OrderTarget::All,
5786 sort_type: OrderSortType::Native,
5787 ascending,
5788 nulls_first,
5789 });
5790 continue;
5791 }
5792
5793 let (target, sort_type) = match &order_expr.expr {
5794 SqlExpr::Identifier(_) | SqlExpr::CompoundIdentifier(_) => (
5795 OrderTarget::Column(self.resolve_simple_column_expr(
5796 resolver,
5797 id_context.clone(),
5798 &order_expr.expr,
5799 )?),
5800 OrderSortType::Native,
5801 ),
5802 SqlExpr::Cast {
5803 expr,
5804 data_type:
5805 SqlDataType::Int(_)
5806 | SqlDataType::Integer(_)
5807 | SqlDataType::BigInt(_)
5808 | SqlDataType::SmallInt(_)
5809 | SqlDataType::TinyInt(_),
5810 ..
5811 } => (
5812 OrderTarget::Column(self.resolve_simple_column_expr(
5813 resolver,
5814 id_context.clone(),
5815 expr,
5816 )?),
5817 OrderSortType::CastTextToInteger,
5818 ),
5819 SqlExpr::Cast { data_type, .. } => {
5820 return Err(Error::InvalidArgumentError(format!(
5821 "ORDER BY CAST target type {:?} is not supported",
5822 data_type
5823 )));
5824 }
5825 SqlExpr::Value(value_with_span) => match &value_with_span.value {
5826 Value::Number(raw, _) => {
5827 let position: usize = raw.parse().map_err(|_| {
5828 Error::InvalidArgumentError(format!(
5829 "ORDER BY position '{}' is not a valid positive integer",
5830 raw
5831 ))
5832 })?;
5833 if position == 0 {
5834 return Err(Error::InvalidArgumentError(
5835 "ORDER BY position must be at least 1".into(),
5836 ));
5837 }
5838 (OrderTarget::Index(position - 1), OrderSortType::Native)
5839 }
5840 other => {
5841 return Err(Error::InvalidArgumentError(format!(
5842 "unsupported ORDER BY literal expression: {other:?}"
5843 )));
5844 }
5845 },
5846 other => {
5847 return Err(Error::InvalidArgumentError(format!(
5848 "unsupported ORDER BY expression: {other:?}"
5849 )));
5850 }
5851 };
5852
5853 plans.push(OrderByPlan {
5854 target,
5855 sort_type,
5856 ascending,
5857 nulls_first,
5858 });
5859 }
5860
5861 Ok(plans)
5862 }
5863
5864 fn translate_group_by_columns(
5865 &self,
5866 resolver: &IdentifierResolver<'_>,
5867 id_context: IdentifierContext,
5868 group_by: &GroupByExpr,
5869 ) -> SqlResult<Vec<String>> {
5870 use sqlparser::ast::Expr as SqlExpr;
5871
5872 match group_by {
5873 GroupByExpr::All(_) => Err(Error::InvalidArgumentError(
5874 "GROUP BY ALL is not supported".into(),
5875 )),
5876 GroupByExpr::Expressions(exprs, modifiers) => {
5877 if !modifiers.is_empty() {
5878 return Err(Error::InvalidArgumentError(
5879 "GROUP BY modifiers are not supported".into(),
5880 ));
5881 }
5882 let mut columns = Vec::with_capacity(exprs.len());
5883 for expr in exprs {
5884 let parts: Vec<String> = match expr {
5885 SqlExpr::Identifier(ident) => vec![ident.value.clone()],
5886 SqlExpr::CompoundIdentifier(idents) => {
5887 idents.iter().map(|id| id.value.clone()).collect()
5888 }
5889 _ => {
5890 return Err(Error::InvalidArgumentError(
5891 "GROUP BY expressions must be simple column references".into(),
5892 ));
5893 }
5894 };
5895 let resolution = resolver.resolve(&parts, id_context.clone())?;
5896 if !resolution.is_simple() {
5897 return Err(Error::InvalidArgumentError(
5898 "GROUP BY nested field references are not supported".into(),
5899 ));
5900 }
5901 columns.push(resolution.column().to_string());
5902 }
5903 Ok(columns)
5904 }
5905 }
5906 }
5907
5908 fn resolve_simple_column_expr(
5909 &self,
5910 resolver: &IdentifierResolver<'_>,
5911 context: IdentifierContext,
5912 expr: &SqlExpr,
5913 ) -> SqlResult<String> {
5914 let normalized_expr = self.materialize_in_subquery(expr.clone())?;
5915 let scalar = translate_scalar_with_context(resolver, context, &normalized_expr)?;
5916 match scalar {
5917 llkv_expr::expr::ScalarExpr::Column(column) => Ok(column),
5918 other => Err(Error::InvalidArgumentError(format!(
5919 "ORDER BY expression must reference a simple column, found {other:?}"
5920 ))),
5921 }
5922 }
5923
5924 fn detect_simple_aggregates(
5925 &self,
5926 projection_items: &[SelectItem],
5927 ) -> SqlResult<Option<Vec<AggregateExpr>>> {
5928 if projection_items.is_empty() {
5929 return Ok(None);
5930 }
5931
5932 let mut specs: Vec<AggregateExpr> = Vec::with_capacity(projection_items.len());
5933 for (idx, item) in projection_items.iter().enumerate() {
5934 let (expr, alias_opt) = match item {
5935 SelectItem::UnnamedExpr(expr) => (expr, None),
5936 SelectItem::ExprWithAlias { expr, alias } => (expr, Some(alias.value.clone())),
5937 _ => return Ok(None),
5938 };
5939
5940 let alias = alias_opt.unwrap_or_else(|| format!("col{}", idx + 1));
5941 let SqlExpr::Function(func) = expr else {
5942 return Ok(None);
5943 };
5944
5945 if func.uses_odbc_syntax {
5946 return Err(Error::InvalidArgumentError(
5947 "ODBC function syntax is not supported in aggregate queries".into(),
5948 ));
5949 }
5950 if !matches!(func.parameters, FunctionArguments::None) {
5951 return Err(Error::InvalidArgumentError(
5952 "parameterized aggregate functions are not supported".into(),
5953 ));
5954 }
5955 if func.filter.is_some()
5956 || func.null_treatment.is_some()
5957 || func.over.is_some()
5958 || !func.within_group.is_empty()
5959 {
5960 return Err(Error::InvalidArgumentError(
5961 "advanced aggregate clauses are not supported".into(),
5962 ));
5963 }
5964
5965 let mut is_distinct = false;
5966 let args_slice: &[FunctionArg] = match &func.args {
5967 FunctionArguments::List(list) => {
5968 if let Some(dup) = &list.duplicate_treatment {
5969 use sqlparser::ast::DuplicateTreatment;
5970 match dup {
5971 DuplicateTreatment::All => {}
5972 DuplicateTreatment::Distinct => is_distinct = true,
5973 }
5974 }
5975 if !list.clauses.is_empty() {
5976 return Err(Error::InvalidArgumentError(
5977 "aggregate argument clauses are not supported".into(),
5978 ));
5979 }
5980 &list.args
5981 }
5982 FunctionArguments::None => &[],
5983 FunctionArguments::Subquery(_) => {
5984 return Err(Error::InvalidArgumentError(
5985 "aggregate subquery arguments are not supported".into(),
5986 ));
5987 }
5988 };
5989
5990 let func_name = if func.name.0.len() == 1 {
5991 match &func.name.0[0] {
5992 ObjectNamePart::Identifier(ident) => ident.value.to_ascii_lowercase(),
5993 _ => {
5994 return Err(Error::InvalidArgumentError(
5995 "unsupported aggregate function name".into(),
5996 ));
5997 }
5998 }
5999 } else {
6000 return Err(Error::InvalidArgumentError(
6001 "qualified aggregate function names are not supported".into(),
6002 ));
6003 };
6004
6005 let aggregate = match func_name.as_str() {
6006 "count" => {
6007 if args_slice.len() != 1 {
6008 return Err(Error::InvalidArgumentError(
6009 "COUNT accepts exactly one argument".into(),
6010 ));
6011 }
6012 match &args_slice[0] {
6013 FunctionArg::Unnamed(FunctionArgExpr::Wildcard) => {
6014 if is_distinct {
6015 return Err(Error::InvalidArgumentError(
6016 "DISTINCT aggregates must be applied to columns not *, e.g. table columns like: 1,0,2,2".into(),
6017 ));
6018 }
6019 AggregateExpr::count_star(alias, false)
6020 }
6021 FunctionArg::Unnamed(FunctionArgExpr::Expr(arg_expr)) => {
6022 if !is_simple_aggregate_column(arg_expr) {
6023 return Ok(None);
6024 }
6025 let column = resolve_column_name(arg_expr)?;
6026 AggregateExpr::count_column(column, alias, is_distinct)
6027 }
6028 FunctionArg::Named { .. } | FunctionArg::ExprNamed { .. } => {
6029 return Err(Error::InvalidArgumentError(
6030 "named COUNT arguments are not supported".into(),
6031 ));
6032 }
6033 FunctionArg::Unnamed(FunctionArgExpr::QualifiedWildcard(_)) => {
6034 return Err(Error::InvalidArgumentError(
6035 "COUNT does not support qualified wildcards".into(),
6036 ));
6037 }
6038 }
6039 }
6040 "sum" | "min" | "max" => {
6041 if args_slice.len() != 1 {
6042 return Err(Error::InvalidArgumentError(format!(
6043 "{} accepts exactly one argument",
6044 func_name.to_uppercase()
6045 )));
6046 }
6047 let arg_expr = match &args_slice[0] {
6048 FunctionArg::Unnamed(FunctionArgExpr::Expr(arg_expr)) => arg_expr,
6049 FunctionArg::Unnamed(FunctionArgExpr::Wildcard)
6050 | FunctionArg::Unnamed(FunctionArgExpr::QualifiedWildcard(_)) => {
6051 return Err(Error::InvalidArgumentError(format!(
6052 "{} does not support wildcard arguments",
6053 func_name.to_uppercase()
6054 )));
6055 }
6056 FunctionArg::Named { .. } | FunctionArg::ExprNamed { .. } => {
6057 return Err(Error::InvalidArgumentError(format!(
6058 "{} arguments must be column references",
6059 func_name.to_uppercase()
6060 )));
6061 }
6062 };
6063
6064 if is_distinct {
6065 return Ok(None);
6066 }
6067
6068 if func_name == "sum" {
6069 if let Some(column) = parse_count_nulls_case(arg_expr)? {
6070 AggregateExpr::count_nulls(column, alias)
6071 } else {
6072 if !is_simple_aggregate_column(arg_expr) {
6073 return Ok(None);
6074 }
6075 let column = resolve_column_name(arg_expr)?;
6076 AggregateExpr::sum_int64(column, alias)
6077 }
6078 } else {
6079 if !is_simple_aggregate_column(arg_expr) {
6080 return Ok(None);
6081 }
6082 let column = resolve_column_name(arg_expr)?;
6083 if func_name == "min" {
6084 AggregateExpr::min_int64(column, alias)
6085 } else {
6086 AggregateExpr::max_int64(column, alias)
6087 }
6088 }
6089 }
6090 _ => return Ok(None),
6091 };
6092
6093 specs.push(aggregate);
6094 }
6095
6096 if specs.is_empty() {
6097 return Ok(None);
6098 }
6099 Ok(Some(specs))
6100 }
6101
6102 fn build_projection_list(
6103 &self,
6104 resolver: &IdentifierResolver<'_>,
6105 id_context: IdentifierContext,
6106 projection_items: &[SelectItem],
6107 outer_scopes: &[IdentifierContext],
6108 scalar_subqueries: &mut Vec<llkv_plan::ScalarSubquery>,
6109 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
6110 ) -> SqlResult<Vec<SelectProjection>> {
6111 if projection_items.is_empty() {
6112 return Err(Error::InvalidArgumentError(
6113 "SELECT projection must include at least one column".into(),
6114 ));
6115 }
6116
6117 let mut projections = Vec::with_capacity(projection_items.len());
6118 for (idx, item) in projection_items.iter().enumerate() {
6119 match item {
6120 SelectItem::Wildcard(options) => {
6121 if let Some(exclude) = &options.opt_exclude {
6122 use sqlparser::ast::ExcludeSelectItem;
6123 let exclude_cols = match exclude {
6124 ExcludeSelectItem::Single(ident) => vec![ident.value.clone()],
6125 ExcludeSelectItem::Multiple(idents) => {
6126 idents.iter().map(|id| id.value.clone()).collect()
6127 }
6128 };
6129 projections.push(SelectProjection::AllColumnsExcept {
6130 exclude: exclude_cols,
6131 });
6132 } else {
6133 projections.push(SelectProjection::AllColumns);
6134 }
6135 }
6136 SelectItem::QualifiedWildcard(kind, _) => match kind {
6137 SelectItemQualifiedWildcardKind::ObjectName(name) => {
6138 projections.push(SelectProjection::Column {
6139 name: name.to_string(),
6140 alias: None,
6141 });
6142 }
6143 SelectItemQualifiedWildcardKind::Expr(_) => {
6144 return Err(Error::InvalidArgumentError(
6145 "expression-qualified wildcards are not supported".into(),
6146 ));
6147 }
6148 },
6149 SelectItem::UnnamedExpr(expr) => match expr {
6150 SqlExpr::Identifier(ident) => {
6151 let parts = vec![ident.value.clone()];
6152 let resolution = resolver.resolve(&parts, id_context.clone())?;
6153 if resolution.is_simple() {
6154 projections.push(SelectProjection::Column {
6155 name: resolution.column().to_string(),
6156 alias: None,
6157 });
6158 } else {
6159 let alias = format!("col{}", idx + 1);
6160 projections.push(SelectProjection::Computed {
6161 expr: resolution.into_scalar_expr(),
6162 alias,
6163 });
6164 }
6165 }
6166 SqlExpr::CompoundIdentifier(parts) => {
6167 let name_parts: Vec<String> =
6168 parts.iter().map(|part| part.value.clone()).collect();
6169 let resolution = resolver.resolve(&name_parts, id_context.clone())?;
6170 if resolution.is_simple() {
6171 projections.push(SelectProjection::Column {
6172 name: resolution.column().to_string(),
6173 alias: None,
6174 });
6175 } else {
6176 let alias = format!("col{}", idx + 1);
6177 projections.push(SelectProjection::Computed {
6178 expr: resolution.into_scalar_expr(),
6179 alias,
6180 });
6181 }
6182 }
6183 _ => {
6184 let alias = expr.to_string();
6187 let normalized_expr = if matches!(expr, SqlExpr::Subquery(_)) {
6188 expr.clone()
6189 } else {
6190 self.materialize_in_subquery(expr.clone())?
6191 };
6192 let scalar = {
6193 let tracker_view = correlated_tracker.reborrow();
6194 let mut builder = ScalarSubqueryPlanner {
6195 engine: self,
6196 scalar_subqueries,
6197 };
6198 let mut tracker_wrapper =
6199 SubqueryCorrelatedTracker::from_option(tracker_view);
6200 translate_scalar_internal(
6201 &normalized_expr,
6202 Some(resolver),
6203 Some(&id_context),
6204 outer_scopes,
6205 &mut tracker_wrapper,
6206 Some(&mut builder),
6207 )?
6208 };
6209 projections.push(SelectProjection::Computed {
6210 expr: scalar,
6211 alias,
6212 });
6213 }
6214 },
6215 SelectItem::ExprWithAlias { expr, alias } => match expr {
6216 SqlExpr::Identifier(ident) => {
6217 let parts = vec![ident.value.clone()];
6218 let resolution = resolver.resolve(&parts, id_context.clone())?;
6219 if resolution.is_simple() {
6220 projections.push(SelectProjection::Column {
6221 name: resolution.column().to_string(),
6222 alias: Some(alias.value.clone()),
6223 });
6224 } else {
6225 projections.push(SelectProjection::Computed {
6226 expr: resolution.into_scalar_expr(),
6227 alias: alias.value.clone(),
6228 });
6229 }
6230 }
6231 SqlExpr::CompoundIdentifier(parts) => {
6232 let name_parts: Vec<String> =
6233 parts.iter().map(|part| part.value.clone()).collect();
6234 let resolution = resolver.resolve(&name_parts, id_context.clone())?;
6235 if resolution.is_simple() {
6236 projections.push(SelectProjection::Column {
6237 name: resolution.column().to_string(),
6238 alias: Some(alias.value.clone()),
6239 });
6240 } else {
6241 projections.push(SelectProjection::Computed {
6242 expr: resolution.into_scalar_expr(),
6243 alias: alias.value.clone(),
6244 });
6245 }
6246 }
6247 _ => {
6248 let normalized_expr = if matches!(expr, SqlExpr::Subquery(_)) {
6249 expr.clone()
6250 } else {
6251 self.materialize_in_subquery(expr.clone())?
6252 };
6253 let scalar = {
6254 let tracker_view = correlated_tracker.reborrow();
6255 let mut builder = ScalarSubqueryPlanner {
6256 engine: self,
6257 scalar_subqueries,
6258 };
6259 let mut tracker_wrapper =
6260 SubqueryCorrelatedTracker::from_option(tracker_view);
6261 translate_scalar_internal(
6262 &normalized_expr,
6263 Some(resolver),
6264 Some(&id_context),
6265 outer_scopes,
6266 &mut tracker_wrapper,
6267 Some(&mut builder),
6268 )?
6269 };
6270 projections.push(SelectProjection::Computed {
6271 expr: scalar,
6272 alias: alias.value.clone(),
6273 });
6274 }
6275 },
6276 }
6277 }
6278 Ok(projections)
6279 }
6280
6281 #[allow(clippy::too_many_arguments)] fn handle_start_transaction(
6283 &self,
6284 modes: Vec<TransactionMode>,
6285 begin: bool,
6286 transaction: Option<BeginTransactionKind>,
6287 modifier: Option<TransactionModifier>,
6288 statements: Vec<Statement>,
6289 exception: Option<Vec<ExceptionWhen>>,
6290 has_end_keyword: bool,
6291 ) -> SqlResult<RuntimeStatementResult<P>> {
6292 if !modes.is_empty() {
6293 return Err(Error::InvalidArgumentError(
6294 "transaction modes are not supported".into(),
6295 ));
6296 }
6297 if modifier.is_some() {
6298 return Err(Error::InvalidArgumentError(
6299 "transaction modifiers are not supported".into(),
6300 ));
6301 }
6302 if !statements.is_empty() || exception.is_some() || has_end_keyword {
6303 return Err(Error::InvalidArgumentError(
6304 "BEGIN blocks with inline statements or exceptions are not supported".into(),
6305 ));
6306 }
6307 if let Some(kind) = transaction {
6308 match kind {
6309 BeginTransactionKind::Transaction | BeginTransactionKind::Work => {}
6310 }
6311 }
6312 if !begin {
6313 tracing::warn!("Currently treat `START TRANSACTION` same as `BEGIN`")
6315 }
6316
6317 self.execute_plan_statement(PlanStatement::BeginTransaction)
6318 }
6319
6320 fn handle_commit(
6321 &self,
6322 chain: bool,
6323 end: bool,
6324 modifier: Option<TransactionModifier>,
6325 ) -> SqlResult<RuntimeStatementResult<P>> {
6326 if chain {
6327 return Err(Error::InvalidArgumentError(
6328 "COMMIT AND [NO] CHAIN is not supported".into(),
6329 ));
6330 }
6331 if end {
6332 return Err(Error::InvalidArgumentError(
6333 "END blocks are not supported".into(),
6334 ));
6335 }
6336 if modifier.is_some() {
6337 return Err(Error::InvalidArgumentError(
6338 "transaction modifiers are not supported".into(),
6339 ));
6340 }
6341
6342 self.execute_plan_statement(PlanStatement::CommitTransaction)
6343 }
6344
6345 fn handle_rollback(
6346 &self,
6347 chain: bool,
6348 savepoint: Option<Ident>,
6349 ) -> SqlResult<RuntimeStatementResult<P>> {
6350 if chain {
6351 return Err(Error::InvalidArgumentError(
6352 "ROLLBACK AND [NO] CHAIN is not supported".into(),
6353 ));
6354 }
6355 if savepoint.is_some() {
6356 return Err(Error::InvalidArgumentError(
6357 "ROLLBACK TO SAVEPOINT is not supported".into(),
6358 ));
6359 }
6360
6361 self.execute_plan_statement(PlanStatement::RollbackTransaction)
6362 }
6363
6364 fn handle_set(&self, set_stmt: Set) -> SqlResult<RuntimeStatementResult<P>> {
6365 match set_stmt {
6366 Set::SingleAssignment {
6367 scope,
6368 hivevar,
6369 variable,
6370 values,
6371 } => {
6372 if scope.is_some() || hivevar {
6373 return Err(Error::InvalidArgumentError(
6374 "SET modifiers are not supported".into(),
6375 ));
6376 }
6377
6378 let variable_name_raw = variable.to_string();
6379 let variable_name = variable_name_raw.to_ascii_lowercase();
6380
6381 match variable_name.as_str() {
6382 "default_null_order" => {
6383 if values.len() != 1 {
6384 return Err(Error::InvalidArgumentError(
6385 "SET default_null_order expects exactly one value".into(),
6386 ));
6387 }
6388
6389 let value_expr = &values[0];
6390 let normalized = match value_expr {
6391 SqlExpr::Value(value_with_span) => value_with_span
6392 .value
6393 .clone()
6394 .into_string()
6395 .map(|s| s.to_ascii_lowercase()),
6396 SqlExpr::Identifier(ident) => Some(ident.value.to_ascii_lowercase()),
6397 _ => None,
6398 };
6399
6400 if !matches!(normalized.as_deref(), Some("nulls_first" | "nulls_last")) {
6401 return Err(Error::InvalidArgumentError(format!(
6402 "unsupported value for SET default_null_order: {value_expr:?}"
6403 )));
6404 }
6405
6406 let use_nulls_first = matches!(normalized.as_deref(), Some("nulls_first"));
6407 self.default_nulls_first
6408 .store(use_nulls_first, AtomicOrdering::Relaxed);
6409
6410 Ok(RuntimeStatementResult::NoOp)
6411 }
6412 "immediate_transaction_mode" => {
6413 if values.len() != 1 {
6414 return Err(Error::InvalidArgumentError(
6415 "SET immediate_transaction_mode expects exactly one value".into(),
6416 ));
6417 }
6418 let normalized = values[0].to_string().to_ascii_lowercase();
6419 let enabled = match normalized.as_str() {
6420 "true" | "on" | "1" => true,
6421 "false" | "off" | "0" => false,
6422 _ => {
6423 return Err(Error::InvalidArgumentError(format!(
6424 "unsupported value for SET immediate_transaction_mode: {}",
6425 values[0]
6426 )));
6427 }
6428 };
6429 if !enabled {
6430 tracing::warn!(
6431 "SET immediate_transaction_mode=false has no effect; continuing with auto mode"
6432 );
6433 }
6434 Ok(RuntimeStatementResult::NoOp)
6435 }
6436 _ => Err(Error::InvalidArgumentError(format!(
6437 "unsupported SET variable: {variable_name_raw}"
6438 ))),
6439 }
6440 }
6441 other => Err(Error::InvalidArgumentError(format!(
6442 "unsupported SQL SET statement: {other:?}",
6443 ))),
6444 }
6445 }
6446
6447 fn handle_pragma(
6448 &self,
6449 name: ObjectName,
6450 value: Option<Value>,
6451 is_eq: bool,
6452 ) -> SqlResult<RuntimeStatementResult<P>> {
6453 let (display, canonical) = canonical_object_name(&name)?;
6454 if value.is_some() || is_eq {
6455 return Err(Error::InvalidArgumentError(format!(
6456 "PRAGMA '{display}' does not accept a value"
6457 )));
6458 }
6459
6460 match canonical.as_str() {
6461 "enable_verification" | "disable_verification" => Ok(RuntimeStatementResult::NoOp),
6462 _ => Err(Error::InvalidArgumentError(format!(
6463 "unsupported PRAGMA '{}'",
6464 display
6465 ))),
6466 }
6467 }
6468
6469 fn handle_vacuum(&self, vacuum: VacuumStatement) -> SqlResult<RuntimeStatementResult<P>> {
6470 if vacuum.reindex {
6472 let index_name = vacuum.table_name.ok_or_else(|| {
6473 Error::InvalidArgumentError("REINDEX requires an index name".to_string())
6474 })?;
6475
6476 let (display_name, canonical_name) = canonical_object_name(&index_name)?;
6477
6478 let plan = ReindexPlan::new(display_name.clone()).with_canonical(canonical_name);
6479
6480 let statement = PlanStatement::Reindex(plan);
6481 self.engine.execute_statement(statement).map_err(|err| {
6482 tracing::error!("REINDEX failed for '{}': {}", display_name, err);
6483 err
6484 })
6485 } else {
6486 Err(Error::InvalidArgumentError(
6488 "Only REINDEX is supported; general VACUUM is not implemented".to_string(),
6489 ))
6490 }
6491 }
6492}
6493
6494fn canonical_object_name(name: &ObjectName) -> SqlResult<(String, String)> {
6495 if name.0.is_empty() {
6496 return Err(Error::InvalidArgumentError(
6497 "object name must not be empty".into(),
6498 ));
6499 }
6500 let mut parts: Vec<String> = Vec::with_capacity(name.0.len());
6501 for part in &name.0 {
6502 let ident = match part {
6503 ObjectNamePart::Identifier(ident) => ident,
6504 _ => {
6505 return Err(Error::InvalidArgumentError(
6506 "object names using functions are not supported".into(),
6507 ));
6508 }
6509 };
6510 parts.push(ident.value.clone());
6511 }
6512 let display = parts.join(".");
6513 let canonical = display.to_ascii_lowercase();
6514 Ok((display, canonical))
6515}
6516
6517fn parse_schema_qualified_name(name: &ObjectName) -> SqlResult<(Option<String>, String)> {
6526 if name.0.is_empty() {
6527 return Err(Error::InvalidArgumentError(
6528 "object name must not be empty".into(),
6529 ));
6530 }
6531
6532 let mut parts: Vec<String> = Vec::with_capacity(name.0.len());
6533 for part in &name.0 {
6534 let ident = match part {
6535 ObjectNamePart::Identifier(ident) => ident,
6536 _ => {
6537 return Err(Error::InvalidArgumentError(
6538 "object names using functions are not supported".into(),
6539 ));
6540 }
6541 };
6542 parts.push(ident.value.clone());
6543 }
6544
6545 match parts.len() {
6546 1 => Ok((None, parts[0].clone())),
6547 2 => Ok((Some(parts[0].clone()), parts[1].clone())),
6548 _ => Err(Error::InvalidArgumentError(format!(
6549 "table name has too many parts: {}",
6550 name
6551 ))),
6552 }
6553}
6554
6555fn extract_index_column_name(
6569 index_col: &sqlparser::ast::IndexColumn,
6570 context: &str,
6571 allow_sort_options: bool,
6572 allow_compound: bool,
6573) -> SqlResult<String> {
6574 use sqlparser::ast::Expr as SqlExpr;
6575
6576 if index_col.operator_class.is_some() {
6578 return Err(Error::InvalidArgumentError(format!(
6579 "{} operator classes are not supported",
6580 context
6581 )));
6582 }
6583
6584 let order_expr = &index_col.column;
6585
6586 if allow_sort_options {
6588 let _ascending = order_expr.options.asc.unwrap_or(true);
6590 let _nulls_first = order_expr.options.nulls_first.unwrap_or(false);
6591 } else {
6593 if order_expr.options.asc.is_some()
6595 || order_expr.options.nulls_first.is_some()
6596 || order_expr.with_fill.is_some()
6597 {
6598 return Err(Error::InvalidArgumentError(format!(
6599 "{} columns must be simple identifiers",
6600 context
6601 )));
6602 }
6603 }
6604
6605 let column_name = match &order_expr.expr {
6607 SqlExpr::Identifier(ident) => ident.value.clone(),
6608 SqlExpr::CompoundIdentifier(parts) => {
6609 if allow_compound {
6610 parts
6612 .last()
6613 .map(|ident| ident.value.clone())
6614 .ok_or_else(|| {
6615 Error::InvalidArgumentError(format!(
6616 "invalid column reference in {}",
6617 context
6618 ))
6619 })?
6620 } else if parts.len() == 1 {
6621 parts[0].value.clone()
6623 } else {
6624 return Err(Error::InvalidArgumentError(format!(
6625 "{} columns must be column identifiers",
6626 context
6627 )));
6628 }
6629 }
6630 other => {
6631 return Err(Error::InvalidArgumentError(format!(
6632 "{} only supports column references, found {:?}",
6633 context, other
6634 )));
6635 }
6636 };
6637
6638 Ok(column_name)
6639}
6640
6641fn validate_create_table_common(stmt: &sqlparser::ast::CreateTable) -> SqlResult<()> {
6642 if stmt.clone.is_some() || stmt.like.is_some() {
6643 return Err(Error::InvalidArgumentError(
6644 "CREATE TABLE LIKE/CLONE is not supported".into(),
6645 ));
6646 }
6647 if stmt.or_replace && stmt.if_not_exists {
6648 return Err(Error::InvalidArgumentError(
6649 "CREATE TABLE cannot combine OR REPLACE with IF NOT EXISTS".into(),
6650 ));
6651 }
6652 use sqlparser::ast::TableConstraint;
6653
6654 let mut seen_primary_key = false;
6655 for constraint in &stmt.constraints {
6656 match constraint {
6657 TableConstraint::PrimaryKey { .. } => {
6658 if seen_primary_key {
6659 return Err(Error::InvalidArgumentError(
6660 "multiple PRIMARY KEY constraints are not supported".into(),
6661 ));
6662 }
6663 seen_primary_key = true;
6664 }
6665 TableConstraint::Unique { .. } => {
6666 }
6668 TableConstraint::ForeignKey { .. } => {
6669 }
6671 other => {
6672 return Err(Error::InvalidArgumentError(format!(
6673 "table-level constraint {:?} is not supported",
6674 other
6675 )));
6676 }
6677 }
6678 }
6679 Ok(())
6680}
6681
6682fn validate_check_constraint(
6683 check_expr: &sqlparser::ast::Expr,
6684 table_name: &str,
6685 column_names: &[&str],
6686) -> SqlResult<()> {
6687 use sqlparser::ast::Expr as SqlExpr;
6688
6689 let column_names_lower: FxHashSet<String> = column_names
6690 .iter()
6691 .map(|name| name.to_ascii_lowercase())
6692 .collect();
6693
6694 let mut stack: Vec<&SqlExpr> = vec![check_expr];
6695
6696 while let Some(expr) = stack.pop() {
6697 match expr {
6698 SqlExpr::Subquery(_) => {
6699 return Err(Error::InvalidArgumentError(
6700 "Subqueries are not allowed in CHECK constraints".into(),
6701 ));
6702 }
6703 SqlExpr::Function(func) => {
6704 let func_name = func.name.to_string().to_uppercase();
6705 if matches!(func_name.as_str(), "SUM" | "AVG" | "COUNT" | "MIN" | "MAX") {
6706 return Err(Error::InvalidArgumentError(
6707 "Aggregate functions are not allowed in CHECK constraints".into(),
6708 ));
6709 }
6710
6711 if let sqlparser::ast::FunctionArguments::List(list) = &func.args {
6712 for arg in &list.args {
6713 if let sqlparser::ast::FunctionArg::Unnamed(
6714 sqlparser::ast::FunctionArgExpr::Expr(expr),
6715 ) = arg
6716 {
6717 stack.push(expr);
6718 }
6719 }
6720 }
6721 }
6722 SqlExpr::Identifier(ident) => {
6723 if !column_names_lower.contains(&ident.value.to_ascii_lowercase()) {
6724 return Err(Error::InvalidArgumentError(format!(
6725 "Column '{}' referenced in CHECK constraint does not exist",
6726 ident.value
6727 )));
6728 }
6729 }
6730 SqlExpr::CompoundIdentifier(idents) => {
6731 if idents.len() == 2 {
6732 let first = idents[0].value.as_str();
6733 let second = &idents[1].value;
6734
6735 if column_names_lower.contains(&first.to_ascii_lowercase()) {
6736 continue;
6737 }
6738
6739 if !first.eq_ignore_ascii_case(table_name) {
6740 return Err(Error::InvalidArgumentError(format!(
6741 "CHECK constraint references column from different table '{}'",
6742 first
6743 )));
6744 }
6745
6746 if !column_names_lower.contains(&second.to_ascii_lowercase()) {
6747 return Err(Error::InvalidArgumentError(format!(
6748 "Column '{}' referenced in CHECK constraint does not exist",
6749 second
6750 )));
6751 }
6752 } else if idents.len() == 3 {
6753 let first = &idents[0].value;
6754 let second = &idents[1].value;
6755 let third = &idents[2].value;
6756
6757 if first.eq_ignore_ascii_case(table_name) {
6758 if !column_names_lower.contains(&second.to_ascii_lowercase()) {
6759 return Err(Error::InvalidArgumentError(format!(
6760 "Column '{}' referenced in CHECK constraint does not exist",
6761 second
6762 )));
6763 }
6764 } else if second.eq_ignore_ascii_case(table_name) {
6765 if !column_names_lower.contains(&third.to_ascii_lowercase()) {
6766 return Err(Error::InvalidArgumentError(format!(
6767 "Column '{}' referenced in CHECK constraint does not exist",
6768 third
6769 )));
6770 }
6771 } else {
6772 return Err(Error::InvalidArgumentError(format!(
6773 "CHECK constraint references column from different table '{}'",
6774 second
6775 )));
6776 }
6777 }
6778 }
6779 SqlExpr::BinaryOp { left, right, .. } => {
6780 stack.push(left);
6781 stack.push(right);
6782 }
6783 SqlExpr::UnaryOp { expr, .. } | SqlExpr::Nested(expr) => {
6784 stack.push(expr);
6785 }
6786 SqlExpr::Value(_) | SqlExpr::TypedString { .. } => {}
6787 _ => {}
6788 }
6789 }
6790
6791 Ok(())
6792}
6793
6794fn validate_create_table_definition(stmt: &sqlparser::ast::CreateTable) -> SqlResult<()> {
6795 for column in &stmt.columns {
6796 for ColumnOptionDef { option, .. } in &column.options {
6797 match option {
6798 ColumnOption::Null
6799 | ColumnOption::NotNull
6800 | ColumnOption::Unique { .. }
6801 | ColumnOption::Check(_)
6802 | ColumnOption::ForeignKey { .. } => {}
6803 ColumnOption::Default(_) => {
6804 return Err(Error::InvalidArgumentError(format!(
6805 "DEFAULT values are not supported for column '{}'",
6806 column.name
6807 )));
6808 }
6809 other => {
6810 return Err(Error::InvalidArgumentError(format!(
6811 "unsupported column option {:?} on '{}'",
6812 other, column.name
6813 )));
6814 }
6815 }
6816 }
6817 }
6818 Ok(())
6819}
6820
6821fn validate_create_table_as(stmt: &sqlparser::ast::CreateTable) -> SqlResult<()> {
6822 if !stmt.columns.is_empty() {
6823 return Err(Error::InvalidArgumentError(
6824 "CREATE TABLE AS SELECT does not support column definitions yet".into(),
6825 ));
6826 }
6827 Ok(())
6828}
6829
6830fn validate_simple_query(query: &Query) -> SqlResult<()> {
6831 if query.with.is_some() {
6832 return Err(Error::InvalidArgumentError(
6833 "WITH clauses are not supported".into(),
6834 ));
6835 }
6836 if let Some(limit_clause) = &query.limit_clause {
6837 match limit_clause {
6838 LimitClause::LimitOffset {
6839 offset: Some(_), ..
6840 }
6841 | LimitClause::OffsetCommaLimit { .. } => {
6842 return Err(Error::InvalidArgumentError(
6843 "OFFSET clauses are not supported".into(),
6844 ));
6845 }
6846 LimitClause::LimitOffset { limit_by, .. } if !limit_by.is_empty() => {
6847 return Err(Error::InvalidArgumentError(
6848 "LIMIT BY clauses are not supported".into(),
6849 ));
6850 }
6851 _ => {}
6852 }
6853 }
6854 if query.fetch.is_some() {
6855 return Err(Error::InvalidArgumentError(
6856 "FETCH clauses are not supported".into(),
6857 ));
6858 }
6859 Ok(())
6860}
6861
6862fn resolve_column_name(expr: &SqlExpr) -> SqlResult<String> {
6863 match expr {
6864 SqlExpr::Identifier(ident) => Ok(ident.value.clone()),
6865 SqlExpr::CompoundIdentifier(parts) => {
6866 if let Some(last) = parts.last() {
6867 Ok(last.value.clone())
6868 } else {
6869 Err(Error::InvalidArgumentError(
6870 "empty column identifier".into(),
6871 ))
6872 }
6873 }
6874 SqlExpr::Nested(inner) => resolve_column_name(inner),
6875 SqlExpr::UnaryOp {
6877 op: UnaryOperator::Plus | UnaryOperator::Minus,
6878 expr,
6879 } => resolve_column_name(expr),
6880 _ => Err(Error::InvalidArgumentError(
6881 "aggregate arguments must be plain column identifiers".into(),
6882 )),
6883 }
6884}
6885
6886fn is_simple_aggregate_column(expr: &SqlExpr) -> bool {
6887 match expr {
6888 SqlExpr::Identifier(_) | SqlExpr::CompoundIdentifier(_) => true,
6889 SqlExpr::Nested(inner) => is_simple_aggregate_column(inner),
6890 SqlExpr::UnaryOp {
6891 op: UnaryOperator::Plus,
6892 expr,
6893 } => is_simple_aggregate_column(expr),
6894 _ => false,
6895 }
6896}
6897
6898fn validate_projection_alias_qualifiers(
6899 projection_items: &[SelectItem],
6900 alias: &str,
6901) -> SqlResult<()> {
6902 let alias_lower = alias.to_ascii_lowercase();
6903 for item in projection_items {
6904 match item {
6905 SelectItem::UnnamedExpr(expr) | SelectItem::ExprWithAlias { expr, .. } => {
6906 if let SqlExpr::CompoundIdentifier(parts) = expr
6907 && parts.len() >= 2
6908 && let Some(first) = parts.first()
6909 && !first.value.eq_ignore_ascii_case(&alias_lower)
6910 {
6911 return Err(Error::InvalidArgumentError(format!(
6912 "Binder Error: table '{}' not found",
6913 first.value
6914 )));
6915 }
6916 }
6917 _ => {}
6918 }
6919 }
6920 Ok(())
6921}
6922
6923#[allow(dead_code)] fn expr_contains_aggregate(expr: &llkv_expr::expr::ScalarExpr<String>) -> bool {
6927 match expr {
6928 llkv_expr::expr::ScalarExpr::Aggregate(_) => true,
6929 llkv_expr::expr::ScalarExpr::Binary { left, right, .. } => {
6930 expr_contains_aggregate(left) || expr_contains_aggregate(right)
6931 }
6932 llkv_expr::expr::ScalarExpr::Compare { left, right, .. } => {
6933 expr_contains_aggregate(left) || expr_contains_aggregate(right)
6934 }
6935 llkv_expr::expr::ScalarExpr::Not(inner) => expr_contains_aggregate(inner),
6936 llkv_expr::expr::ScalarExpr::IsNull { expr, .. } => expr_contains_aggregate(expr),
6937 llkv_expr::expr::ScalarExpr::GetField { base, .. } => expr_contains_aggregate(base),
6938 llkv_expr::expr::ScalarExpr::Cast { expr, .. } => expr_contains_aggregate(expr),
6939 llkv_expr::expr::ScalarExpr::Case {
6940 operand,
6941 branches,
6942 else_expr,
6943 } => {
6944 operand
6945 .as_deref()
6946 .map(expr_contains_aggregate)
6947 .unwrap_or(false)
6948 || branches.iter().any(|(when_expr, then_expr)| {
6949 expr_contains_aggregate(when_expr) || expr_contains_aggregate(then_expr)
6950 })
6951 || else_expr
6952 .as_deref()
6953 .map(expr_contains_aggregate)
6954 .unwrap_or(false)
6955 }
6956 llkv_expr::expr::ScalarExpr::Coalesce(items) => items.iter().any(expr_contains_aggregate),
6957 llkv_expr::expr::ScalarExpr::Column(_) | llkv_expr::expr::ScalarExpr::Literal(_) => false,
6958 llkv_expr::expr::ScalarExpr::ScalarSubquery(_) => false,
6959 }
6960}
6961
6962fn try_parse_aggregate_function(
6963 func: &sqlparser::ast::Function,
6964 resolver: Option<&IdentifierResolver<'_>>,
6965 context: Option<&IdentifierContext>,
6966 outer_scopes: &[IdentifierContext],
6967 tracker: &mut SubqueryCorrelatedTracker<'_>,
6968) -> SqlResult<Option<llkv_expr::expr::AggregateCall<String>>> {
6969 use sqlparser::ast::{
6970 DuplicateTreatment, FunctionArg, FunctionArgExpr, FunctionArguments, ObjectNamePart,
6971 };
6972
6973 if func.uses_odbc_syntax {
6974 return Ok(None);
6975 }
6976 if !matches!(func.parameters, FunctionArguments::None) {
6977 return Ok(None);
6978 }
6979 if func.filter.is_some()
6980 || func.null_treatment.is_some()
6981 || func.over.is_some()
6982 || !func.within_group.is_empty()
6983 {
6984 return Ok(None);
6985 }
6986
6987 let func_name = if func.name.0.len() == 1 {
6988 match &func.name.0[0] {
6989 ObjectNamePart::Identifier(ident) => ident.value.to_ascii_lowercase(),
6990 _ => return Ok(None),
6991 }
6992 } else {
6993 return Ok(None);
6994 };
6995
6996 let distinct = match &func.args {
6998 FunctionArguments::List(list) => {
6999 if !list.clauses.is_empty() {
7000 return Ok(None);
7001 }
7002 matches!(list.duplicate_treatment, Some(DuplicateTreatment::Distinct))
7003 }
7004 _ => false,
7005 };
7006
7007 let args_slice: &[FunctionArg] = match &func.args {
7008 FunctionArguments::List(list) => &list.args,
7009 FunctionArguments::None => &[],
7010 FunctionArguments::Subquery(_) => return Ok(None),
7011 };
7012
7013 let agg_call = match func_name.as_str() {
7014 "count" => {
7015 if args_slice.len() != 1 {
7016 return Err(Error::InvalidArgumentError(
7017 "COUNT accepts exactly one argument".into(),
7018 ));
7019 }
7020 match &args_slice[0] {
7021 FunctionArg::Unnamed(FunctionArgExpr::Wildcard) => {
7022 if distinct {
7023 return Err(Error::InvalidArgumentError(
7024 "COUNT(DISTINCT *) is not supported".into(),
7025 ));
7026 }
7027 llkv_expr::expr::AggregateCall::CountStar
7028 }
7029 FunctionArg::Unnamed(FunctionArgExpr::Expr(arg_expr)) => {
7030 let expr = translate_scalar_internal(
7031 arg_expr,
7032 resolver,
7033 context,
7034 outer_scopes,
7035 tracker,
7036 None,
7037 )?;
7038 llkv_expr::expr::AggregateCall::Count {
7039 expr: Box::new(expr),
7040 distinct,
7041 }
7042 }
7043 _ => {
7044 return Err(Error::InvalidArgumentError(
7045 "unsupported COUNT argument".into(),
7046 ));
7047 }
7048 }
7049 }
7050 "sum" => {
7051 if args_slice.len() != 1 {
7052 return Err(Error::InvalidArgumentError(
7053 "SUM accepts exactly one argument".into(),
7054 ));
7055 }
7056 let arg_expr = match &args_slice[0] {
7057 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7058 _ => {
7059 return Err(Error::InvalidArgumentError(
7060 "SUM requires a column argument".into(),
7061 ));
7062 }
7063 };
7064
7065 if let Some(column) = parse_count_nulls_case(arg_expr)? {
7067 if distinct {
7068 return Err(Error::InvalidArgumentError(
7069 "DISTINCT not supported for COUNT(CASE ...) pattern".into(),
7070 ));
7071 }
7072 llkv_expr::expr::AggregateCall::CountNulls(Box::new(
7073 llkv_expr::expr::ScalarExpr::column(column),
7074 ))
7075 } else {
7076 let expr = translate_scalar_internal(
7077 arg_expr,
7078 resolver,
7079 context,
7080 outer_scopes,
7081 tracker,
7082 None,
7083 )?;
7084 llkv_expr::expr::AggregateCall::Sum {
7085 expr: Box::new(expr),
7086 distinct,
7087 }
7088 }
7089 }
7090 "total" => {
7091 if args_slice.len() != 1 {
7092 return Err(Error::InvalidArgumentError(
7093 "TOTAL accepts exactly one argument".into(),
7094 ));
7095 }
7096 let arg_expr = match &args_slice[0] {
7097 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7098 _ => {
7099 return Err(Error::InvalidArgumentError(
7100 "TOTAL requires a column argument".into(),
7101 ));
7102 }
7103 };
7104
7105 let expr = translate_scalar_internal(
7106 arg_expr,
7107 resolver,
7108 context,
7109 outer_scopes,
7110 tracker,
7111 None,
7112 )?;
7113 llkv_expr::expr::AggregateCall::Total {
7114 expr: Box::new(expr),
7115 distinct,
7116 }
7117 }
7118 "min" => {
7119 if args_slice.len() != 1 {
7120 return Err(Error::InvalidArgumentError(
7121 "MIN accepts exactly one argument".into(),
7122 ));
7123 }
7124 let arg_expr = match &args_slice[0] {
7125 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7126 _ => {
7127 return Err(Error::InvalidArgumentError(
7128 "MIN requires a column argument".into(),
7129 ));
7130 }
7131 };
7132 let expr = translate_scalar_internal(
7133 arg_expr,
7134 resolver,
7135 context,
7136 outer_scopes,
7137 tracker,
7138 None,
7139 )?;
7140 llkv_expr::expr::AggregateCall::Min(Box::new(expr))
7141 }
7142 "max" => {
7143 if args_slice.len() != 1 {
7144 return Err(Error::InvalidArgumentError(
7145 "MAX accepts exactly one argument".into(),
7146 ));
7147 }
7148 let arg_expr = match &args_slice[0] {
7149 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7150 _ => {
7151 return Err(Error::InvalidArgumentError(
7152 "MAX requires a column argument".into(),
7153 ));
7154 }
7155 };
7156 let expr = translate_scalar_internal(
7157 arg_expr,
7158 resolver,
7159 context,
7160 outer_scopes,
7161 tracker,
7162 None,
7163 )?;
7164 llkv_expr::expr::AggregateCall::Max(Box::new(expr))
7165 }
7166 "avg" => {
7167 if args_slice.len() != 1 {
7168 return Err(Error::InvalidArgumentError(
7169 "AVG accepts exactly one argument".into(),
7170 ));
7171 }
7172 let arg_expr = match &args_slice[0] {
7173 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7174 _ => {
7175 return Err(Error::InvalidArgumentError(
7176 "AVG requires a column argument".into(),
7177 ));
7178 }
7179 };
7180 let expr = translate_scalar_internal(
7181 arg_expr,
7182 resolver,
7183 context,
7184 outer_scopes,
7185 tracker,
7186 None,
7187 )?;
7188 llkv_expr::expr::AggregateCall::Avg {
7189 expr: Box::new(expr),
7190 distinct,
7191 }
7192 }
7193 "group_concat" => {
7194 if args_slice.is_empty() || args_slice.len() > 2 {
7195 return Err(Error::InvalidArgumentError(
7196 "GROUP_CONCAT accepts one or two arguments".into(),
7197 ));
7198 }
7199
7200 let arg_expr = match &args_slice[0] {
7202 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
7203 _ => {
7204 return Err(Error::InvalidArgumentError(
7205 "GROUP_CONCAT requires a column argument".into(),
7206 ));
7207 }
7208 };
7209
7210 let expr = translate_scalar_internal(
7211 arg_expr,
7212 resolver,
7213 context,
7214 outer_scopes,
7215 tracker,
7216 None,
7217 )?;
7218
7219 let separator = if args_slice.len() == 2 {
7221 match &args_slice[1] {
7222 FunctionArg::Unnamed(FunctionArgExpr::Expr(SqlExpr::Value(
7223 ValueWithSpan {
7224 value: sqlparser::ast::Value::SingleQuotedString(s),
7225 ..
7226 },
7227 ))) => Some(s.clone()),
7228 _ => {
7229 return Err(Error::InvalidArgumentError(
7230 "GROUP_CONCAT separator must be a string literal".into(),
7231 ));
7232 }
7233 }
7234 } else {
7235 None
7236 };
7237
7238 if distinct && separator.is_some() {
7240 return Err(Error::InvalidArgumentError(
7241 "GROUP_CONCAT does not support DISTINCT with a custom separator".into(),
7242 ));
7243 }
7244
7245 llkv_expr::expr::AggregateCall::GroupConcat {
7246 expr: Box::new(expr),
7247 distinct,
7248 separator,
7249 }
7250 }
7251 _ => return Ok(None),
7252 };
7253
7254 Ok(Some(agg_call))
7255}
7256
7257fn parse_count_nulls_case(expr: &SqlExpr) -> SqlResult<Option<String>> {
7258 let SqlExpr::Case {
7259 operand,
7260 conditions,
7261 else_result,
7262 ..
7263 } = expr
7264 else {
7265 return Ok(None);
7266 };
7267
7268 if operand.is_some() || conditions.len() != 1 {
7269 return Ok(None);
7270 }
7271
7272 let case_when = &conditions[0];
7273 if !is_integer_literal(&case_when.result, 1) {
7274 return Ok(None);
7275 }
7276
7277 let else_expr = match else_result {
7278 Some(expr) => expr.as_ref(),
7279 None => return Ok(None),
7280 };
7281 if !is_integer_literal(else_expr, 0) {
7282 return Ok(None);
7283 }
7284
7285 let inner = match &case_when.condition {
7286 SqlExpr::IsNull(inner) => inner.as_ref(),
7287 _ => return Ok(None),
7288 };
7289
7290 resolve_column_name(inner).map(Some)
7291}
7292
7293fn is_integer_literal(expr: &SqlExpr, expected: i64) -> bool {
7294 match expr {
7295 SqlExpr::Value(ValueWithSpan {
7296 value: Value::Number(text, _),
7297 ..
7298 }) => text.parse::<i64>() == Ok(expected),
7299 _ => false,
7300 }
7301}
7302
7303fn strip_sql_expr_nesting(expr: &SqlExpr) -> &SqlExpr {
7304 match expr {
7305 SqlExpr::Nested(inner) => strip_sql_expr_nesting(inner),
7306 other => other,
7307 }
7308}
7309
7310struct BetweenBounds<'a> {
7311 lower: &'a SqlExpr,
7312 upper: &'a SqlExpr,
7313}
7314
7315fn translate_between_expr(
7316 resolver: &IdentifierResolver<'_>,
7317 context: IdentifierContext,
7318 between_expr: &SqlExpr,
7319 bounds: BetweenBounds<'_>,
7320 negated: bool,
7321 outer_scopes: &[IdentifierContext],
7322 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
7323) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
7324 let lower_op = if negated {
7325 BinaryOperator::Lt
7326 } else {
7327 BinaryOperator::GtEq
7328 };
7329 let upper_op = if negated {
7330 BinaryOperator::Gt
7331 } else {
7332 BinaryOperator::LtEq
7333 };
7334
7335 let lower_bound = translate_comparison_with_context(
7336 resolver,
7337 context.clone(),
7338 between_expr,
7339 lower_op,
7340 bounds.lower,
7341 outer_scopes,
7342 correlated_tracker.reborrow(),
7343 )?;
7344 let upper_bound = translate_comparison_with_context(
7345 resolver,
7346 context,
7347 between_expr,
7348 upper_op,
7349 bounds.upper,
7350 outer_scopes,
7351 correlated_tracker,
7352 )?;
7353
7354 if negated {
7355 Ok(llkv_expr::expr::Expr::Or(vec![lower_bound, upper_bound]))
7356 } else {
7357 Ok(llkv_expr::expr::Expr::And(vec![lower_bound, upper_bound]))
7358 }
7359}
7360
7361fn correlated_scalar_from_resolution(
7362 placeholder: String,
7363 resolution: &ColumnResolution,
7364) -> llkv_expr::expr::ScalarExpr<String> {
7365 let mut expr = llkv_expr::expr::ScalarExpr::column(placeholder);
7366 for field in resolution.field_path() {
7367 expr = llkv_expr::expr::ScalarExpr::get_field(expr, field.clone());
7368 }
7369 expr
7370}
7371
7372fn resolve_correlated_identifier(
7373 resolver: &IdentifierResolver<'_>,
7374 parts: &[String],
7375 outer_scopes: &[IdentifierContext],
7376 mut tracker: SubqueryCorrelatedTracker<'_>,
7377) -> SqlResult<Option<llkv_expr::expr::ScalarExpr<String>>> {
7378 if !tracker.is_active() {
7379 return Ok(None);
7380 }
7381
7382 for scope in outer_scopes.iter().rev() {
7383 match resolver.resolve(parts, scope.clone()) {
7384 Ok(resolution) => {
7385 if let Some(placeholder) = tracker.placeholder_for_resolution(&resolution) {
7386 let expr = correlated_scalar_from_resolution(placeholder, &resolution);
7387 return Ok(Some(expr));
7388 }
7389 }
7390 Err(_) => continue,
7391 }
7392 }
7393
7394 Ok(None)
7395}
7396
7397fn resolve_identifier_expr(
7398 resolver: &IdentifierResolver<'_>,
7399 context: &IdentifierContext,
7400 parts: Vec<String>,
7401 outer_scopes: &[IdentifierContext],
7402 tracker: SubqueryCorrelatedTracker<'_>,
7403) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
7404 match resolver.resolve(&parts, context.clone()) {
7405 Ok(resolution) => Ok(resolution.into_scalar_expr()),
7406 Err(err) => {
7407 if let Some(expr) =
7408 resolve_correlated_identifier(resolver, &parts, outer_scopes, tracker)?
7409 {
7410 Ok(expr)
7411 } else {
7412 Err(err)
7413 }
7414 }
7415 }
7416}
7417
7418fn translate_condition_with_context(
7419 engine: &SqlEngine,
7420 resolver: &IdentifierResolver<'_>,
7421 context: IdentifierContext,
7422 expr: &SqlExpr,
7423 outer_scopes: &[IdentifierContext],
7424 subqueries: &mut Vec<llkv_plan::FilterSubquery>,
7425 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
7426) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
7427 enum ConditionExitContext {
7434 And,
7435 Or,
7436 Not,
7437 Nested,
7438 }
7439
7440 type ConditionFrame<'a> = llkv_plan::TransformFrame<
7441 'a,
7442 SqlExpr,
7443 llkv_expr::expr::Expr<'static, String>,
7444 ConditionExitContext,
7445 >;
7446
7447 let mut work_stack: Vec<ConditionFrame> = vec![ConditionFrame::Enter(expr)];
7448 let mut result_stack: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
7449
7450 while let Some(frame) = work_stack.pop() {
7451 match frame {
7452 ConditionFrame::Enter(node) => match node {
7453 SqlExpr::BinaryOp { left, op, right } => match op {
7454 BinaryOperator::And => {
7455 work_stack.push(ConditionFrame::Exit(ConditionExitContext::And));
7456 work_stack.push(ConditionFrame::Enter(right));
7457 work_stack.push(ConditionFrame::Enter(left));
7458 }
7459 BinaryOperator::Or => {
7460 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Or));
7461 work_stack.push(ConditionFrame::Enter(right));
7462 work_stack.push(ConditionFrame::Enter(left));
7463 }
7464 BinaryOperator::Eq
7465 | BinaryOperator::NotEq
7466 | BinaryOperator::Lt
7467 | BinaryOperator::LtEq
7468 | BinaryOperator::Gt
7469 | BinaryOperator::GtEq => {
7470 let result = translate_comparison_with_context(
7471 resolver,
7472 context.clone(),
7473 left,
7474 op.clone(),
7475 right,
7476 outer_scopes,
7477 correlated_tracker.reborrow(),
7478 )?;
7479 work_stack.push(ConditionFrame::Leaf(result));
7480 }
7481 other => {
7482 return Err(Error::InvalidArgumentError(format!(
7483 "unsupported binary operator in WHERE clause: {other:?}"
7484 )));
7485 }
7486 },
7487 SqlExpr::UnaryOp {
7488 op: UnaryOperator::Not,
7489 expr: inner,
7490 } => {
7491 let inner_stripped = strip_sql_expr_nesting(inner);
7492 if let SqlExpr::Between {
7493 expr: between_expr,
7494 negated: inner_negated,
7495 low,
7496 high,
7497 } = inner_stripped
7498 {
7499 let negated_mode = !*inner_negated;
7500 let between_expr_result = translate_between_expr(
7501 resolver,
7502 context.clone(),
7503 between_expr,
7504 BetweenBounds {
7505 lower: low,
7506 upper: high,
7507 },
7508 negated_mode,
7509 outer_scopes,
7510 correlated_tracker.reborrow(),
7511 )?;
7512 work_stack.push(ConditionFrame::Leaf(between_expr_result));
7513 continue;
7514 }
7515 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Not));
7519 work_stack.push(ConditionFrame::Enter(inner));
7520 }
7521 SqlExpr::Nested(inner) => {
7522 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Nested));
7523 work_stack.push(ConditionFrame::Enter(inner));
7524 }
7525 SqlExpr::IsNull(inner) => {
7526 let scalar = translate_scalar_with_context_scoped(
7527 resolver,
7528 context.clone(),
7529 inner,
7530 outer_scopes,
7531 correlated_tracker.reborrow(),
7532 )?;
7533 match scalar {
7534 llkv_expr::expr::ScalarExpr::Column(column) => {
7535 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Pred(
7537 llkv_expr::expr::Filter {
7538 field_id: column,
7539 op: llkv_expr::expr::Operator::IsNull,
7540 },
7541 )));
7542 }
7543 other => {
7548 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::IsNull {
7550 expr: other,
7551 negated: false,
7552 }));
7553 }
7554 }
7555 }
7556 SqlExpr::IsNotNull(inner) => {
7557 let scalar = translate_scalar_with_context_scoped(
7558 resolver,
7559 context.clone(),
7560 inner,
7561 outer_scopes,
7562 correlated_tracker.reborrow(),
7563 )?;
7564 match scalar {
7565 llkv_expr::expr::ScalarExpr::Column(column) => {
7566 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Pred(
7568 llkv_expr::expr::Filter {
7569 field_id: column,
7570 op: llkv_expr::expr::Operator::IsNotNull,
7571 },
7572 )));
7573 }
7574 other => {
7579 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::IsNull {
7581 expr: other,
7582 negated: true,
7583 }));
7584 }
7585 }
7586 }
7587 SqlExpr::InList {
7588 expr: in_expr,
7589 list,
7590 negated,
7591 } => {
7592 if list.is_empty() {
7593 let result = if *negated {
7594 llkv_expr::expr::Expr::Literal(true)
7595 } else {
7596 llkv_expr::expr::Expr::Literal(false)
7597 };
7598 work_stack.push(ConditionFrame::Leaf(result));
7599 } else {
7600 let target = translate_scalar_with_context_scoped(
7601 resolver,
7602 context.clone(),
7603 in_expr,
7604 outer_scopes,
7605 correlated_tracker.reborrow(),
7606 )?;
7607 let mut values = Vec::with_capacity(list.len());
7608 for value_expr in list {
7609 let scalar = translate_scalar_with_context_scoped(
7610 resolver,
7611 context.clone(),
7612 value_expr,
7613 outer_scopes,
7614 correlated_tracker.reborrow(),
7615 )?;
7616 values.push(scalar);
7617 }
7618
7619 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::InList {
7620 expr: target,
7621 list: values,
7622 negated: *negated,
7623 }));
7624 }
7625 }
7626 SqlExpr::InSubquery { .. } => {
7627 return Err(Error::InvalidArgumentError(
7628 "IN (SELECT ...) subqueries must be materialized before translation".into(),
7629 ));
7630 }
7631 SqlExpr::Between {
7632 expr: between_expr,
7633 negated,
7634 low,
7635 high,
7636 } => {
7637 let between_expr_result = translate_between_expr(
7638 resolver,
7639 context.clone(),
7640 between_expr,
7641 BetweenBounds {
7642 lower: low,
7643 upper: high,
7644 },
7645 *negated,
7646 outer_scopes,
7647 correlated_tracker.reborrow(),
7648 )?;
7649 work_stack.push(ConditionFrame::Leaf(between_expr_result));
7650 }
7651 SqlExpr::Exists { subquery, negated } => {
7652 let mut nested_scopes = outer_scopes.to_vec();
7654 nested_scopes.push(context.clone());
7655
7656 let mut tracker = SubqueryCorrelatedColumnTracker::new();
7657 let mut nested_subqueries = Vec::new();
7658
7659 let subquery_plan = engine.build_select_plan_internal(
7661 (**subquery).clone(),
7662 resolver,
7663 &nested_scopes,
7664 &mut nested_subqueries,
7665 Some(&mut tracker),
7666 )?;
7667
7668 let subquery_id = llkv_expr::SubqueryId(subqueries.len() as u32);
7669 let filter_subquery = llkv_plan::FilterSubquery {
7670 id: subquery_id,
7671 plan: Box::new(subquery_plan),
7672 correlated_columns: tracker.into_columns(),
7673 };
7674 subqueries.push(filter_subquery);
7675
7676 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Exists(
7677 llkv_expr::SubqueryExpr {
7678 id: subquery_id,
7679 negated: *negated,
7680 },
7681 )));
7682 }
7683 other => {
7684 return Err(Error::InvalidArgumentError(format!(
7685 "unsupported WHERE clause: {other:?}"
7686 )));
7687 }
7688 },
7689 ConditionFrame::Leaf(translated) => {
7690 result_stack.push(translated);
7691 }
7692 ConditionFrame::Exit(exit_context) => match exit_context {
7693 ConditionExitContext::And => {
7694 let right = result_stack.pop().ok_or_else(|| {
7695 Error::Internal(
7696 "translate_condition: result stack underflow for And right".into(),
7697 )
7698 })?;
7699 let left = result_stack.pop().ok_or_else(|| {
7700 Error::Internal(
7701 "translate_condition: result stack underflow for And left".into(),
7702 )
7703 })?;
7704 result_stack.push(flatten_and(left, right));
7705 }
7706 ConditionExitContext::Or => {
7707 let right = result_stack.pop().ok_or_else(|| {
7708 Error::Internal(
7709 "translate_condition: result stack underflow for Or right".into(),
7710 )
7711 })?;
7712 let left = result_stack.pop().ok_or_else(|| {
7713 Error::Internal(
7714 "translate_condition: result stack underflow for Or left".into(),
7715 )
7716 })?;
7717 result_stack.push(flatten_or(left, right));
7718 }
7719 ConditionExitContext::Not => {
7720 let inner = result_stack.pop().ok_or_else(|| {
7721 Error::Internal(
7722 "translate_condition: result stack underflow for Not".into(),
7723 )
7724 })?;
7725 match inner {
7727 llkv_expr::expr::Expr::IsNull { expr, negated } => {
7728 result_stack.push(llkv_expr::expr::Expr::IsNull {
7729 expr,
7730 negated: !negated,
7731 });
7732 }
7733 other => {
7734 result_stack.push(llkv_expr::expr::Expr::not(other));
7735 }
7736 }
7737 }
7738 ConditionExitContext::Nested => {
7739 }
7741 },
7742 }
7743 }
7744
7745 result_stack.pop().ok_or_else(|| {
7746 Error::Internal("translate_condition_with_context: empty result stack".into())
7747 })
7748}
7749
7750fn flatten_and(
7751 left: llkv_expr::expr::Expr<'static, String>,
7752 right: llkv_expr::expr::Expr<'static, String>,
7753) -> llkv_expr::expr::Expr<'static, String> {
7754 let mut children: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
7755 match left {
7756 llkv_expr::expr::Expr::And(mut left_children) => children.append(&mut left_children),
7757 other => children.push(other),
7758 }
7759 match right {
7760 llkv_expr::expr::Expr::And(mut right_children) => children.append(&mut right_children),
7761 other => children.push(other),
7762 }
7763 if children.len() == 1 {
7764 children.into_iter().next().unwrap()
7765 } else {
7766 llkv_expr::expr::Expr::And(children)
7767 }
7768}
7769
7770fn flatten_or(
7771 left: llkv_expr::expr::Expr<'static, String>,
7772 right: llkv_expr::expr::Expr<'static, String>,
7773) -> llkv_expr::expr::Expr<'static, String> {
7774 let mut children: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
7775 match left {
7776 llkv_expr::expr::Expr::Or(mut left_children) => children.append(&mut left_children),
7777 other => children.push(other),
7778 }
7779 match right {
7780 llkv_expr::expr::Expr::Or(mut right_children) => children.append(&mut right_children),
7781 other => children.push(other),
7782 }
7783 if children.len() == 1 {
7784 children.into_iter().next().unwrap()
7785 } else {
7786 llkv_expr::expr::Expr::Or(children)
7787 }
7788}
7789
7790fn peel_unparenthesized_not_chain(expr: &SqlExpr) -> (usize, &SqlExpr) {
7791 let mut count: usize = 0;
7792 let mut current = expr;
7793 while let SqlExpr::UnaryOp {
7794 op: UnaryOperator::Not,
7795 expr: inner,
7796 } = current
7797 {
7798 if matches!(inner.as_ref(), SqlExpr::Nested(_)) {
7799 break;
7800 }
7801 count += 1;
7802 current = inner.as_ref();
7803 }
7804 (count, current)
7805}
7806
7807fn translate_comparison_with_context(
7808 resolver: &IdentifierResolver<'_>,
7809 context: IdentifierContext,
7810 left: &SqlExpr,
7811 op: BinaryOperator,
7812 right: &SqlExpr,
7813 outer_scopes: &[IdentifierContext],
7814 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
7815) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
7816 let (not_count, comparison_left) = peel_unparenthesized_not_chain(left);
7817
7818 let left_scalar = {
7819 let tracker = correlated_tracker.reborrow();
7820 translate_scalar_with_context_scoped(
7821 resolver,
7822 context.clone(),
7823 comparison_left,
7824 outer_scopes,
7825 tracker,
7826 )?
7827 };
7828 let right_scalar = {
7829 let tracker = correlated_tracker.reborrow();
7830 translate_scalar_with_context_scoped(resolver, context, right, outer_scopes, tracker)?
7831 };
7832 let compare_op = match op {
7833 BinaryOperator::Eq => llkv_expr::expr::CompareOp::Eq,
7834 BinaryOperator::NotEq => llkv_expr::expr::CompareOp::NotEq,
7835 BinaryOperator::Lt => llkv_expr::expr::CompareOp::Lt,
7836 BinaryOperator::LtEq => llkv_expr::expr::CompareOp::LtEq,
7837 BinaryOperator::Gt => llkv_expr::expr::CompareOp::Gt,
7838 BinaryOperator::GtEq => llkv_expr::expr::CompareOp::GtEq,
7839 other => {
7840 return Err(Error::InvalidArgumentError(format!(
7841 "unsupported comparison operator: {other:?}"
7842 )));
7843 }
7844 };
7845
7846 let mut expr = llkv_expr::expr::Expr::Compare {
7847 left: left_scalar.clone(),
7848 op: compare_op,
7849 right: right_scalar.clone(),
7850 };
7851
7852 if let (
7853 llkv_expr::expr::ScalarExpr::Column(column),
7854 llkv_expr::expr::ScalarExpr::Literal(literal),
7855 ) = (&left_scalar, &right_scalar)
7856 && let Some(op) = compare_op_to_filter_operator(compare_op, literal)
7857 {
7858 tracing::debug!(
7859 column = ?column,
7860 literal = ?literal,
7861 ?compare_op,
7862 "translate_comparison direct"
7863 );
7864 expr = llkv_expr::expr::Expr::Pred(llkv_expr::expr::Filter {
7865 field_id: column.clone(),
7866 op,
7867 });
7868 } else if let (
7869 llkv_expr::expr::ScalarExpr::Literal(literal),
7870 llkv_expr::expr::ScalarExpr::Column(column),
7871 ) = (&left_scalar, &right_scalar)
7872 && let Some(flipped) = flip_compare_op(compare_op)
7873 && let Some(op) = compare_op_to_filter_operator(flipped, literal)
7874 {
7875 tracing::debug!(
7876 column = ?column,
7877 literal = ?literal,
7878 original_op = ?compare_op,
7879 flipped_op = ?flipped,
7880 "translate_comparison flipped"
7881 );
7882 expr = llkv_expr::expr::Expr::Pred(llkv_expr::expr::Filter {
7883 field_id: column.clone(),
7884 op,
7885 });
7886 }
7887
7888 let mut wrapped = expr;
7889 for _ in 0..not_count {
7890 wrapped = llkv_expr::expr::Expr::Not(Box::new(wrapped));
7891 }
7892
7893 Ok(wrapped)
7894}
7895
7896fn compare_op_to_filter_operator(
7897 op: llkv_expr::expr::CompareOp,
7898 literal: &Literal,
7899) -> Option<llkv_expr::expr::Operator<'static>> {
7900 if matches!(literal, Literal::Null) {
7901 return None;
7902 }
7903 let lit = literal.clone();
7904 tracing::debug!(?op, literal = ?literal, "compare_op_to_filter_operator input");
7905 match op {
7906 llkv_expr::expr::CompareOp::Eq => Some(llkv_expr::expr::Operator::Equals(lit)),
7907 llkv_expr::expr::CompareOp::Lt => Some(llkv_expr::expr::Operator::LessThan(lit)),
7908 llkv_expr::expr::CompareOp::LtEq => Some(llkv_expr::expr::Operator::LessThanOrEquals(lit)),
7909 llkv_expr::expr::CompareOp::Gt => Some(llkv_expr::expr::Operator::GreaterThan(lit)),
7910 llkv_expr::expr::CompareOp::GtEq => {
7911 Some(llkv_expr::expr::Operator::GreaterThanOrEquals(lit))
7912 }
7913 llkv_expr::expr::CompareOp::NotEq => None,
7914 }
7915}
7916
7917fn flip_compare_op(op: llkv_expr::expr::CompareOp) -> Option<llkv_expr::expr::CompareOp> {
7918 match op {
7919 llkv_expr::expr::CompareOp::Eq => Some(llkv_expr::expr::CompareOp::Eq),
7920 llkv_expr::expr::CompareOp::Lt => Some(llkv_expr::expr::CompareOp::Gt),
7921 llkv_expr::expr::CompareOp::LtEq => Some(llkv_expr::expr::CompareOp::GtEq),
7922 llkv_expr::expr::CompareOp::Gt => Some(llkv_expr::expr::CompareOp::Lt),
7923 llkv_expr::expr::CompareOp::GtEq => Some(llkv_expr::expr::CompareOp::LtEq),
7924 llkv_expr::expr::CompareOp::NotEq => None,
7925 }
7926}
7927fn translate_scalar_with_context(
7930 resolver: &IdentifierResolver<'_>,
7931 context: IdentifierContext,
7932 expr: &SqlExpr,
7933) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
7934 let mut tracker = SubqueryCorrelatedTracker::from_option(None);
7935 translate_scalar_internal(
7936 expr,
7937 Some(resolver),
7938 Some(&context),
7939 &[],
7940 &mut tracker,
7941 None,
7942 )
7943}
7944
7945fn translate_scalar_with_context_scoped(
7946 resolver: &IdentifierResolver<'_>,
7947 context: IdentifierContext,
7948 expr: &SqlExpr,
7949 outer_scopes: &[IdentifierContext],
7950 correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
7951) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
7952 let mut tracker = SubqueryCorrelatedTracker::from_option(correlated_tracker);
7953 translate_scalar_internal(
7954 expr,
7955 Some(resolver),
7956 Some(&context),
7957 outer_scopes,
7958 &mut tracker,
7959 None,
7960 )
7961}
7962
7963#[allow(dead_code)]
7964fn translate_scalar(expr: &SqlExpr) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
7965 let mut tracker = SubqueryCorrelatedTracker::from_option(None);
7966 translate_scalar_internal(expr, None, None, &[], &mut tracker, None)
7967}
7968
7969fn translate_scalar_internal(
7970 expr: &SqlExpr,
7971 resolver: Option<&IdentifierResolver<'_>>,
7972 context: Option<&IdentifierContext>,
7973 outer_scopes: &[IdentifierContext],
7974 tracker: &mut SubqueryCorrelatedTracker<'_>,
7975 mut subquery_resolver: Option<&mut dyn ScalarSubqueryResolver>,
7976) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
7977 enum ScalarExitContext {
7985 BinaryOp {
7986 op: BinaryOperator,
7987 },
7988 Compare {
7989 op: llkv_expr::expr::CompareOp,
7990 },
7991 UnaryNot,
7992 UnaryMinus,
7993 UnaryPlus,
7994 Nested,
7995 Cast(DataType),
7996 IsNull {
7997 negated: bool,
7998 },
7999 Between {
8000 negated: bool,
8001 },
8002 InList {
8003 list_len: usize,
8004 negated: bool,
8005 },
8006 Case {
8007 branch_count: usize,
8008 has_operand: bool,
8009 has_else: bool,
8010 },
8011 BuiltinFunction {
8012 func: BuiltinScalarFunction,
8013 arg_count: usize,
8014 },
8015 }
8016
8017 #[derive(Clone, Copy)]
8018 enum BuiltinScalarFunction {
8019 Abs,
8020 Coalesce,
8021 NullIf,
8022 }
8023
8024 type ScalarFrame<'a> =
8025 TransformFrame<'a, SqlExpr, llkv_expr::expr::ScalarExpr<String>, ScalarExitContext>;
8026
8027 let mut work_stack: Vec<ScalarFrame> = vec![ScalarFrame::Enter(expr)];
8028 let mut result_stack: Vec<llkv_expr::expr::ScalarExpr<String>> = Vec::new();
8029
8030 while let Some(frame) = work_stack.pop() {
8031 match frame {
8032 ScalarFrame::Enter(node) => match node {
8033 SqlExpr::Identifier(ident) => {
8034 if let (Some(resolver), Some(ctx)) = (resolver, context) {
8035 let parts = vec![ident.value.clone()];
8036 let tracker_view = tracker.reborrow();
8037 let expr = resolve_identifier_expr(
8038 resolver,
8039 ctx,
8040 parts,
8041 outer_scopes,
8042 tracker_view,
8043 )?;
8044 work_stack.push(ScalarFrame::Leaf(expr));
8045 } else {
8046 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::column(
8047 ident.value.clone(),
8048 )));
8049 }
8050 }
8051 SqlExpr::CompoundIdentifier(idents) => {
8052 if idents.is_empty() {
8053 return Err(Error::InvalidArgumentError(
8054 "invalid compound identifier".into(),
8055 ));
8056 }
8057
8058 if let (Some(resolver), Some(ctx)) = (resolver, context) {
8059 let parts: Vec<String> =
8060 idents.iter().map(|ident| ident.value.clone()).collect();
8061 let tracker_view = tracker.reborrow();
8062 let expr = resolve_identifier_expr(
8063 resolver,
8064 ctx,
8065 parts,
8066 outer_scopes,
8067 tracker_view,
8068 )?;
8069 work_stack.push(ScalarFrame::Leaf(expr));
8070 } else {
8071 let column_name = idents[0].value.clone();
8072 let mut result = llkv_expr::expr::ScalarExpr::column(column_name);
8073
8074 for part in &idents[1..] {
8075 let field_name = part.value.clone();
8076 result = llkv_expr::expr::ScalarExpr::get_field(result, field_name);
8077 }
8078
8079 work_stack.push(ScalarFrame::Leaf(result));
8080 }
8081 }
8082 SqlExpr::Value(value) => {
8083 let result = literal_from_value(value)?;
8084 work_stack.push(ScalarFrame::Leaf(result));
8085 }
8086 SqlExpr::BinaryOp { left, op, right } => match op {
8087 BinaryOperator::Plus
8088 | BinaryOperator::Minus
8089 | BinaryOperator::Multiply
8090 | BinaryOperator::Divide
8091 | BinaryOperator::Modulo
8092 | BinaryOperator::And
8093 | BinaryOperator::Or
8094 | BinaryOperator::PGBitwiseShiftLeft
8095 | BinaryOperator::PGBitwiseShiftRight => {
8096 work_stack.push(ScalarFrame::Exit(ScalarExitContext::BinaryOp {
8097 op: op.clone(),
8098 }));
8099 work_stack.push(ScalarFrame::Enter(right));
8100 work_stack.push(ScalarFrame::Enter(left));
8101 }
8102 BinaryOperator::Eq
8103 | BinaryOperator::NotEq
8104 | BinaryOperator::Lt
8105 | BinaryOperator::LtEq
8106 | BinaryOperator::Gt
8107 | BinaryOperator::GtEq => {
8108 let compare_op = match op {
8109 BinaryOperator::Eq => llkv_expr::expr::CompareOp::Eq,
8110 BinaryOperator::NotEq => llkv_expr::expr::CompareOp::NotEq,
8111 BinaryOperator::Lt => llkv_expr::expr::CompareOp::Lt,
8112 BinaryOperator::LtEq => llkv_expr::expr::CompareOp::LtEq,
8113 BinaryOperator::Gt => llkv_expr::expr::CompareOp::Gt,
8114 BinaryOperator::GtEq => llkv_expr::expr::CompareOp::GtEq,
8115 _ => unreachable!(),
8116 };
8117 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Compare {
8118 op: compare_op,
8119 }));
8120 work_stack.push(ScalarFrame::Enter(right));
8121 work_stack.push(ScalarFrame::Enter(left));
8122 }
8123 other => {
8124 return Err(Error::InvalidArgumentError(format!(
8125 "unsupported scalar binary operator: {other:?}"
8126 )));
8127 }
8128 },
8129 SqlExpr::UnaryOp {
8130 op: UnaryOperator::Not,
8131 expr: inner,
8132 } => {
8133 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryNot));
8134 work_stack.push(ScalarFrame::Enter(inner));
8135 }
8136 SqlExpr::UnaryOp {
8137 op: UnaryOperator::Minus,
8138 expr: inner,
8139 } => {
8140 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryMinus));
8141 work_stack.push(ScalarFrame::Enter(inner));
8142 }
8143 SqlExpr::UnaryOp {
8144 op: UnaryOperator::Plus,
8145 expr: inner,
8146 } => {
8147 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryPlus));
8148 work_stack.push(ScalarFrame::Enter(inner));
8149 }
8150 SqlExpr::Nested(inner) => {
8151 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Nested));
8152 work_stack.push(ScalarFrame::Enter(inner));
8153 }
8154 SqlExpr::Cast {
8155 expr: inner,
8156 data_type,
8157 ..
8158 } => {
8159 let target_type = arrow_type_from_sql(data_type)?;
8160 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Cast(target_type)));
8161 work_stack.push(ScalarFrame::Enter(inner));
8162 }
8163 SqlExpr::Case {
8164 operand,
8165 conditions,
8166 else_result,
8167 ..
8168 } => {
8169 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Case {
8170 branch_count: conditions.len(),
8171 has_operand: operand.is_some(),
8172 has_else: else_result.is_some(),
8173 }));
8174 if let Some(else_expr) = else_result.as_deref() {
8175 work_stack.push(ScalarFrame::Enter(else_expr));
8176 }
8177 for case_when in conditions.iter().rev() {
8178 work_stack.push(ScalarFrame::Enter(&case_when.result));
8179 work_stack.push(ScalarFrame::Enter(&case_when.condition));
8180 }
8181 if let Some(opnd) = operand.as_deref() {
8182 work_stack.push(ScalarFrame::Enter(opnd));
8183 }
8184 }
8185 SqlExpr::InList {
8186 expr: in_expr,
8187 list,
8188 negated,
8189 } => {
8190 if list.is_empty() {
8191 let literal_value = if *negated {
8192 llkv_expr::expr::ScalarExpr::literal(Literal::Integer(1))
8193 } else {
8194 llkv_expr::expr::ScalarExpr::literal(Literal::Integer(0))
8195 };
8196 work_stack.push(ScalarFrame::Leaf(literal_value));
8197 } else {
8198 work_stack.push(ScalarFrame::Exit(ScalarExitContext::InList {
8199 list_len: list.len(),
8200 negated: *negated,
8201 }));
8202 for value_expr in list.iter().rev() {
8203 work_stack.push(ScalarFrame::Enter(value_expr));
8204 }
8205 work_stack.push(ScalarFrame::Enter(in_expr));
8206 }
8207 }
8208 SqlExpr::IsNull(inner) => {
8209 work_stack.push(ScalarFrame::Exit(ScalarExitContext::IsNull {
8210 negated: false,
8211 }));
8212 work_stack.push(ScalarFrame::Enter(inner));
8213 }
8214 SqlExpr::IsNotNull(inner) => {
8215 work_stack.push(ScalarFrame::Exit(ScalarExitContext::IsNull {
8216 negated: true,
8217 }));
8218 work_stack.push(ScalarFrame::Enter(inner));
8219 }
8220 SqlExpr::Between {
8221 expr: between_expr,
8222 negated,
8223 low,
8224 high,
8225 } => {
8226 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Between {
8227 negated: *negated,
8228 }));
8229 work_stack.push(ScalarFrame::Enter(high));
8230 work_stack.push(ScalarFrame::Enter(low));
8231 work_stack.push(ScalarFrame::Enter(between_expr));
8232 }
8233 SqlExpr::Function(func) => {
8234 if let Some(agg_call) = try_parse_aggregate_function(
8235 func,
8236 resolver,
8237 context,
8238 outer_scopes,
8239 tracker,
8240 )? {
8241 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::aggregate(
8242 agg_call,
8243 )));
8244 } else {
8245 use sqlparser::ast::{
8246 FunctionArg, FunctionArgExpr, FunctionArguments, ObjectNamePart,
8247 };
8248
8249 if func.uses_odbc_syntax
8250 || !matches!(func.parameters, FunctionArguments::None)
8251 || func.filter.is_some()
8252 || func.null_treatment.is_some()
8253 || func.over.is_some()
8254 || !func.within_group.is_empty()
8255 {
8256 return Err(Error::InvalidArgumentError(format!(
8257 "unsupported function in scalar expression: {:?}",
8258 func.name
8259 )));
8260 }
8261
8262 let func_name = if func.name.0.len() == 1 {
8263 match &func.name.0[0] {
8264 ObjectNamePart::Identifier(ident) => {
8265 ident.value.to_ascii_lowercase()
8266 }
8267 _ => {
8268 return Err(Error::InvalidArgumentError(format!(
8269 "unsupported function in scalar expression: {:?}",
8270 func.name
8271 )));
8272 }
8273 }
8274 } else {
8275 return Err(Error::InvalidArgumentError(format!(
8276 "unsupported function in scalar expression: {:?}",
8277 func.name
8278 )));
8279 };
8280
8281 match func_name.as_str() {
8282 "abs" => {
8283 let args_slice: &[FunctionArg] = match &func.args {
8284 FunctionArguments::List(list) => {
8285 if list.duplicate_treatment.is_some()
8286 || !list.clauses.is_empty()
8287 {
8288 return Err(Error::InvalidArgumentError(
8289 "ABS does not support qualifiers".into(),
8290 ));
8291 }
8292 &list.args
8293 }
8294 _ => {
8295 return Err(Error::InvalidArgumentError(
8296 "ABS requires exactly one argument".into(),
8297 ));
8298 }
8299 };
8300
8301 if args_slice.len() != 1 {
8302 return Err(Error::InvalidArgumentError(
8303 "ABS requires exactly one argument".into(),
8304 ));
8305 }
8306
8307 let arg_expr = match &args_slice[0] {
8308 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
8309 _ => {
8310 return Err(Error::InvalidArgumentError(
8311 "ABS argument must be an expression".into(),
8312 ));
8313 }
8314 };
8315
8316 work_stack.push(ScalarFrame::Exit(
8317 ScalarExitContext::BuiltinFunction {
8318 func: BuiltinScalarFunction::Abs,
8319 arg_count: 1,
8320 },
8321 ));
8322 work_stack.push(ScalarFrame::Enter(arg_expr));
8323 continue;
8324 }
8325 "coalesce" => {
8326 let args_slice: &[FunctionArg] = match &func.args {
8327 FunctionArguments::List(list) => {
8328 if list.duplicate_treatment.is_some()
8329 || !list.clauses.is_empty()
8330 {
8331 return Err(Error::InvalidArgumentError(
8332 "COALESCE does not support qualifiers".into(),
8333 ));
8334 }
8335 &list.args
8336 }
8337 _ => {
8338 return Err(Error::InvalidArgumentError(
8339 "COALESCE requires at least one argument".into(),
8340 ));
8341 }
8342 };
8343
8344 if args_slice.is_empty() {
8345 return Err(Error::InvalidArgumentError(
8346 "COALESCE requires at least one argument".into(),
8347 ));
8348 }
8349
8350 work_stack.push(ScalarFrame::Exit(
8351 ScalarExitContext::BuiltinFunction {
8352 func: BuiltinScalarFunction::Coalesce,
8353 arg_count: args_slice.len(),
8354 },
8355 ));
8356
8357 for arg in args_slice.iter().rev() {
8358 let arg_expr = match arg {
8359 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
8360 _ => {
8361 return Err(Error::InvalidArgumentError(
8362 "COALESCE arguments must be expressions".into(),
8363 ));
8364 }
8365 };
8366 work_stack.push(ScalarFrame::Enter(arg_expr));
8367 }
8368 continue;
8369 }
8370 "nullif" => {
8371 let args_slice: &[FunctionArg] = match &func.args {
8372 FunctionArguments::List(list) => {
8373 if list.duplicate_treatment.is_some()
8374 || !list.clauses.is_empty()
8375 {
8376 return Err(Error::InvalidArgumentError(
8377 "NULLIF does not support qualifiers".into(),
8378 ));
8379 }
8380 &list.args
8381 }
8382 _ => {
8383 return Err(Error::InvalidArgumentError(
8384 "NULLIF requires exactly two arguments".into(),
8385 ));
8386 }
8387 };
8388
8389 if args_slice.len() != 2 {
8390 return Err(Error::InvalidArgumentError(
8391 "NULLIF requires exactly two arguments".into(),
8392 ));
8393 }
8394
8395 work_stack.push(ScalarFrame::Exit(
8396 ScalarExitContext::BuiltinFunction {
8397 func: BuiltinScalarFunction::NullIf,
8398 arg_count: 2,
8399 },
8400 ));
8401
8402 for arg in args_slice.iter().rev() {
8403 let arg_expr = match arg {
8404 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
8405 _ => {
8406 return Err(Error::InvalidArgumentError(
8407 "NULLIF arguments must be expressions".into(),
8408 ));
8409 }
8410 };
8411 work_stack.push(ScalarFrame::Enter(arg_expr));
8412 }
8413 continue;
8414 }
8415 _ => {
8416 return Err(Error::InvalidArgumentError(format!(
8417 "unsupported function in scalar expression: {:?}",
8418 func.name
8419 )));
8420 }
8421 }
8422 }
8423 }
8424 SqlExpr::Dictionary(fields) => {
8425 let mut struct_fields = Vec::new();
8427 for entry in fields {
8428 let key = entry.key.value.clone();
8429 let mut tracker_view = tracker.reborrow();
8432 let value_expr = translate_scalar_internal(
8433 &entry.value,
8434 resolver,
8435 context,
8436 outer_scopes,
8437 &mut tracker_view,
8438 None,
8439 )?;
8440 match value_expr {
8441 llkv_expr::expr::ScalarExpr::Literal(lit) => {
8442 struct_fields.push((key, Box::new(lit)));
8443 }
8444 _ => {
8445 return Err(Error::InvalidArgumentError(
8446 "Dictionary values must be literals".to_string(),
8447 ));
8448 }
8449 }
8450 }
8451 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::literal(
8452 Literal::Struct(struct_fields),
8453 )));
8454 }
8455 SqlExpr::Subquery(subquery) => {
8456 let handler = subquery_resolver.as_mut().ok_or_else(|| {
8457 Error::InvalidArgumentError(
8458 "Correlated scalar subqueries not yet fully implemented - requires plan-level support".
8459 to_string(),
8460 )
8461 })?;
8462 let resolver_ref = resolver.ok_or_else(|| {
8463 Error::InvalidArgumentError(
8464 "scalar subquery translation requires identifier resolver".into(),
8465 )
8466 })?;
8467 let context_ref = context.ok_or_else(|| {
8468 Error::InvalidArgumentError(
8469 "scalar subquery translation requires identifier context".into(),
8470 )
8471 })?;
8472 let translated = handler.handle_scalar_subquery(
8473 subquery.as_ref(),
8474 resolver_ref,
8475 context_ref,
8476 outer_scopes,
8477 )?;
8478 work_stack.push(ScalarFrame::Leaf(translated));
8479 }
8480 other => {
8481 return Err(Error::InvalidArgumentError(format!(
8482 "unsupported scalar expression: {other:?}"
8483 )));
8484 }
8485 },
8486 ScalarFrame::Leaf(translated) => {
8487 result_stack.push(translated);
8488 }
8489 ScalarFrame::Exit(exit_context) => match exit_context {
8490 ScalarExitContext::BinaryOp { op } => {
8491 let right_expr = result_stack.pop().ok_or_else(|| {
8492 Error::Internal(
8493 "translate_scalar: result stack underflow for BinaryOp right".into(),
8494 )
8495 })?;
8496 let left_expr = result_stack.pop().ok_or_else(|| {
8497 Error::Internal(
8498 "translate_scalar: result stack underflow for BinaryOp left".into(),
8499 )
8500 })?;
8501 match op {
8502 BinaryOperator::Plus => {
8503 let expr = llkv_expr::expr::ScalarExpr::binary(
8504 left_expr,
8505 llkv_expr::expr::BinaryOp::Add,
8506 right_expr,
8507 );
8508 result_stack.push(expr);
8509 }
8510 BinaryOperator::Minus => {
8511 let expr = llkv_expr::expr::ScalarExpr::binary(
8512 left_expr,
8513 llkv_expr::expr::BinaryOp::Subtract,
8514 right_expr,
8515 );
8516 result_stack.push(expr);
8517 }
8518 BinaryOperator::Multiply => {
8519 let expr = llkv_expr::expr::ScalarExpr::binary(
8520 left_expr,
8521 llkv_expr::expr::BinaryOp::Multiply,
8522 right_expr,
8523 );
8524 result_stack.push(expr);
8525 }
8526 BinaryOperator::Divide => {
8527 let expr = llkv_expr::expr::ScalarExpr::binary(
8528 left_expr,
8529 llkv_expr::expr::BinaryOp::Divide,
8530 right_expr,
8531 );
8532 result_stack.push(expr);
8533 }
8534 BinaryOperator::Modulo => {
8535 let expr = llkv_expr::expr::ScalarExpr::binary(
8536 left_expr,
8537 llkv_expr::expr::BinaryOp::Modulo,
8538 right_expr,
8539 );
8540 result_stack.push(expr);
8541 }
8542 BinaryOperator::And => {
8543 let expr = llkv_expr::expr::ScalarExpr::binary(
8544 left_expr,
8545 llkv_expr::expr::BinaryOp::And,
8546 right_expr,
8547 );
8548 result_stack.push(expr);
8549 }
8550 BinaryOperator::Or => {
8551 let expr = llkv_expr::expr::ScalarExpr::binary(
8552 left_expr,
8553 llkv_expr::expr::BinaryOp::Or,
8554 right_expr,
8555 );
8556 result_stack.push(expr);
8557 }
8558 BinaryOperator::PGBitwiseShiftLeft => {
8559 let expr = llkv_expr::expr::ScalarExpr::binary(
8560 left_expr,
8561 llkv_expr::expr::BinaryOp::BitwiseShiftLeft,
8562 right_expr,
8563 );
8564 result_stack.push(expr);
8565 }
8566 BinaryOperator::PGBitwiseShiftRight => {
8567 let expr = llkv_expr::expr::ScalarExpr::binary(
8568 left_expr,
8569 llkv_expr::expr::BinaryOp::BitwiseShiftRight,
8570 right_expr,
8571 );
8572 result_stack.push(expr);
8573 }
8574 other => {
8575 return Err(Error::InvalidArgumentError(format!(
8576 "unsupported scalar binary operator: {other:?}"
8577 )));
8578 }
8579 }
8580 }
8581 ScalarExitContext::Compare { op } => {
8582 let right_expr = result_stack.pop().ok_or_else(|| {
8583 Error::Internal(
8584 "translate_scalar: result stack underflow for Compare right".into(),
8585 )
8586 })?;
8587 let left_expr = result_stack.pop().ok_or_else(|| {
8588 Error::Internal(
8589 "translate_scalar: result stack underflow for Compare left".into(),
8590 )
8591 })?;
8592 result_stack.push(llkv_expr::expr::ScalarExpr::compare(
8593 left_expr, op, right_expr,
8594 ));
8595 }
8596 ScalarExitContext::BuiltinFunction { func, arg_count } => {
8597 if result_stack.len() < arg_count {
8598 return Err(Error::Internal(
8599 "translate_scalar: result stack underflow for builtin function".into(),
8600 ));
8601 }
8602
8603 let mut args: Vec<llkv_expr::expr::ScalarExpr<String>> =
8604 Vec::with_capacity(arg_count);
8605 for _ in 0..arg_count {
8606 if let Some(expr) = result_stack.pop() {
8607 args.push(expr);
8608 }
8609 }
8610 args.reverse();
8611
8612 let result_expr = match func {
8613 BuiltinScalarFunction::Abs => {
8614 debug_assert_eq!(args.len(), 1);
8615 build_abs_case_expr(args.pop().expect("ABS expects one argument"))
8616 }
8617 BuiltinScalarFunction::Coalesce => {
8618 llkv_expr::expr::ScalarExpr::coalesce(args)
8619 }
8620 BuiltinScalarFunction::NullIf => {
8621 debug_assert_eq!(args.len(), 2);
8622 let left = args.remove(0);
8623 let right = args.remove(0);
8624 let condition = llkv_expr::expr::ScalarExpr::compare(
8625 left.clone(),
8626 llkv_expr::expr::CompareOp::Eq,
8627 right,
8628 );
8629 llkv_expr::expr::ScalarExpr::Case {
8630 operand: None,
8631 branches: vec![(
8632 condition,
8633 llkv_expr::expr::ScalarExpr::literal(Literal::Null),
8634 )],
8635 else_expr: Some(Box::new(left)),
8636 }
8637 }
8638 };
8639
8640 result_stack.push(result_expr);
8641 }
8642 ScalarExitContext::UnaryMinus => {
8643 let inner = result_stack.pop().ok_or_else(|| {
8644 Error::Internal(
8645 "translate_scalar: result stack underflow for UnaryMinus".into(),
8646 )
8647 })?;
8648 match inner {
8649 llkv_expr::expr::ScalarExpr::Literal(lit) => match lit {
8650 Literal::Integer(v) => {
8651 result_stack.push(llkv_expr::expr::ScalarExpr::literal(
8652 Literal::Integer(-v),
8653 ));
8654 }
8655 Literal::Float(v) => {
8656 result_stack
8657 .push(llkv_expr::expr::ScalarExpr::literal(Literal::Float(-v)));
8658 }
8659 Literal::Boolean(_) => {
8660 return Err(Error::InvalidArgumentError(
8661 "cannot negate boolean literal".into(),
8662 ));
8663 }
8664 Literal::String(_) => {
8665 return Err(Error::InvalidArgumentError(
8666 "cannot negate string literal".into(),
8667 ));
8668 }
8669 Literal::Struct(_) => {
8670 return Err(Error::InvalidArgumentError(
8671 "cannot negate struct literal".into(),
8672 ));
8673 }
8674 Literal::Null => {
8675 result_stack
8676 .push(llkv_expr::expr::ScalarExpr::literal(Literal::Null));
8677 }
8678 },
8679 other => {
8680 let zero = llkv_expr::expr::ScalarExpr::literal(Literal::Integer(0));
8681 result_stack.push(llkv_expr::expr::ScalarExpr::binary(
8682 zero,
8683 llkv_expr::expr::BinaryOp::Subtract,
8684 other,
8685 ));
8686 }
8687 }
8688 }
8689 ScalarExitContext::UnaryNot => {
8690 let inner = result_stack.pop().ok_or_else(|| {
8691 Error::Internal(
8692 "translate_scalar: result stack underflow for UnaryNot".into(),
8693 )
8694 })?;
8695 result_stack.push(llkv_expr::expr::ScalarExpr::logical_not(inner));
8696 }
8697 ScalarExitContext::UnaryPlus => {
8698 let inner = result_stack.pop().ok_or_else(|| {
8702 Error::Internal(
8703 "translate_scalar: result stack underflow for UnaryPlus".into(),
8704 )
8705 })?;
8706 result_stack.push(inner);
8707 }
8708 ScalarExitContext::Nested => {
8709 }
8711 ScalarExitContext::Cast(target_type) => {
8712 let inner = result_stack.pop().ok_or_else(|| {
8713 Error::Internal("translate_scalar: result stack underflow for CAST".into())
8714 })?;
8715 result_stack.push(llkv_expr::expr::ScalarExpr::cast(inner, target_type));
8716 }
8717 ScalarExitContext::InList { list_len, negated } => {
8718 let mut list_exprs = Vec::with_capacity(list_len);
8719 for _ in 0..list_len {
8720 let value_expr = result_stack.pop().ok_or_else(|| {
8721 Error::Internal(
8722 "translate_scalar: result stack underflow for IN list value".into(),
8723 )
8724 })?;
8725 list_exprs.push(value_expr);
8726 }
8727 list_exprs.reverse();
8728
8729 let target_expr = result_stack.pop().ok_or_else(|| {
8730 Error::Internal(
8731 "translate_scalar: result stack underflow for IN list target".into(),
8732 )
8733 })?;
8734
8735 let mut comparisons: Vec<llkv_expr::expr::ScalarExpr<String>> =
8736 Vec::with_capacity(list_len);
8737 for value in &list_exprs {
8738 comparisons.push(llkv_expr::expr::ScalarExpr::compare(
8739 target_expr.clone(),
8740 llkv_expr::expr::CompareOp::Eq,
8741 value.clone(),
8742 ));
8743 }
8744
8745 let mut branches: Vec<(
8746 llkv_expr::expr::ScalarExpr<String>,
8747 llkv_expr::expr::ScalarExpr<String>,
8748 )> = Vec::with_capacity(list_len.saturating_mul(2));
8749
8750 for comparison in &comparisons {
8751 branches.push((
8752 comparison.clone(),
8753 llkv_expr::expr::ScalarExpr::literal(Literal::Integer(1)),
8754 ));
8755 }
8756
8757 for comparison in comparisons {
8758 let comparison_is_null =
8759 llkv_expr::expr::ScalarExpr::is_null(comparison, false);
8760 branches.push((
8761 comparison_is_null,
8762 llkv_expr::expr::ScalarExpr::literal(Literal::Null),
8763 ));
8764 }
8765
8766 let else_expr = Some(llkv_expr::expr::ScalarExpr::literal(Literal::Integer(0)));
8767 let in_result = llkv_expr::expr::ScalarExpr::case(None, branches, else_expr);
8768 let final_expr = if negated {
8769 llkv_expr::expr::ScalarExpr::logical_not(in_result)
8770 } else {
8771 in_result
8772 };
8773
8774 result_stack.push(final_expr);
8775 }
8776 ScalarExitContext::Case {
8777 branch_count,
8778 has_operand,
8779 has_else,
8780 } => {
8781 let else_expr = if has_else {
8782 Some(result_stack.pop().ok_or_else(|| {
8783 Error::Internal(
8784 "translate_scalar: result stack underflow for CASE ELSE".into(),
8785 )
8786 })?)
8787 } else {
8788 None
8789 };
8790
8791 let mut branches_rev = Vec::with_capacity(branch_count);
8792 for _ in 0..branch_count {
8793 let then_expr = result_stack.pop().ok_or_else(|| {
8794 Error::Internal(
8795 "translate_scalar: result stack underflow for CASE THEN".into(),
8796 )
8797 })?;
8798 let when_expr = result_stack.pop().ok_or_else(|| {
8799 Error::Internal(
8800 "translate_scalar: result stack underflow for CASE WHEN".into(),
8801 )
8802 })?;
8803 branches_rev.push((when_expr, then_expr));
8804 }
8805 branches_rev.reverse();
8806
8807 let operand_expr = if has_operand {
8808 Some(result_stack.pop().ok_or_else(|| {
8809 Error::Internal(
8810 "translate_scalar: result stack underflow for CASE operand".into(),
8811 )
8812 })?)
8813 } else {
8814 None
8815 };
8816
8817 let case_expr =
8818 llkv_expr::expr::ScalarExpr::case(operand_expr, branches_rev, else_expr);
8819 result_stack.push(case_expr);
8820 }
8821 ScalarExitContext::IsNull { negated } => {
8822 let inner = result_stack.pop().ok_or_else(|| {
8823 Error::Internal(
8824 "translate_scalar: result stack underflow for IS NULL operand".into(),
8825 )
8826 })?;
8827 result_stack.push(llkv_expr::expr::ScalarExpr::is_null(inner, negated));
8828 }
8829 ScalarExitContext::Between { negated } => {
8830 let high = result_stack.pop().ok_or_else(|| {
8831 Error::Internal(
8832 "translate_scalar: result stack underflow for BETWEEN upper".into(),
8833 )
8834 })?;
8835 let low = result_stack.pop().ok_or_else(|| {
8836 Error::Internal(
8837 "translate_scalar: result stack underflow for BETWEEN lower".into(),
8838 )
8839 })?;
8840 let expr_value = result_stack.pop().ok_or_else(|| {
8841 Error::Internal(
8842 "translate_scalar: result stack underflow for BETWEEN operand".into(),
8843 )
8844 })?;
8845
8846 let between_expr = if negated {
8847 let less_than = llkv_expr::expr::ScalarExpr::compare(
8848 expr_value.clone(),
8849 llkv_expr::expr::CompareOp::Lt,
8850 low.clone(),
8851 );
8852 let greater_than = llkv_expr::expr::ScalarExpr::compare(
8853 expr_value,
8854 llkv_expr::expr::CompareOp::Gt,
8855 high,
8856 );
8857 llkv_expr::expr::ScalarExpr::binary(
8858 less_than,
8859 llkv_expr::expr::BinaryOp::Or,
8860 greater_than,
8861 )
8862 } else {
8863 let greater_or_equal = llkv_expr::expr::ScalarExpr::compare(
8864 expr_value.clone(),
8865 llkv_expr::expr::CompareOp::GtEq,
8866 low,
8867 );
8868 let less_or_equal = llkv_expr::expr::ScalarExpr::compare(
8869 expr_value,
8870 llkv_expr::expr::CompareOp::LtEq,
8871 high,
8872 );
8873 llkv_expr::expr::ScalarExpr::binary(
8874 greater_or_equal,
8875 llkv_expr::expr::BinaryOp::And,
8876 less_or_equal,
8877 )
8878 };
8879 result_stack.push(between_expr);
8880 }
8881 },
8882 }
8883 }
8884
8885 result_stack
8886 .pop()
8887 .ok_or_else(|| Error::Internal("translate_scalar: empty result stack".into()))
8888}
8889
8890struct ScalarSubqueryPlanner<'engine, 'vec> {
8891 engine: &'engine SqlEngine,
8892 scalar_subqueries: &'vec mut Vec<llkv_plan::ScalarSubquery>,
8893}
8894
8895impl<'engine, 'vec> ScalarSubqueryResolver for ScalarSubqueryPlanner<'engine, 'vec> {
8896 fn handle_scalar_subquery(
8897 &mut self,
8898 subquery: &Query,
8899 resolver: &IdentifierResolver<'_>,
8900 context: &IdentifierContext,
8901 outer_scopes: &[IdentifierContext],
8902 ) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
8903 let mut nested_scopes = outer_scopes.to_vec();
8904 nested_scopes.push(context.clone());
8905
8906 let mut tracker = SubqueryCorrelatedColumnTracker::new();
8907 let mut nested_filter_subqueries = Vec::new();
8908
8909 let plan = self.engine.build_select_plan_internal(
8910 subquery.clone(),
8911 resolver,
8912 &nested_scopes,
8913 &mut nested_filter_subqueries,
8914 Some(&mut tracker),
8915 )?;
8916
8917 debug_assert!(nested_filter_subqueries.is_empty());
8918
8919 let id = u32::try_from(self.scalar_subqueries.len()).map_err(|_| {
8920 Error::InvalidArgumentError(
8921 "scalar subquery limit exceeded for current query".to_string(),
8922 )
8923 })?;
8924 let subquery_id = llkv_expr::SubqueryId(id);
8925 self.scalar_subqueries.push(llkv_plan::ScalarSubquery {
8926 id: subquery_id,
8927 plan: Box::new(plan),
8928 correlated_columns: tracker.into_columns(),
8929 });
8930
8931 Ok(llkv_expr::expr::ScalarExpr::scalar_subquery(subquery_id))
8932 }
8933}
8934
8935fn build_abs_case_expr(
8936 arg: llkv_expr::expr::ScalarExpr<String>,
8937) -> llkv_expr::expr::ScalarExpr<String> {
8938 use llkv_expr::expr::{BinaryOp, CompareOp, ScalarExpr};
8939
8940 let zero = ScalarExpr::literal(Literal::Integer(0));
8941 let condition = ScalarExpr::compare(arg.clone(), CompareOp::Lt, zero.clone());
8942 let negated = ScalarExpr::binary(zero.clone(), BinaryOp::Subtract, arg.clone());
8943
8944 ScalarExpr::case(None, vec![(condition, negated)], Some(arg))
8945}
8946
8947fn literal_from_value(value: &ValueWithSpan) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
8948 match &value.value {
8949 Value::Number(text, _) => {
8950 if text.contains(['.', 'e', 'E']) {
8951 let parsed = text.parse::<f64>().map_err(|err| {
8952 Error::InvalidArgumentError(format!("invalid float literal: {err}"))
8953 })?;
8954 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Float(parsed)))
8955 } else {
8956 let parsed = text.parse::<i128>().map_err(|err| {
8957 Error::InvalidArgumentError(format!("invalid integer literal: {err}"))
8958 })?;
8959 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Integer(
8960 parsed,
8961 )))
8962 }
8963 }
8964 Value::Boolean(value) => Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Boolean(
8965 *value,
8966 ))),
8967 Value::Null => Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Null)),
8968 other => {
8969 if let Some(text) = other.clone().into_string() {
8970 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::String(text)))
8971 } else {
8972 Err(Error::InvalidArgumentError(format!(
8973 "unsupported literal: {other:?}"
8974 )))
8975 }
8976 }
8977 }
8978}
8979
8980fn resolve_assignment_column_name(target: &AssignmentTarget) -> SqlResult<String> {
8981 match target {
8982 AssignmentTarget::ColumnName(name) => {
8983 if name.0.len() != 1 {
8984 return Err(Error::InvalidArgumentError(
8985 "qualified column names in UPDATE assignments are not supported yet".into(),
8986 ));
8987 }
8988 match &name.0[0] {
8989 ObjectNamePart::Identifier(ident) => Ok(ident.value.clone()),
8990 other => Err(Error::InvalidArgumentError(format!(
8991 "unsupported column reference in UPDATE assignment: {other:?}"
8992 ))),
8993 }
8994 }
8995 AssignmentTarget::Tuple(_) => Err(Error::InvalidArgumentError(
8996 "tuple assignments are not supported yet".into(),
8997 )),
8998 }
8999}
9000
9001fn arrow_type_from_sql(data_type: &SqlDataType) -> SqlResult<arrow::datatypes::DataType> {
9002 use arrow::datatypes::DataType;
9003 match data_type {
9004 SqlDataType::Int(_)
9005 | SqlDataType::Integer(_)
9006 | SqlDataType::BigInt(_)
9007 | SqlDataType::SmallInt(_)
9008 | SqlDataType::TinyInt(_) => Ok(DataType::Int64),
9009 SqlDataType::Float(_)
9010 | SqlDataType::Real
9011 | SqlDataType::Double(_)
9012 | SqlDataType::DoublePrecision => Ok(DataType::Float64),
9013 SqlDataType::Text
9014 | SqlDataType::String(_)
9015 | SqlDataType::Varchar(_)
9016 | SqlDataType::Char(_)
9017 | SqlDataType::Uuid => Ok(DataType::Utf8),
9018 SqlDataType::Date => Ok(DataType::Date32),
9019 SqlDataType::Decimal(_) | SqlDataType::Numeric(_) => Ok(DataType::Float64),
9020 SqlDataType::Boolean => Ok(DataType::Boolean),
9021 SqlDataType::Custom(name, args) => {
9022 if name.0.len() == 1
9023 && let ObjectNamePart::Identifier(ident) = &name.0[0]
9024 && ident.value.eq_ignore_ascii_case("row")
9025 {
9026 return row_type_to_arrow(data_type, args);
9027 }
9028 Err(Error::InvalidArgumentError(format!(
9029 "unsupported SQL data type: {data_type:?}"
9030 )))
9031 }
9032 other => Err(Error::InvalidArgumentError(format!(
9033 "unsupported SQL data type: {other:?}"
9034 ))),
9035 }
9036}
9037
9038fn row_type_to_arrow(
9039 data_type: &SqlDataType,
9040 tokens: &[String],
9041) -> SqlResult<arrow::datatypes::DataType> {
9042 use arrow::datatypes::{DataType, Field, FieldRef, Fields};
9043
9044 let row_str = data_type.to_string();
9045 if tokens.is_empty() {
9046 return Err(Error::InvalidArgumentError(
9047 "ROW type must define at least one field".into(),
9048 ));
9049 }
9050
9051 let dialect = GenericDialect {};
9052 let field_definitions = resolve_row_field_types(tokens, &dialect).map_err(|err| {
9053 Error::InvalidArgumentError(format!("unable to parse ROW type '{row_str}': {err}"))
9054 })?;
9055
9056 let mut fields: Vec<FieldRef> = Vec::with_capacity(field_definitions.len());
9057 for (field_name, field_type) in field_definitions {
9058 let arrow_field_type = arrow_type_from_sql(&field_type)?;
9059 fields.push(Arc::new(Field::new(field_name, arrow_field_type, true)));
9060 }
9061
9062 let struct_fields: Fields = fields.into();
9063 Ok(DataType::Struct(struct_fields))
9064}
9065
9066fn resolve_row_field_types(
9067 tokens: &[String],
9068 dialect: &GenericDialect,
9069) -> SqlResult<Vec<(String, SqlDataType)>> {
9070 if tokens.is_empty() {
9071 return Err(Error::InvalidArgumentError(
9072 "ROW type must define at least one field".into(),
9073 ));
9074 }
9075
9076 let mut start = 0;
9077 let mut end = tokens.len();
9078 if tokens[start] == "(" {
9079 if end == 0 || tokens[end - 1] != ")" {
9080 return Err(Error::InvalidArgumentError(
9081 "ROW type is missing closing ')'".into(),
9082 ));
9083 }
9084 start += 1;
9085 end -= 1;
9086 } else if tokens[end - 1] == ")" {
9087 return Err(Error::InvalidArgumentError(
9088 "ROW type contains unmatched ')'".into(),
9089 ));
9090 }
9091
9092 let slice = &tokens[start..end];
9093 if slice.is_empty() {
9094 return Err(Error::InvalidArgumentError(
9095 "ROW type did not provide any field definitions".into(),
9096 ));
9097 }
9098
9099 let mut fields = Vec::new();
9100 let mut index = 0;
9101
9102 while index < slice.len() {
9103 if slice[index] == "," {
9104 index += 1;
9105 continue;
9106 }
9107
9108 let field_name = normalize_row_field_name(&slice[index])?;
9109 index += 1;
9110
9111 if index >= slice.len() {
9112 return Err(Error::InvalidArgumentError(format!(
9113 "ROW field '{field_name}' is missing a type specification"
9114 )));
9115 }
9116
9117 let mut last_success: Option<(usize, SqlDataType)> = None;
9118 let mut type_end = index;
9119
9120 while type_end <= slice.len() {
9121 let candidate = slice[index..type_end].join(" ");
9122 if candidate.trim().is_empty() {
9123 type_end += 1;
9124 continue;
9125 }
9126
9127 if let Ok(parsed_type) = parse_sql_data_type(&candidate, dialect) {
9128 last_success = Some((type_end, parsed_type));
9129 }
9130
9131 if type_end == slice.len() {
9132 break;
9133 }
9134
9135 if slice[type_end] == "," && last_success.is_some() {
9136 break;
9137 }
9138
9139 type_end += 1;
9140 }
9141
9142 let Some((next_index, data_type)) = last_success else {
9143 return Err(Error::InvalidArgumentError(format!(
9144 "failed to parse ROW field type for '{field_name}'"
9145 )));
9146 };
9147
9148 fields.push((field_name, data_type));
9149 index = next_index;
9150
9151 if index < slice.len() && slice[index] == "," {
9152 index += 1;
9153 }
9154 }
9155
9156 if fields.is_empty() {
9157 return Err(Error::InvalidArgumentError(
9158 "ROW type did not provide any field definitions".into(),
9159 ));
9160 }
9161
9162 Ok(fields)
9163}
9164
9165fn parse_sql_with_recursion_limit(
9179 dialect: &GenericDialect,
9180 sql: &str,
9181) -> Result<Vec<Statement>, sqlparser::parser::ParserError> {
9182 Parser::new(dialect)
9183 .with_recursion_limit(PARSER_RECURSION_LIMIT)
9184 .try_with_sql(sql)?
9185 .parse_statements()
9186}
9187
9188fn normalize_row_field_name(raw: &str) -> SqlResult<String> {
9189 let trimmed = raw.trim();
9190 if trimmed.is_empty() {
9191 return Err(Error::InvalidArgumentError(
9192 "ROW field name must not be empty".into(),
9193 ));
9194 }
9195
9196 if let Some(stripped) = trimmed.strip_prefix('"') {
9197 let without_end = stripped.strip_suffix('"').ok_or_else(|| {
9198 Error::InvalidArgumentError(format!("unterminated quoted ROW field name: {trimmed}"))
9199 })?;
9200 let name = without_end.replace("\"\"", "\"");
9201 return Ok(name);
9202 }
9203
9204 Ok(trimmed.to_string())
9205}
9206
9207fn parse_sql_data_type(type_str: &str, dialect: &GenericDialect) -> SqlResult<SqlDataType> {
9208 let trimmed = type_str.trim();
9209 let sql = format!("CREATE TABLE __row(__field {trimmed});");
9210 let statements = parse_sql_with_recursion_limit(dialect, &sql).map_err(|err| {
9211 Error::InvalidArgumentError(format!("failed to parse ROW field type '{trimmed}': {err}"))
9212 })?;
9213
9214 let stmt = statements.into_iter().next().ok_or_else(|| {
9215 Error::InvalidArgumentError(format!(
9216 "ROW field type '{trimmed}' did not produce a statement"
9217 ))
9218 })?;
9219
9220 match stmt {
9221 Statement::CreateTable(table) => table
9222 .columns
9223 .first()
9224 .map(|col| col.data_type.clone())
9225 .ok_or_else(|| {
9226 Error::InvalidArgumentError(format!(
9227 "ROW field type '{trimmed}' missing column definition"
9228 ))
9229 }),
9230 other => Err(Error::InvalidArgumentError(format!(
9231 "unexpected statement while parsing ROW field type: {other:?}"
9232 ))),
9233 }
9234}
9235
9236type ExtractValuesResult = Option<(Vec<Vec<PlanValue>>, Vec<String>)>;
9239
9240#[allow(clippy::type_complexity)]
9241fn extract_values_from_derived_table(from: &[TableWithJoins]) -> SqlResult<ExtractValuesResult> {
9242 if from.len() != 1 {
9243 return Ok(None);
9244 }
9245
9246 let table_with_joins = &from[0];
9247 if !table_with_joins.joins.is_empty() {
9248 return Ok(None);
9249 }
9250
9251 match &table_with_joins.relation {
9252 TableFactor::Derived {
9253 subquery, alias, ..
9254 } => {
9255 let values = match subquery.body.as_ref() {
9257 SetExpr::Values(v) => v,
9258 _ => return Ok(None),
9259 };
9260
9261 let column_names = if let Some(alias) = alias {
9263 alias
9264 .columns
9265 .iter()
9266 .map(|col_def| col_def.name.value.clone())
9267 .collect::<Vec<_>>()
9268 } else {
9269 if values.rows.is_empty() {
9271 return Err(Error::InvalidArgumentError(
9272 "VALUES expression must have at least one row".into(),
9273 ));
9274 }
9275 let first_row = &values.rows[0];
9276 (0..first_row.len())
9277 .map(|i| format!("column{}", i))
9278 .collect()
9279 };
9280
9281 if values.rows.is_empty() {
9283 return Err(Error::InvalidArgumentError(
9284 "VALUES expression must have at least one row".into(),
9285 ));
9286 }
9287
9288 let mut rows = Vec::with_capacity(values.rows.len());
9289 for row in &values.rows {
9290 if row.len() != column_names.len() {
9291 return Err(Error::InvalidArgumentError(format!(
9292 "VALUES row has {} columns but table alias specifies {} columns",
9293 row.len(),
9294 column_names.len()
9295 )));
9296 }
9297
9298 let mut converted_row = Vec::with_capacity(row.len());
9299 for expr in row {
9300 let value = SqlValue::try_from_expr(expr)?;
9301 converted_row.push(PlanValue::from(value));
9302 }
9303 rows.push(converted_row);
9304 }
9305
9306 Ok(Some((rows, column_names)))
9307 }
9308 _ => Ok(None),
9309 }
9310}
9311
9312fn extract_constant_select_rows(select: &Select) -> SqlResult<Option<Vec<Vec<PlanValue>>>> {
9313 if !select.from.is_empty() {
9314 return Ok(None);
9315 }
9316
9317 if select.selection.is_some()
9318 || select.having.is_some()
9319 || !select.named_window.is_empty()
9320 || select.qualify.is_some()
9321 || select.distinct.is_some()
9322 || select.top.is_some()
9323 || select.into.is_some()
9324 || select.prewhere.is_some()
9325 || !select.lateral_views.is_empty()
9326 || select.value_table_mode.is_some()
9327 || !group_by_is_empty(&select.group_by)
9328 {
9329 return Err(Error::InvalidArgumentError(
9330 "constant SELECT statements do not support advanced clauses".into(),
9331 ));
9332 }
9333
9334 if select.projection.is_empty() {
9335 return Err(Error::InvalidArgumentError(
9336 "constant SELECT requires at least one projection".into(),
9337 ));
9338 }
9339
9340 let mut row: Vec<PlanValue> = Vec::with_capacity(select.projection.len());
9341 for item in &select.projection {
9342 let expr = match item {
9343 SelectItem::UnnamedExpr(expr) => expr,
9344 SelectItem::ExprWithAlias { expr, .. } => expr,
9345 other => {
9346 return Err(Error::InvalidArgumentError(format!(
9347 "unsupported projection in constant SELECT: {other:?}"
9348 )));
9349 }
9350 };
9351
9352 let value = SqlValue::try_from_expr(expr)?;
9353 row.push(PlanValue::from(value));
9354 }
9355
9356 Ok(Some(vec![row]))
9357}
9358
9359fn extract_single_table(from: &[TableWithJoins]) -> SqlResult<(String, String)> {
9360 if from.len() != 1 {
9361 return Err(Error::InvalidArgumentError(
9362 "queries over multiple tables are not supported yet".into(),
9363 ));
9364 }
9365 let item = &from[0];
9366
9367 if table_with_joins_has_join(item) {
9368 return Err(Error::InvalidArgumentError(
9369 "JOIN clauses are not supported yet".into(),
9370 ));
9371 }
9372 match &item.relation {
9373 TableFactor::Table { name, .. } => canonical_object_name(name),
9374 TableFactor::Derived { alias, .. } => {
9375 let table_name = alias
9378 .as_ref()
9379 .map(|a| a.name.value.clone())
9380 .unwrap_or_else(|| "derived".to_string());
9381 let canonical = table_name.to_ascii_lowercase();
9382 Ok((table_name, canonical))
9383 }
9384 TableFactor::NestedJoin { .. } => Err(Error::InvalidArgumentError(
9385 "JOIN clauses are not supported yet".into(),
9386 )),
9387 _ => Err(Error::InvalidArgumentError(
9388 "queries require a plain table name or derived table".into(),
9389 )),
9390 }
9391}
9392
9393fn table_with_joins_has_join(item: &TableWithJoins) -> bool {
9395 if !item.joins.is_empty() {
9396 return true;
9397 }
9398 match &item.relation {
9399 TableFactor::NestedJoin {
9400 table_with_joins, ..
9401 } => table_with_joins_has_join(table_with_joins.as_ref()),
9402 _ => false,
9403 }
9404}
9405
9406type ExtractedJoinData = (
9410 Vec<llkv_plan::TableRef>,
9411 Vec<llkv_plan::JoinMetadata>,
9412 Vec<Option<SqlExpr>>,
9413);
9414
9415fn extract_tables(from: &[TableWithJoins]) -> SqlResult<ExtractedJoinData> {
9420 let mut tables = Vec::new();
9421 let mut join_metadata = Vec::new();
9422 let mut join_filters = Vec::new();
9423
9424 for item in from {
9425 flatten_table_with_joins(item, &mut tables, &mut join_metadata, &mut join_filters)?;
9426 }
9427
9428 Ok((tables, join_metadata, join_filters))
9429}
9430
9431fn push_table_factor(
9432 factor: &TableFactor,
9433 tables: &mut Vec<llkv_plan::TableRef>,
9434 join_metadata: &mut Vec<llkv_plan::JoinMetadata>,
9435 join_filters: &mut Vec<Option<SqlExpr>>,
9436) -> SqlResult<()> {
9437 match factor {
9438 TableFactor::Table { name, alias, .. } => {
9439 let (schema_opt, table) = parse_schema_qualified_name(name)?;
9442 let schema = schema_opt.unwrap_or_default();
9443 let alias_name = alias.as_ref().map(|a| a.name.value.clone());
9444 tables.push(llkv_plan::TableRef::with_alias(schema, table, alias_name));
9445 Ok(())
9446 }
9447 TableFactor::NestedJoin {
9448 table_with_joins,
9449 alias,
9450 } => {
9451 if alias.is_some() {
9452 return Err(Error::InvalidArgumentError(
9453 "parenthesized JOINs with aliases are not supported yet".into(),
9454 ));
9455 }
9456 flatten_table_with_joins(
9457 table_with_joins.as_ref(),
9458 tables,
9459 join_metadata,
9460 join_filters,
9461 )
9462 }
9463 TableFactor::Derived { .. } => Err(Error::InvalidArgumentError(
9464 "JOIN clauses require base tables; derived tables are not supported".into(),
9465 )),
9466 _ => Err(Error::InvalidArgumentError(
9467 "queries require a plain table name".into(),
9468 )),
9469 }
9470}
9471
9472fn flatten_table_with_joins(
9473 item: &TableWithJoins,
9474 tables: &mut Vec<llkv_plan::TableRef>,
9475 join_metadata: &mut Vec<llkv_plan::JoinMetadata>,
9476 join_filters: &mut Vec<Option<SqlExpr>>,
9477) -> SqlResult<()> {
9478 push_table_factor(&item.relation, tables, join_metadata, join_filters)?;
9479
9480 for join in &item.joins {
9481 let left_table_index = tables.len() - 1;
9482
9483 match &join.join_operator {
9484 JoinOperator::CrossJoin(JoinConstraint::None)
9485 | JoinOperator::Join(JoinConstraint::None)
9486 | JoinOperator::Inner(JoinConstraint::None) => {
9487 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
9488 join_metadata.push(llkv_plan::JoinMetadata {
9489 left_table_index,
9490 join_type: llkv_plan::JoinPlan::Inner,
9491 on_condition: None,
9492 });
9493 join_filters.push(None);
9494 }
9495 JoinOperator::Join(JoinConstraint::On(condition))
9496 | JoinOperator::Inner(JoinConstraint::On(condition)) => {
9497 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
9498 join_filters.push(Some(condition.clone()));
9499 join_metadata.push(llkv_plan::JoinMetadata {
9500 left_table_index,
9501 join_type: llkv_plan::JoinPlan::Inner,
9502 on_condition: None,
9503 });
9504 }
9505 JoinOperator::Left(JoinConstraint::On(condition))
9506 | JoinOperator::LeftOuter(JoinConstraint::On(condition)) => {
9507 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
9508 join_filters.push(Some(condition.clone()));
9509 join_metadata.push(llkv_plan::JoinMetadata {
9510 left_table_index,
9511 join_type: llkv_plan::JoinPlan::Left,
9512 on_condition: None,
9513 });
9514 }
9515 JoinOperator::Left(JoinConstraint::None)
9516 | JoinOperator::LeftOuter(JoinConstraint::None) => {
9517 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
9518 join_metadata.push(llkv_plan::JoinMetadata {
9519 left_table_index,
9520 join_type: llkv_plan::JoinPlan::Left,
9521 on_condition: None,
9522 });
9523 join_filters.push(None);
9524 }
9525 JoinOperator::CrossJoin(_) => {
9526 return Err(Error::InvalidArgumentError(
9527 "CROSS JOIN with constraints is not supported".into(),
9528 ));
9529 }
9530 JoinOperator::Join(JoinConstraint::Using(_))
9531 | JoinOperator::Inner(JoinConstraint::Using(_))
9532 | JoinOperator::Left(JoinConstraint::Using(_))
9533 | JoinOperator::LeftOuter(JoinConstraint::Using(_)) => {
9534 return Err(Error::InvalidArgumentError(
9535 "JOIN ... USING (...) is not supported yet".into(),
9536 ));
9537 }
9538 JoinOperator::Join(JoinConstraint::Natural)
9539 | JoinOperator::Inner(JoinConstraint::Natural)
9540 | JoinOperator::Left(JoinConstraint::Natural)
9541 | JoinOperator::LeftOuter(JoinConstraint::Natural)
9542 | JoinOperator::Right(_)
9543 | JoinOperator::RightOuter(_)
9544 | JoinOperator::FullOuter(_)
9545 | JoinOperator::Semi(_)
9546 | JoinOperator::LeftSemi(_)
9547 | JoinOperator::LeftAnti(_)
9548 | JoinOperator::RightSemi(_)
9549 | JoinOperator::RightAnti(_)
9550 | JoinOperator::CrossApply
9551 | JoinOperator::OuterApply
9552 | JoinOperator::Anti(_)
9553 | JoinOperator::StraightJoin(_) => {
9554 return Err(Error::InvalidArgumentError(
9555 "only INNER JOIN and LEFT JOIN with optional ON constraints are supported"
9556 .into(),
9557 ));
9558 }
9559 other => {
9560 return Err(Error::InvalidArgumentError(format!(
9561 "unsupported JOIN clause: {other:?}"
9562 )));
9563 }
9564 }
9565 }
9566
9567 Ok(())
9568}
9569
9570fn group_by_is_empty(expr: &GroupByExpr) -> bool {
9571 matches!(
9572 expr,
9573 GroupByExpr::Expressions(exprs, modifiers)
9574 if exprs.is_empty() && modifiers.is_empty()
9575 )
9576}
9577
9578fn convert_value_table_mode(mode: sqlparser::ast::ValueTableMode) -> llkv_plan::ValueTableMode {
9579 use llkv_plan::ValueTableMode as PlanMode;
9580 match mode {
9581 sqlparser::ast::ValueTableMode::AsStruct => PlanMode::AsStruct,
9582 sqlparser::ast::ValueTableMode::AsValue => PlanMode::AsValue,
9583 sqlparser::ast::ValueTableMode::DistinctAsStruct => PlanMode::DistinctAsStruct,
9584 sqlparser::ast::ValueTableMode::DistinctAsValue => PlanMode::DistinctAsValue,
9585 }
9586}
9587#[cfg(test)]
9588mod tests {
9589 use super::*;
9590 use arrow::array::{Array, Float64Array, Int32Array, Int64Array, StringArray};
9591 use arrow::record_batch::RecordBatch;
9592 use llkv_storage::pager::MemPager;
9593
9594 fn extract_string_options(batches: &[RecordBatch]) -> Vec<Option<String>> {
9595 let mut values: Vec<Option<String>> = Vec::new();
9596 for batch in batches {
9597 let column = batch
9598 .column(0)
9599 .as_any()
9600 .downcast_ref::<StringArray>()
9601 .expect("string column");
9602 for idx in 0..column.len() {
9603 if column.is_null(idx) {
9604 values.push(None);
9605 } else {
9606 values.push(Some(column.value(idx).to_string()));
9607 }
9608 }
9609 }
9610 values
9611 }
9612
9613 #[test]
9614 fn test_insert_batching_across_calls() {
9615 let engine = SqlEngine::new(Arc::new(MemPager::default()));
9616
9617 engine.execute("CREATE TABLE test (id INTEGER)").unwrap();
9619
9620 engine.execute("INSERT INTO test VALUES (1)").unwrap();
9622 engine.execute("INSERT INTO test VALUES (2)").unwrap();
9623
9624 let result = engine.execute("SELECT * FROM test ORDER BY id").unwrap();
9626 let select_result = result
9627 .into_iter()
9628 .find_map(|res| match res {
9629 RuntimeStatementResult::Select { execution, .. } => {
9630 Some(execution.collect().unwrap())
9631 }
9632 _ => None,
9633 })
9634 .expect("expected SELECT result in response");
9635 let batches = select_result;
9636 assert_eq!(
9637 batches[0].num_rows(),
9638 2,
9639 "Should have 2 rows after cross-call batching"
9640 );
9641 }
9642
9643 #[test]
9644 fn create_insert_select_roundtrip() {
9645 let pager = Arc::new(MemPager::default());
9646 let engine = SqlEngine::new(pager);
9647
9648 let result = engine
9649 .execute("CREATE TABLE people (id INT NOT NULL, name TEXT NOT NULL)")
9650 .expect("create table");
9651 assert!(matches!(
9652 result[0],
9653 RuntimeStatementResult::CreateTable { .. }
9654 ));
9655
9656 let result = engine
9657 .execute("INSERT INTO people (id, name) VALUES (1, 'alice'), (2, 'bob')")
9658 .expect("insert rows");
9659 assert!(matches!(
9660 result[0],
9661 RuntimeStatementResult::Insert {
9662 rows_inserted: 2,
9663 ..
9664 }
9665 ));
9666
9667 let mut result = engine
9668 .execute("SELECT name FROM people WHERE id = 2")
9669 .expect("select rows");
9670 let select_result = result.remove(0);
9671 let batches = match select_result {
9672 RuntimeStatementResult::Select { execution, .. } => {
9673 execution.collect().expect("collect batches")
9674 }
9675 _ => panic!("expected select result"),
9676 };
9677 assert_eq!(batches.len(), 1);
9678 let column = batches[0]
9679 .column(0)
9680 .as_any()
9681 .downcast_ref::<StringArray>()
9682 .expect("string column");
9683 assert_eq!(column.len(), 1);
9684 assert_eq!(column.value(0), "bob");
9685 }
9686
9687 #[test]
9688 fn insert_select_constant_including_null() {
9689 let pager = Arc::new(MemPager::default());
9690 let engine = SqlEngine::new(pager);
9691
9692 engine
9693 .execute("CREATE TABLE integers(i INTEGER)")
9694 .expect("create table");
9695
9696 let result = engine
9697 .execute("INSERT INTO integers SELECT 42")
9698 .expect("insert literal");
9699 assert!(matches!(
9700 result[0],
9701 RuntimeStatementResult::Insert {
9702 rows_inserted: 1,
9703 ..
9704 }
9705 ));
9706
9707 let result = engine
9708 .execute("INSERT INTO integers SELECT CAST(NULL AS VARCHAR)")
9709 .expect("insert null literal");
9710 assert!(matches!(
9711 result[0],
9712 RuntimeStatementResult::Insert {
9713 rows_inserted: 1,
9714 ..
9715 }
9716 ));
9717
9718 let mut result = engine
9719 .execute("SELECT * FROM integers")
9720 .expect("select rows");
9721 let select_result = result.remove(0);
9722 let batches = match select_result {
9723 RuntimeStatementResult::Select { execution, .. } => {
9724 execution.collect().expect("collect batches")
9725 }
9726 _ => panic!("expected select result"),
9727 };
9728
9729 let mut values: Vec<Option<i64>> = Vec::new();
9730 for batch in &batches {
9731 let column = batch
9732 .column(0)
9733 .as_any()
9734 .downcast_ref::<Int64Array>()
9735 .expect("int column");
9736 for idx in 0..column.len() {
9737 if column.is_null(idx) {
9738 values.push(None);
9739 } else {
9740 values.push(Some(column.value(idx)));
9741 }
9742 }
9743 }
9744
9745 assert_eq!(values, vec![Some(42), None]);
9746 }
9747
9748 #[test]
9749 fn not_null_comparison_filters_all_rows() {
9750 let pager = Arc::new(MemPager::default());
9751 let engine = SqlEngine::new(pager);
9752
9753 engine
9754 .execute("CREATE TABLE single(col INTEGER)")
9755 .expect("create table");
9756 engine
9757 .execute("INSERT INTO single VALUES (1)")
9758 .expect("insert row");
9759
9760 let batches = engine
9761 .sql("SELECT * FROM single WHERE NOT ( NULL ) >= NULL")
9762 .expect("run constant null comparison");
9763
9764 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9765 assert_eq!(total_rows, 0, "expected filter to remove all rows");
9766 }
9767
9768 #[test]
9769 fn not_null_in_list_filters_all_rows() {
9770 let pager = Arc::new(MemPager::default());
9771 let engine = SqlEngine::new(pager);
9772
9773 engine
9774 .execute("CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
9775 .expect("create table");
9776 engine
9777 .execute("INSERT INTO tab0 VALUES (1, 2, 3)")
9778 .expect("insert row");
9779
9780 let batches = engine
9781 .sql("SELECT * FROM tab0 WHERE NOT ( NULL ) IN ( - col2 * + col2 )")
9782 .expect("run IN list null comparison");
9783
9784 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9785 assert_eq!(total_rows, 0, "expected IN list filter to remove all rows");
9786 }
9787
9788 #[test]
9789 fn empty_in_list_filters_all_rows() {
9790 let pager = Arc::new(MemPager::default());
9791 let engine = SqlEngine::new(pager);
9792
9793 engine
9794 .execute("CREATE TABLE test_table(col INTEGER)")
9795 .expect("create table");
9796 engine
9797 .execute("INSERT INTO test_table VALUES (1), (2), (3)")
9798 .expect("insert rows");
9799
9800 let batches = engine
9801 .sql("SELECT * FROM test_table WHERE col IN ()")
9802 .expect("run empty IN list");
9803
9804 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9805 assert_eq!(total_rows, 0, "expected empty IN list to filter all rows");
9806 }
9807
9808 #[test]
9809 fn empty_not_in_list_preserves_all_rows() {
9810 let pager = Arc::new(MemPager::default());
9811 let engine = SqlEngine::new(pager);
9812
9813 engine
9814 .execute("CREATE TABLE test_table(col INTEGER)")
9815 .expect("create table");
9816 engine
9817 .execute("INSERT INTO test_table VALUES (1), (2), (3)")
9818 .expect("insert rows");
9819
9820 let batches = engine
9821 .sql("SELECT * FROM test_table WHERE col NOT IN () ORDER BY col")
9822 .expect("run empty NOT IN list");
9823
9824 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9825 assert_eq!(
9826 total_rows, 3,
9827 "expected empty NOT IN list to preserve all rows"
9828 );
9829
9830 let mut values: Vec<i64> = Vec::new();
9831 for batch in &batches {
9832 let column = batch
9833 .column(0)
9834 .as_any()
9835 .downcast_ref::<Int64Array>()
9836 .expect("int column");
9837 for idx in 0..column.len() {
9838 if !column.is_null(idx) {
9839 values.push(column.value(idx));
9840 }
9841 }
9842 }
9843
9844 assert_eq!(values, vec![1, 2, 3]);
9845 }
9846
9847 #[test]
9848 fn empty_in_list_with_constant_expression() {
9849 let pager = Arc::new(MemPager::default());
9850 let engine = SqlEngine::new(pager);
9851
9852 let batches = engine
9853 .sql("SELECT 1 IN ()")
9854 .expect("run constant empty IN list");
9855
9856 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9857 assert_eq!(total_rows, 1, "expected one result row");
9858
9859 let value = batches[0]
9860 .column(0)
9861 .as_any()
9862 .downcast_ref::<Int64Array>()
9863 .expect("int column")
9864 .value(0);
9865
9866 assert_eq!(value, 0, "expected 1 IN () to evaluate to 0 (false)");
9867 }
9868
9869 #[test]
9870 fn empty_not_in_list_with_constant_expression() {
9871 let pager = Arc::new(MemPager::default());
9872 let engine = SqlEngine::new(pager);
9873
9874 let batches = engine
9875 .sql("SELECT 1 NOT IN ()")
9876 .expect("run constant empty NOT IN list");
9877
9878 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9879 assert_eq!(total_rows, 1, "expected one result row");
9880
9881 let value = batches[0]
9882 .column(0)
9883 .as_any()
9884 .downcast_ref::<Int64Array>()
9885 .expect("int column")
9886 .value(0);
9887
9888 assert_eq!(value, 1, "expected 1 NOT IN () to evaluate to 1 (true)");
9889 }
9890
9891 #[test]
9892 fn not_in_with_cast_preserves_rows_for_self_comparison() {
9893 let pager = Arc::new(MemPager::default());
9894 let engine = SqlEngine::new(pager);
9895
9896 engine
9897 .execute("CREATE TABLE tab2(col1 INTEGER, col2 INTEGER)")
9898 .expect("create tab2");
9899 engine
9900 .execute("INSERT INTO tab2 VALUES (51, 51), (67, 67), (77, 77)")
9901 .expect("seed tab2");
9902
9903 let batches = engine
9904 .sql(
9905 "SELECT col1 FROM tab2 WHERE NOT col2 NOT IN ( + CAST ( + + col2 AS REAL ) ) ORDER BY col1",
9906 )
9907 .expect("run NOT IN self comparison query");
9908
9909 let mut values: Vec<i64> = Vec::new();
9910 for batch in &batches {
9911 let column = batch
9912 .column(0)
9913 .as_any()
9914 .downcast_ref::<Int64Array>()
9915 .expect("int column");
9916 for idx in 0..column.len() {
9917 if !column.is_null(idx) {
9918 values.push(column.value(idx));
9919 }
9920 }
9921 }
9922
9923 assert_eq!(values, vec![51, 67, 77]);
9924 }
9925
9926 #[test]
9927 fn cross_join_not_null_comparison_filters_all_rows() {
9928 let pager = Arc::new(MemPager::default());
9929 let engine = SqlEngine::new(pager);
9930
9931 engine
9932 .execute("CREATE TABLE left_side(col INTEGER)")
9933 .expect("create left table");
9934 engine
9935 .execute("CREATE TABLE right_side(col INTEGER)")
9936 .expect("create right table");
9937 engine
9938 .execute("INSERT INTO left_side VALUES (1)")
9939 .expect("insert left row");
9940 engine
9941 .execute("INSERT INTO right_side VALUES (2)")
9942 .expect("insert right row");
9943
9944 let batches = engine
9945 .sql("SELECT * FROM left_side CROSS JOIN right_side WHERE NOT ( NULL ) >= NULL")
9946 .expect("run cross join null comparison");
9947
9948 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
9949 assert_eq!(
9950 total_rows, 0,
9951 "expected cross join filter to remove all rows"
9952 );
9953 }
9954
9955 #[test]
9956 fn not_between_null_bounds_matches_sqlite_behavior() {
9957 let pager = Arc::new(MemPager::default());
9958 let engine = SqlEngine::new(pager);
9959
9960 engine
9961 .execute("CREATE TABLE tab2(col1 INTEGER, col2 INTEGER)")
9962 .expect("create tab2");
9963 engine
9964 .execute("INSERT INTO tab2 VALUES (1, 2), (-5, 7), (NULL, 11)")
9965 .expect("seed rows");
9966
9967 let batches = engine
9968 .sql(
9969 "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )",
9970 )
9971 .expect("run NOT BETWEEN query with NULL bounds");
9972
9973 let mut values: Vec<i64> = Vec::new();
9974 for batch in &batches {
9975 let column = batch.column(0);
9976 match column.data_type() {
9977 arrow::datatypes::DataType::Int64 => {
9978 let array = column
9979 .as_any()
9980 .downcast_ref::<Int64Array>()
9981 .expect("int64 column");
9982 for idx in 0..array.len() {
9983 if !array.is_null(idx) {
9984 values.push(array.value(idx));
9985 }
9986 }
9987 }
9988 arrow::datatypes::DataType::Int32 => {
9989 let array = column
9990 .as_any()
9991 .downcast_ref::<Int32Array>()
9992 .expect("int32 column");
9993 for idx in 0..array.len() {
9994 if !array.is_null(idx) {
9995 values.push(array.value(idx) as i64);
9996 }
9997 }
9998 }
9999 other => panic!("unexpected data type: {other:?}"),
10000 }
10001 }
10002
10003 values.sort_unstable();
10004 assert_eq!(values, vec![-7, -2]);
10005 }
10006
10007 #[test]
10008 fn not_chain_precedence_matches_sqlite_behavior() {
10009 let pager = Arc::new(MemPager::default());
10010 let engine = SqlEngine::new(pager);
10011
10012 engine
10013 .execute("CREATE TABLE tab1(col0 INTEGER)")
10014 .expect("create tab1");
10015 engine
10016 .execute("INSERT INTO tab1 VALUES (1), (2)")
10017 .expect("seed tab1");
10018
10019 use sqlparser::ast::Statement;
10020 use sqlparser::dialect::SQLiteDialect;
10021 use sqlparser::parser::Parser;
10022
10023 let dialect = SQLiteDialect {};
10024 let mut statements = Parser::parse_sql(
10025 &dialect,
10026 "SELECT DISTINCT 85 AS value FROM tab1 WHERE NOT + 84 < - + 69 GROUP BY col0, col0",
10027 )
10028 .expect("parse sql");
10029 let statement = statements.pop().expect("expected single statement");
10030 let Statement::Query(query_ast) = statement else {
10031 panic!("expected SELECT query");
10032 };
10033 let plan = engine
10034 .build_select_plan(*query_ast)
10035 .expect("build select plan");
10036 let filter_expr = plan.filter.expect("expected filter predicate").predicate;
10037 if let llkv_expr::expr::Expr::Not(inner) = &filter_expr {
10038 if !matches!(inner.as_ref(), llkv_expr::expr::Expr::Compare { .. }) {
10039 panic!("expected NOT to wrap comparison, got: {inner:?}");
10040 }
10041 } else {
10042 panic!("expected filter to be NOT-wrapped comparison: {filter_expr:?}");
10043 }
10044
10045 let batches = engine
10046 .sql(
10047 "SELECT DISTINCT 85 AS value FROM tab1 WHERE NOT + 84 < - + 69 GROUP BY col0, col0",
10048 )
10049 .expect("run NOT precedence query");
10050
10051 let mut values: Vec<i64> = Vec::new();
10052 for batch in &batches {
10053 let column = batch.column(0);
10054 match column.data_type() {
10055 arrow::datatypes::DataType::Int64 => {
10056 let array = column
10057 .as_any()
10058 .downcast_ref::<Int64Array>()
10059 .expect("int64 column");
10060 for idx in 0..array.len() {
10061 if !array.is_null(idx) {
10062 values.push(array.value(idx));
10063 }
10064 }
10065 }
10066 arrow::datatypes::DataType::Int32 => {
10067 let array = column
10068 .as_any()
10069 .downcast_ref::<Int32Array>()
10070 .expect("int32 column");
10071 for idx in 0..array.len() {
10072 if !array.is_null(idx) {
10073 values.push(array.value(idx) as i64);
10074 }
10075 }
10076 }
10077 arrow::datatypes::DataType::Float64 => {
10078 let array = column
10079 .as_any()
10080 .downcast_ref::<Float64Array>()
10081 .expect("float64 column");
10082 for idx in 0..array.len() {
10083 if !array.is_null(idx) {
10084 values.push(array.value(idx) as i64);
10085 }
10086 }
10087 }
10088 other => panic!("unexpected data type: {other:?}"),
10089 }
10090 }
10091
10092 values.sort_unstable();
10093 assert_eq!(values, vec![85]);
10094 }
10095
10096 #[test]
10097 fn not_between_null_bounds_matches_harness_fixture() {
10098 let pager = Arc::new(MemPager::default());
10099 let engine = SqlEngine::new(pager);
10100
10101 engine
10102 .execute("CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
10103 .expect("create tab2");
10104 engine
10105 .execute("INSERT INTO tab2 VALUES (7, 31, 27), (79, 17, 38), (78, 59, 26)")
10106 .expect("seed rows");
10107
10108 let batches = engine
10109 .sql(
10110 "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )",
10111 )
10112 .expect("run harness-matched NOT BETWEEN query");
10113
10114 let mut values: Vec<i64> = Vec::new();
10115 for batch in &batches {
10116 let column = batch
10117 .column(0)
10118 .as_any()
10119 .downcast_ref::<Int64Array>()
10120 .expect("integer column");
10121 for idx in 0..column.len() {
10122 if !column.is_null(idx) {
10123 values.push(column.value(idx));
10124 }
10125 }
10126 }
10127
10128 values.sort_unstable();
10129 assert_eq!(values, vec![-38, -27, -26]);
10130 }
10131
10132 #[test]
10133 fn not_between_null_bounds_parser_negated_flag() {
10134 use sqlparser::ast::{Expr as SqlExprAst, Statement};
10135 use sqlparser::dialect::SQLiteDialect;
10136 use sqlparser::parser::Parser;
10137
10138 let dialect = SQLiteDialect {};
10139 let sql = "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )";
10140
10141 let mut statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
10142 let statement = statements.pop().expect("expected single statement");
10143 let Statement::Query(query) = statement else {
10144 panic!("expected SELECT query");
10145 };
10146 let select = query.body.as_select().expect("expected SELECT body");
10147 let where_expr = select.selection.as_ref().expect("expected WHERE clause");
10148
10149 match where_expr {
10150 SqlExprAst::UnaryOp {
10151 op: sqlparser::ast::UnaryOperator::Not,
10152 expr,
10153 } => match expr.as_ref() {
10154 SqlExprAst::Between { negated, .. } => {
10155 assert!(
10156 !negated,
10157 "expected BETWEEN parser to treat leading NOT as part of expression"
10158 );
10159 }
10160 other => panic!("unexpected inner expression: {other:?}"),
10161 },
10162 other => panic!("unexpected where expression: {other:?}"),
10163 }
10164 }
10165
10166 #[test]
10167 fn double_negated_between_null_bounds_filters_all_rows() {
10168 let pager = Arc::new(MemPager::default());
10169 let engine = SqlEngine::new(pager);
10170
10171 engine
10172 .execute("CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
10173 .expect("create tab2");
10174 engine
10175 .execute("INSERT INTO tab2 VALUES (1, 2, 3), (-2, -13, 19), (NULL, 5, 7)")
10176 .expect("seed rows");
10177
10178 let batches = engine
10179 .sql(
10180 "SELECT - col1 * + col2 FROM tab2 WHERE NOT ( col1 ) NOT BETWEEN ( NULL ) AND ( col0 )",
10181 )
10182 .expect("run double NOT BETWEEN query with NULL bounds");
10183
10184 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
10185 assert_eq!(
10186 total_rows, 0,
10187 "expected double NOT BETWEEN to filter all rows"
10188 );
10189 }
10190
10191 #[test]
10192 fn not_scalar_less_than_null_filters_all_rows() {
10193 let pager = Arc::new(MemPager::default());
10194 let engine = SqlEngine::new(pager);
10195
10196 engine
10197 .execute("CREATE TABLE tab(col0 INTEGER, col2 INTEGER)")
10198 .expect("create tab");
10199 engine
10200 .execute("INSERT INTO tab VALUES (1, 2), (5, 10), (-3, 7)")
10201 .expect("seed rows");
10202
10203 let batches = engine
10204 .sql("SELECT col0 FROM tab WHERE NOT ( - col0 / - col2 + - col0 ) < NULL")
10205 .expect("run NOT < NULL query");
10206
10207 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
10208 assert_eq!(total_rows, 0, "expected NOT < NULL to filter all rows");
10209 }
10210
10211 #[test]
10212 fn left_join_not_is_not_null_on_literal_flips_to_is_null() {
10213 use sqlparser::ast::Statement;
10214 use sqlparser::dialect::SQLiteDialect;
10215 use sqlparser::parser::Parser;
10216
10217 let pager = Arc::new(MemPager::default());
10218 let engine = SqlEngine::new(pager);
10219
10220 engine
10221 .execute(
10222 "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER);\
10223 CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
10224 )
10225 .expect("create tables");
10226
10227 let sql = "SELECT DISTINCT * FROM tab1 AS cor0 LEFT JOIN tab1 AS cor1 ON NOT 86 IS NOT NULL, tab0 AS cor2";
10228 let dialect = SQLiteDialect {};
10229 let mut statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
10230 let statement = statements.pop().expect("expected statement");
10231 let Statement::Query(query) = statement else {
10232 panic!("expected SELECT query");
10233 };
10234
10235 let plan = engine.build_select_plan(*query).expect("build select plan");
10236
10237 assert_eq!(plan.joins.len(), 1, "expected single explicit join entry");
10238
10239 let left_join = &plan.joins[0];
10240 let on_condition = left_join
10241 .on_condition
10242 .as_ref()
10243 .expect("left join should preserve ON predicate");
10244
10245 match on_condition {
10246 llkv_expr::expr::Expr::IsNull { expr, negated } => {
10247 assert!(!negated, "expected NOT to flip into IS NULL");
10248 assert!(matches!(
10249 expr,
10250 llkv_expr::expr::ScalarExpr::Literal(llkv_expr::literal::Literal::Integer(86))
10251 ));
10252 }
10253 other => panic!("unexpected ON predicate: {other:?}"),
10254 }
10255 }
10256
10257 #[test]
10258 fn left_join_constant_false_preserves_left_rows_with_null_right() {
10259 let pager = Arc::new(MemPager::default());
10260 let engine = SqlEngine::new(pager);
10261
10262 engine
10263 .execute(
10264 "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER);\
10265 CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
10266 )
10267 .expect("create tables");
10268
10269 engine
10270 .execute(
10271 "INSERT INTO tab0 VALUES (1, 2, 3), (4, 5, 6);\
10272 INSERT INTO tab1 VALUES (10, 11, 12), (13, 14, 15);",
10273 )
10274 .expect("seed rows");
10275
10276 let batches = engine
10277 .sql(
10278 "SELECT * FROM tab1 AS cor0 LEFT JOIN tab1 AS cor1 ON NOT 86 IS NOT NULL, tab0 AS cor2 ORDER BY cor0.col0, cor2.col0",
10279 )
10280 .expect("execute join query");
10281
10282 let mut total_rows = 0;
10283 for batch in &batches {
10284 total_rows += batch.num_rows();
10285
10286 for row_idx in 0..batch.num_rows() {
10288 for col_idx in 3..6 {
10289 assert!(
10290 batch.column(col_idx).is_null(row_idx),
10291 "expected right table column {} to be NULL in row {}",
10292 col_idx,
10293 row_idx
10294 );
10295 }
10296 }
10297 }
10298
10299 assert_eq!(total_rows, 4, "expected Cartesian product with tab0 only");
10301 }
10302
10303 #[test]
10304 fn cross_join_duplicate_table_name_resolves_columns() {
10305 let pager = Arc::new(MemPager::default());
10306 let engine = SqlEngine::new(pager);
10307
10308 use sqlparser::ast::{SetExpr, Statement};
10309 use sqlparser::dialect::SQLiteDialect;
10310 use sqlparser::parser::Parser;
10311
10312 engine
10313 .execute("CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
10314 .expect("create tab1");
10315 engine
10316 .execute("INSERT INTO tab1 VALUES (7, 8, 9)")
10317 .expect("insert tab1 row");
10318
10319 let dialect = SQLiteDialect {};
10320 let ast = Parser::parse_sql(
10321 &dialect,
10322 "SELECT tab1.col2 FROM tab1 AS cor0 CROSS JOIN tab1",
10323 )
10324 .expect("parse cross join query");
10325 let Statement::Query(query) = &ast[0] else {
10326 panic!("expected SELECT query");
10327 };
10328 let select = match query.body.as_ref() {
10329 SetExpr::Select(select) => select.as_ref(),
10330 other => panic!("unexpected query body: {other:?}"),
10331 };
10332 assert_eq!(select.from.len(), 1);
10333 assert!(!select.from[0].joins.is_empty());
10334
10335 let batches = engine
10336 .sql("SELECT tab1.col2 FROM tab1 AS cor0 CROSS JOIN tab1")
10337 .expect("run cross join with alias and base table");
10338
10339 let mut values = Vec::new();
10340 for batch in &batches {
10341 let column = batch
10342 .column(0)
10343 .as_any()
10344 .downcast_ref::<Int64Array>()
10345 .expect("int64 column");
10346 for idx in 0..column.len() {
10347 if !column.is_null(idx) {
10348 values.push(column.value(idx));
10349 }
10350 }
10351 }
10352 assert_eq!(values, vec![9]);
10353
10354 engine
10355 .execute("CREATE TABLE strings(a TEXT)")
10356 .expect("create table");
10357
10358 engine
10359 .execute("INSERT INTO strings VALUES ('3'), ('4'), (NULL)")
10360 .expect("insert seed rows");
10361
10362 let result = engine
10363 .execute("UPDATE strings SET a = 13 WHERE a = '3'")
10364 .expect("update rows");
10365 assert!(matches!(
10366 result[0],
10367 RuntimeStatementResult::Update {
10368 rows_updated: 1,
10369 ..
10370 }
10371 ));
10372
10373 let mut result = engine
10374 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
10375 .expect("select rows");
10376 let select_result = result.remove(0);
10377 let batches = match select_result {
10378 RuntimeStatementResult::Select { execution, .. } => {
10379 execution.collect().expect("collect batches")
10380 }
10381 _ => panic!("expected select result"),
10382 };
10383
10384 let mut values: Vec<Option<String>> = Vec::new();
10385 for batch in &batches {
10386 let column = batch
10387 .column(0)
10388 .as_any()
10389 .downcast_ref::<StringArray>()
10390 .expect("string column");
10391 for idx in 0..column.len() {
10392 if column.is_null(idx) {
10393 values.push(None);
10394 } else {
10395 values.push(Some(column.value(idx).to_string()));
10396 }
10397 }
10398 }
10399
10400 values.sort_by(|a, b| match (a, b) {
10401 (None, None) => std::cmp::Ordering::Equal,
10402 (None, Some(_)) => std::cmp::Ordering::Less,
10403 (Some(_), None) => std::cmp::Ordering::Greater,
10404 (Some(av), Some(bv)) => {
10405 let a_val = av.parse::<i64>().unwrap_or_default();
10406 let b_val = bv.parse::<i64>().unwrap_or_default();
10407 a_val.cmp(&b_val)
10408 }
10409 });
10410
10411 assert_eq!(
10412 values,
10413 vec![None, Some("4".to_string()), Some("13".to_string())]
10414 );
10415 }
10416
10417 #[test]
10418 fn order_by_honors_configured_default_null_order() {
10419 let pager = Arc::new(MemPager::default());
10420 let engine = SqlEngine::new(pager);
10421
10422 engine
10423 .execute("CREATE TABLE strings(a VARCHAR)")
10424 .expect("create table");
10425 engine
10426 .execute("INSERT INTO strings VALUES ('3'), ('4'), (NULL)")
10427 .expect("insert values");
10428 engine
10429 .execute("UPDATE strings SET a = 13 WHERE a = '3'")
10430 .expect("update value");
10431
10432 let mut result = engine
10433 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
10434 .expect("select rows");
10435 let select_result = result.remove(0);
10436 let batches = match select_result {
10437 RuntimeStatementResult::Select { execution, .. } => {
10438 execution.collect().expect("collect batches")
10439 }
10440 _ => panic!("expected select result"),
10441 };
10442
10443 let values = extract_string_options(&batches);
10444 assert_eq!(
10445 values,
10446 vec![Some("4".to_string()), Some("13".to_string()), None]
10447 );
10448
10449 assert!(!engine.default_nulls_first_for_tests());
10450
10451 engine
10452 .execute("SET default_null_order='nulls_first'")
10453 .expect("set default null order");
10454
10455 assert!(engine.default_nulls_first_for_tests());
10456
10457 let mut result = engine
10458 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
10459 .expect("select rows");
10460 let select_result = result.remove(0);
10461 let batches = match select_result {
10462 RuntimeStatementResult::Select { execution, .. } => {
10463 execution.collect().expect("collect batches")
10464 }
10465 _ => panic!("expected select result"),
10466 };
10467
10468 let values = extract_string_options(&batches);
10469 assert_eq!(
10470 values,
10471 vec![None, Some("4".to_string()), Some("13".to_string())]
10472 );
10473 }
10474
10475 #[test]
10476 fn arrow_type_from_row_returns_struct_fields() {
10477 let dialect = GenericDialect {};
10478 let statements = parse_sql_with_recursion_limit(
10479 &dialect,
10480 "CREATE TABLE row_types(payload ROW(a INTEGER, b VARCHAR));",
10481 )
10482 .expect("parse ROW type definition");
10483
10484 let data_type = match &statements[0] {
10485 Statement::CreateTable(stmt) => stmt.columns[0].data_type.clone(),
10486 other => panic!("unexpected statement: {other:?}"),
10487 };
10488
10489 let arrow_type = arrow_type_from_sql(&data_type).expect("convert ROW type");
10490 match arrow_type {
10491 arrow::datatypes::DataType::Struct(fields) => {
10492 assert_eq!(fields.len(), 2, "unexpected field count");
10493 assert_eq!(fields[0].name(), "a");
10494 assert_eq!(fields[1].name(), "b");
10495 assert_eq!(fields[0].data_type(), &arrow::datatypes::DataType::Int64);
10496 assert_eq!(fields[1].data_type(), &arrow::datatypes::DataType::Utf8);
10497 }
10498 other => panic!("expected struct type, got {other:?}"),
10499 }
10500 }
10501}