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