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