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
8748fn normalize_between_operand(operand: &SqlExpr, negated: bool) -> (bool, &SqlExpr) {
8749 let mut effective_negated = negated;
8750 let mut current = operand;
8751 loop {
8752 current = strip_sql_expr_nesting(current);
8753 match current {
8754 SqlExpr::UnaryOp {
8755 op: UnaryOperator::Not,
8756 expr,
8757 } => {
8758 effective_negated = !effective_negated;
8759 current = expr;
8760 }
8761 _ => return (effective_negated, current),
8762 }
8763 }
8764}
8765
8766fn peel_not_chain(expr: &SqlExpr) -> (usize, &SqlExpr) {
8767 let mut count = 0;
8768 let mut current = expr;
8769 loop {
8770 current = strip_sql_expr_nesting(current);
8771 match current {
8772 SqlExpr::UnaryOp {
8773 op: UnaryOperator::Not,
8774 expr,
8775 } => {
8776 count += 1;
8777 current = expr;
8778 }
8779 _ => return (count, current),
8780 }
8781 }
8782}
8783
8784struct BetweenBounds<'a> {
8785 lower: &'a SqlExpr,
8786 upper: &'a SqlExpr,
8787}
8788
8789#[allow(clippy::too_many_arguments)] fn translate_between_expr(
8791 engine: &SqlEngine,
8792 resolver: &IdentifierResolver<'_>,
8793 context: IdentifierContext,
8794 between_expr: &SqlExpr,
8795 bounds: BetweenBounds<'_>,
8796 negated: bool,
8797 outer_scopes: &[IdentifierContext],
8798 scalar_subqueries: &mut Vec<llkv_plan::ScalarSubquery>,
8799 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
8800) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
8801 let lower_op = if negated {
8802 BinaryOperator::Lt
8803 } else {
8804 BinaryOperator::GtEq
8805 };
8806 let upper_op = if negated {
8807 BinaryOperator::Gt
8808 } else {
8809 BinaryOperator::LtEq
8810 };
8811
8812 let lower_bound = translate_comparison_with_context(
8813 engine,
8814 resolver,
8815 context.clone(),
8816 between_expr,
8817 lower_op,
8818 bounds.lower,
8819 outer_scopes,
8820 scalar_subqueries,
8821 correlated_tracker.reborrow(),
8822 )?;
8823 let upper_bound = translate_comparison_with_context(
8824 engine,
8825 resolver,
8826 context,
8827 between_expr,
8828 upper_op,
8829 bounds.upper,
8830 outer_scopes,
8831 scalar_subqueries,
8832 correlated_tracker,
8833 )?;
8834
8835 if negated {
8836 Ok(llkv_expr::expr::Expr::Or(vec![lower_bound, upper_bound]))
8837 } else {
8838 Ok(llkv_expr::expr::Expr::And(vec![lower_bound, upper_bound]))
8839 }
8840}
8841
8842#[allow(clippy::too_many_arguments)] fn translate_like_expr(
8844 engine: &SqlEngine,
8845 resolver: &IdentifierResolver<'_>,
8846 context: IdentifierContext,
8847 like_expr: &SqlExpr,
8848 pattern_expr: &SqlExpr,
8849 negated: bool,
8850 outer_scopes: &[IdentifierContext],
8851 scalar_subqueries: &mut Vec<llkv_plan::ScalarSubquery>,
8852 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
8853) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
8854 let target_scalar = translate_scalar_with_context_scoped(
8856 engine,
8857 resolver,
8858 context,
8859 like_expr,
8860 outer_scopes,
8861 correlated_tracker.reborrow(),
8862 Some(&mut *scalar_subqueries),
8863 )?;
8864
8865 let llkv_expr::expr::ScalarExpr::Column(column) = target_scalar else {
8866 return Err(Error::InvalidArgumentError(
8867 "LIKE expression target must be a column reference".into(),
8868 ));
8869 };
8870
8871 let SqlExpr::Value(pattern_value) = pattern_expr else {
8873 return Err(Error::InvalidArgumentError(
8874 "LIKE pattern must be a string literal".into(),
8875 ));
8876 };
8877
8878 let (Value::SingleQuotedString(pattern_str) | Value::DoubleQuotedString(pattern_str)) =
8879 &pattern_value.value
8880 else {
8881 return Err(Error::InvalidArgumentError(
8882 "LIKE pattern must be a quoted string".into(),
8883 ));
8884 };
8885
8886 let operator = if let Some(optimized_op) = try_optimize_like_pattern(pattern_str) {
8888 optimized_op
8889 } else {
8890 return Err(Error::InvalidArgumentError(format!(
8892 "LIKE pattern '{}' requires full wildcard support (not yet implemented)",
8893 pattern_str
8894 )));
8895 };
8896
8897 let filter = llkv_expr::expr::Filter {
8898 field_id: column,
8899 op: operator,
8900 };
8901
8902 let result = llkv_expr::expr::Expr::Pred(filter);
8903
8904 if negated {
8905 Ok(llkv_expr::expr::Expr::not(result))
8906 } else {
8907 Ok(result)
8908 }
8909}
8910
8911fn try_optimize_like_pattern(pattern: &str) -> Option<llkv_expr::expr::Operator<'static>> {
8921 let bytes = pattern.as_bytes();
8922
8923 if pattern.contains('_') {
8925 return None;
8926 }
8927
8928 let percent_count = pattern.chars().filter(|&c| c == '%').count();
8930
8931 match percent_count {
8932 0 => {
8933 Some(llkv_expr::expr::Operator::Equals(Literal::from(pattern)))
8935 }
8936 1 => {
8937 if bytes.first() == Some(&b'%') {
8938 let suffix = pattern[1..].to_string();
8940 Some(llkv_expr::expr::Operator::EndsWith {
8941 pattern: suffix,
8942 case_sensitive: true,
8943 })
8944 } else if bytes.last() == Some(&b'%') {
8945 let prefix = pattern[..pattern.len() - 1].to_string();
8947 Some(llkv_expr::expr::Operator::StartsWith {
8948 pattern: prefix,
8949 case_sensitive: true,
8950 })
8951 } else {
8952 None
8954 }
8955 }
8956 2 => {
8957 if bytes.first() == Some(&b'%') && bytes.last() == Some(&b'%') {
8958 let substring = pattern[1..pattern.len() - 1].to_string();
8960 Some(llkv_expr::expr::Operator::Contains {
8961 pattern: substring,
8962 case_sensitive: true,
8963 })
8964 } else {
8965 None
8967 }
8968 }
8969 _ => {
8970 None
8972 }
8973 }
8974}
8975
8976fn correlated_scalar_from_resolution(
8977 placeholder: String,
8978 resolution: &ColumnResolution,
8979) -> llkv_expr::expr::ScalarExpr<String> {
8980 let mut expr = llkv_expr::expr::ScalarExpr::column(placeholder);
8981 for field in resolution.field_path() {
8982 expr = llkv_expr::expr::ScalarExpr::get_field(expr, field.clone());
8983 }
8984 expr
8985}
8986
8987fn resolve_correlated_identifier(
8988 resolver: &IdentifierResolver<'_>,
8989 parts: &[String],
8990 outer_scopes: &[IdentifierContext],
8991 mut tracker: SubqueryCorrelatedTracker<'_>,
8992) -> SqlResult<Option<llkv_expr::expr::ScalarExpr<String>>> {
8993 if !tracker.is_active() {
8994 return Ok(None);
8995 }
8996
8997 for scope in outer_scopes.iter().rev() {
8998 match resolver.resolve(parts, scope.clone()) {
8999 Ok(resolution) => {
9000 if let Some(placeholder) = tracker.placeholder_for_resolution(&resolution) {
9001 let expr = correlated_scalar_from_resolution(placeholder, &resolution);
9002 return Ok(Some(expr));
9003 }
9004 }
9005 Err(_) => continue,
9006 }
9007 }
9008
9009 Ok(None)
9010}
9011
9012fn resolve_identifier_expr(
9013 resolver: &IdentifierResolver<'_>,
9014 context: &IdentifierContext,
9015 parts: Vec<String>,
9016 outer_scopes: &[IdentifierContext],
9017 tracker: SubqueryCorrelatedTracker<'_>,
9018) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
9019 match resolver.resolve(&parts, context.clone()) {
9020 Ok(resolution) => {
9021 let use_scope_check =
9022 context.default_table_id().is_none() && context.tracks_available_columns();
9023 if use_scope_check && !context.has_available_column_for(&parts) {
9024 if let Some(expr) =
9025 resolve_correlated_identifier(resolver, &parts, outer_scopes, tracker)?
9026 {
9027 return Ok(expr);
9028 } else {
9029 return Err(Error::InvalidArgumentError(format!(
9030 "Binder Error: does not have a column named '{}'",
9031 parts.join(".")
9032 )));
9033 }
9034 }
9035 Ok(resolution.into_scalar_expr())
9036 }
9037 Err(err) => {
9038 if let Some(expr) =
9039 resolve_correlated_identifier(resolver, &parts, outer_scopes, tracker)?
9040 {
9041 Ok(expr)
9042 } else {
9043 Err(err)
9044 }
9045 }
9046 }
9047}
9048
9049#[allow(clippy::too_many_arguments)] fn translate_condition_with_context(
9051 engine: &SqlEngine,
9052 resolver: &IdentifierResolver<'_>,
9053 context: IdentifierContext,
9054 expr: &SqlExpr,
9055 outer_scopes: &[IdentifierContext],
9056 subqueries: &mut Vec<llkv_plan::FilterSubquery>,
9057 scalar_subqueries: &mut Vec<llkv_plan::ScalarSubquery>,
9058 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
9059) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
9060 enum ConditionExitContext {
9067 And,
9068 Or,
9069 Not,
9070 Nested,
9071 }
9072
9073 type ConditionFrame<'a> = llkv_plan::TransformFrame<
9074 'a,
9075 SqlExpr,
9076 llkv_expr::expr::Expr<'static, String>,
9077 ConditionExitContext,
9078 >;
9079
9080 let mut work_stack: Vec<ConditionFrame> = vec![ConditionFrame::Enter(expr)];
9081 let mut result_stack: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
9082
9083 while let Some(frame) = work_stack.pop() {
9084 match frame {
9085 ConditionFrame::Enter(node) => match node {
9086 SqlExpr::BinaryOp { left, op, right } => match op {
9087 BinaryOperator::And => {
9088 work_stack.push(ConditionFrame::Exit(ConditionExitContext::And));
9089 work_stack.push(ConditionFrame::Enter(right));
9090 work_stack.push(ConditionFrame::Enter(left));
9091 }
9092 BinaryOperator::Or => {
9093 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Or));
9094 work_stack.push(ConditionFrame::Enter(right));
9095 work_stack.push(ConditionFrame::Enter(left));
9096 }
9097 BinaryOperator::Eq
9098 | BinaryOperator::NotEq
9099 | BinaryOperator::Lt
9100 | BinaryOperator::LtEq
9101 | BinaryOperator::Gt
9102 | BinaryOperator::GtEq => {
9103 let result = translate_comparison_with_context(
9104 engine,
9105 resolver,
9106 context.clone(),
9107 left,
9108 op.clone(),
9109 right,
9110 outer_scopes,
9111 scalar_subqueries,
9112 correlated_tracker.reborrow(),
9113 )?;
9114 work_stack.push(ConditionFrame::Leaf(result));
9115 }
9116 other => {
9117 return Err(Error::InvalidArgumentError(format!(
9118 "unsupported binary operator in WHERE clause: {other:?}"
9119 )));
9120 }
9121 },
9122 SqlExpr::UnaryOp {
9123 op: UnaryOperator::Not,
9124 expr: inner,
9125 } => {
9126 let (nested_not_count, core_expr) = peel_not_chain(inner);
9127 let total_not_count = nested_not_count + 1;
9128 if let SqlExpr::Between {
9129 expr: between_expr,
9130 negated,
9131 low,
9132 high,
9133 } = core_expr
9134 {
9135 let mut negated_mode = *negated;
9136 if total_not_count % 2 == 1 {
9137 negated_mode = !negated_mode;
9138 }
9139 let (effective_negated, normalized_expr) =
9140 normalize_between_operand(between_expr, negated_mode);
9141 let between_expr_result = translate_between_expr(
9142 engine,
9143 resolver,
9144 context.clone(),
9145 normalized_expr,
9146 BetweenBounds {
9147 lower: low,
9148 upper: high,
9149 },
9150 effective_negated,
9151 outer_scopes,
9152 scalar_subqueries,
9153 correlated_tracker.reborrow(),
9154 )?;
9155 work_stack.push(ConditionFrame::Leaf(between_expr_result));
9156 continue;
9157 }
9158 for _ in 0..total_not_count {
9162 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Not));
9163 }
9164 work_stack.push(ConditionFrame::Enter(core_expr));
9165 }
9166 SqlExpr::Nested(inner) => {
9167 work_stack.push(ConditionFrame::Exit(ConditionExitContext::Nested));
9168 work_stack.push(ConditionFrame::Enter(inner));
9169 }
9170 SqlExpr::IsNull(inner) => {
9171 let scalar = translate_scalar_with_context_scoped(
9172 engine,
9173 resolver,
9174 context.clone(),
9175 inner,
9176 outer_scopes,
9177 correlated_tracker.reborrow(),
9178 Some(&mut *scalar_subqueries),
9179 )?;
9180 match scalar {
9181 llkv_expr::expr::ScalarExpr::Column(column) => {
9182 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Pred(
9184 llkv_expr::expr::Filter {
9185 field_id: column,
9186 op: llkv_expr::expr::Operator::IsNull,
9187 },
9188 )));
9189 }
9190 other => {
9195 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::IsNull {
9197 expr: other,
9198 negated: false,
9199 }));
9200 }
9201 }
9202 }
9203 SqlExpr::IsNotNull(inner) => {
9204 let scalar = translate_scalar_with_context_scoped(
9205 engine,
9206 resolver,
9207 context.clone(),
9208 inner,
9209 outer_scopes,
9210 correlated_tracker.reborrow(),
9211 Some(&mut *scalar_subqueries),
9212 )?;
9213 match scalar {
9214 llkv_expr::expr::ScalarExpr::Column(column) => {
9215 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Pred(
9217 llkv_expr::expr::Filter {
9218 field_id: column,
9219 op: llkv_expr::expr::Operator::IsNotNull,
9220 },
9221 )));
9222 }
9223 other => {
9228 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::IsNull {
9230 expr: other,
9231 negated: true,
9232 }));
9233 }
9234 }
9235 }
9236 SqlExpr::InList {
9237 expr: in_expr,
9238 list,
9239 negated,
9240 } => {
9241 if list.is_empty() {
9242 let result = if *negated {
9243 llkv_expr::expr::Expr::Literal(true)
9244 } else {
9245 llkv_expr::expr::Expr::Literal(false)
9246 };
9247 work_stack.push(ConditionFrame::Leaf(result));
9248 } else {
9249 let target = translate_scalar_with_context_scoped(
9250 engine,
9251 resolver,
9252 context.clone(),
9253 in_expr,
9254 outer_scopes,
9255 correlated_tracker.reborrow(),
9256 Some(&mut *scalar_subqueries),
9257 )?;
9258 let mut values = Vec::with_capacity(list.len());
9259 for value_expr in list {
9260 let scalar = translate_scalar_with_context_scoped(
9261 engine,
9262 resolver,
9263 context.clone(),
9264 value_expr,
9265 outer_scopes,
9266 correlated_tracker.reborrow(),
9267 Some(&mut *scalar_subqueries),
9268 )?;
9269 values.push(scalar);
9270 }
9271
9272 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::InList {
9273 expr: target,
9274 list: values,
9275 negated: *negated,
9276 }));
9277 }
9278 }
9279 SqlExpr::InSubquery { .. } => {
9280 return Err(Error::InvalidArgumentError(
9281 "IN (SELECT ...) subqueries must be materialized before translation".into(),
9282 ));
9283 }
9284 SqlExpr::Between {
9285 expr: between_expr,
9286 negated,
9287 low,
9288 high,
9289 } => {
9290 let (effective_negated, normalized_expr) =
9291 normalize_between_operand(between_expr, *negated);
9292 let between_expr_result = translate_between_expr(
9293 engine,
9294 resolver,
9295 context.clone(),
9296 normalized_expr,
9297 BetweenBounds {
9298 lower: low,
9299 upper: high,
9300 },
9301 effective_negated,
9302 outer_scopes,
9303 scalar_subqueries,
9304 correlated_tracker.reborrow(),
9305 )?;
9306 work_stack.push(ConditionFrame::Leaf(between_expr_result));
9307 }
9308 SqlExpr::Exists { subquery, negated } => {
9309 let mut nested_scopes = outer_scopes.to_vec();
9311 nested_scopes.push(context.clone());
9312
9313 let mut tracker = SubqueryCorrelatedColumnTracker::new();
9314 let mut nested_subqueries = Vec::new();
9315
9316 let subquery_plan = engine.build_select_plan_internal(
9318 (**subquery).clone(),
9319 resolver,
9320 &nested_scopes,
9321 &mut nested_subqueries,
9322 Some(&mut tracker),
9323 )?;
9324
9325 let subquery_id = llkv_expr::SubqueryId(subqueries.len() as u32);
9326 let filter_subquery = llkv_plan::FilterSubquery {
9327 id: subquery_id,
9328 plan: Box::new(subquery_plan),
9329 correlated_columns: tracker.into_columns(),
9330 };
9331 subqueries.push(filter_subquery);
9332
9333 work_stack.push(ConditionFrame::Leaf(llkv_expr::expr::Expr::Exists(
9334 llkv_expr::SubqueryExpr {
9335 id: subquery_id,
9336 negated: *negated,
9337 },
9338 )));
9339 }
9340 SqlExpr::Like {
9341 negated,
9342 expr: like_expr,
9343 pattern,
9344 escape_char,
9345 any: _,
9346 } => {
9347 if escape_char.is_some() {
9348 return Err(Error::InvalidArgumentError(
9349 "LIKE with ESCAPE clause is not supported".into(),
9350 ));
9351 }
9352
9353 let result = translate_like_expr(
9354 engine,
9355 resolver,
9356 context.clone(),
9357 like_expr,
9358 pattern,
9359 *negated,
9360 outer_scopes,
9361 scalar_subqueries,
9362 correlated_tracker.reborrow(),
9363 )?;
9364 work_stack.push(ConditionFrame::Leaf(result));
9365 }
9366 other => {
9367 return Err(Error::InvalidArgumentError(format!(
9368 "unsupported WHERE clause: {other:?}"
9369 )));
9370 }
9371 },
9372 ConditionFrame::Leaf(translated) => {
9373 result_stack.push(translated);
9374 }
9375 ConditionFrame::Exit(exit_context) => match exit_context {
9376 ConditionExitContext::And => {
9377 let right = result_stack.pop().ok_or_else(|| {
9378 Error::Internal(
9379 "translate_condition: result stack underflow for And right".into(),
9380 )
9381 })?;
9382 let left = result_stack.pop().ok_or_else(|| {
9383 Error::Internal(
9384 "translate_condition: result stack underflow for And left".into(),
9385 )
9386 })?;
9387 result_stack.push(flatten_and(left, right));
9388 }
9389 ConditionExitContext::Or => {
9390 let right = result_stack.pop().ok_or_else(|| {
9391 Error::Internal(
9392 "translate_condition: result stack underflow for Or right".into(),
9393 )
9394 })?;
9395 let left = result_stack.pop().ok_or_else(|| {
9396 Error::Internal(
9397 "translate_condition: result stack underflow for Or left".into(),
9398 )
9399 })?;
9400 result_stack.push(flatten_or(left, right));
9401 }
9402 ConditionExitContext::Not => {
9403 let inner = result_stack.pop().ok_or_else(|| {
9404 Error::Internal(
9405 "translate_condition: result stack underflow for Not".into(),
9406 )
9407 })?;
9408 match inner {
9410 llkv_expr::expr::Expr::IsNull { expr, negated } => {
9411 result_stack.push(llkv_expr::expr::Expr::IsNull {
9412 expr,
9413 negated: !negated,
9414 });
9415 }
9416 other => {
9417 result_stack.push(llkv_expr::expr::Expr::not(other));
9418 }
9419 }
9420 }
9421 ConditionExitContext::Nested => {
9422 }
9424 },
9425 }
9426 }
9427
9428 result_stack.pop().ok_or_else(|| {
9429 Error::Internal("translate_condition_with_context: empty result stack".into())
9430 })
9431}
9432
9433fn flatten_and(
9434 left: llkv_expr::expr::Expr<'static, String>,
9435 right: llkv_expr::expr::Expr<'static, String>,
9436) -> llkv_expr::expr::Expr<'static, String> {
9437 let mut children: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
9438 match left {
9439 llkv_expr::expr::Expr::And(mut left_children) => children.append(&mut left_children),
9440 other => children.push(other),
9441 }
9442 match right {
9443 llkv_expr::expr::Expr::And(mut right_children) => children.append(&mut right_children),
9444 other => children.push(other),
9445 }
9446 if children.len() == 1 {
9447 children.into_iter().next().unwrap()
9448 } else {
9449 llkv_expr::expr::Expr::And(children)
9450 }
9451}
9452
9453fn flatten_or(
9454 left: llkv_expr::expr::Expr<'static, String>,
9455 right: llkv_expr::expr::Expr<'static, String>,
9456) -> llkv_expr::expr::Expr<'static, String> {
9457 let mut children: Vec<llkv_expr::expr::Expr<'static, String>> = Vec::new();
9458 match left {
9459 llkv_expr::expr::Expr::Or(mut left_children) => children.append(&mut left_children),
9460 other => children.push(other),
9461 }
9462 match right {
9463 llkv_expr::expr::Expr::Or(mut right_children) => children.append(&mut right_children),
9464 other => children.push(other),
9465 }
9466 if children.len() == 1 {
9467 children.into_iter().next().unwrap()
9468 } else {
9469 llkv_expr::expr::Expr::Or(children)
9470 }
9471}
9472
9473fn peel_unparenthesized_not_chain(expr: &SqlExpr) -> (usize, &SqlExpr) {
9474 let mut count: usize = 0;
9475 let mut current = expr;
9476 while let SqlExpr::UnaryOp {
9477 op: UnaryOperator::Not,
9478 expr: inner,
9479 } = current
9480 {
9481 if matches!(inner.as_ref(), SqlExpr::Nested(_)) {
9482 break;
9483 }
9484 count += 1;
9485 current = inner.as_ref();
9486 }
9487 (count, current)
9488}
9489#[allow(clippy::too_many_arguments)] fn translate_comparison_with_context(
9491 engine: &SqlEngine,
9492 resolver: &IdentifierResolver<'_>,
9493 context: IdentifierContext,
9494 left: &SqlExpr,
9495 op: BinaryOperator,
9496 right: &SqlExpr,
9497 outer_scopes: &[IdentifierContext],
9498 scalar_subqueries: &mut Vec<llkv_plan::ScalarSubquery>,
9499 mut correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
9500) -> SqlResult<llkv_expr::expr::Expr<'static, String>> {
9501 let (not_count, comparison_left) = peel_unparenthesized_not_chain(left);
9502
9503 let left_scalar = {
9504 let tracker = correlated_tracker.reborrow();
9505 translate_scalar_with_context_scoped(
9506 engine,
9507 resolver,
9508 context.clone(),
9509 comparison_left,
9510 outer_scopes,
9511 tracker,
9512 Some(scalar_subqueries),
9513 )?
9514 };
9515 let right_scalar = {
9516 let tracker = correlated_tracker.reborrow();
9517 translate_scalar_with_context_scoped(
9518 engine,
9519 resolver,
9520 context,
9521 right,
9522 outer_scopes,
9523 tracker,
9524 Some(scalar_subqueries),
9525 )?
9526 };
9527 let compare_op = match op {
9528 BinaryOperator::Eq => llkv_expr::expr::CompareOp::Eq,
9529 BinaryOperator::NotEq => llkv_expr::expr::CompareOp::NotEq,
9530 BinaryOperator::Lt => llkv_expr::expr::CompareOp::Lt,
9531 BinaryOperator::LtEq => llkv_expr::expr::CompareOp::LtEq,
9532 BinaryOperator::Gt => llkv_expr::expr::CompareOp::Gt,
9533 BinaryOperator::GtEq => llkv_expr::expr::CompareOp::GtEq,
9534 other => {
9535 return Err(Error::InvalidArgumentError(format!(
9536 "unsupported comparison operator: {other:?}"
9537 )));
9538 }
9539 };
9540
9541 let mut expr = llkv_expr::expr::Expr::Compare {
9542 left: left_scalar.clone(),
9543 op: compare_op,
9544 right: right_scalar.clone(),
9545 };
9546
9547 if let (
9548 llkv_expr::expr::ScalarExpr::Column(column),
9549 llkv_expr::expr::ScalarExpr::Literal(literal),
9550 ) = (&left_scalar, &right_scalar)
9551 && let Some(op) = compare_op_to_filter_operator(compare_op, literal)
9552 {
9553 tracing::debug!(
9554 column = ?column,
9555 literal = ?literal,
9556 ?compare_op,
9557 "translate_comparison direct"
9558 );
9559 expr = llkv_expr::expr::Expr::Pred(llkv_expr::expr::Filter {
9560 field_id: column.clone(),
9561 op,
9562 });
9563 } else if let (
9564 llkv_expr::expr::ScalarExpr::Literal(literal),
9565 llkv_expr::expr::ScalarExpr::Column(column),
9566 ) = (&left_scalar, &right_scalar)
9567 && let Some(flipped) = flip_compare_op(compare_op)
9568 && let Some(op) = compare_op_to_filter_operator(flipped, literal)
9569 {
9570 tracing::debug!(
9571 column = ?column,
9572 literal = ?literal,
9573 original_op = ?compare_op,
9574 flipped_op = ?flipped,
9575 "translate_comparison flipped"
9576 );
9577 expr = llkv_expr::expr::Expr::Pred(llkv_expr::expr::Filter {
9578 field_id: column.clone(),
9579 op,
9580 });
9581 }
9582
9583 let mut wrapped = expr;
9584 for _ in 0..not_count {
9585 wrapped = llkv_expr::expr::Expr::Not(Box::new(wrapped));
9586 }
9587
9588 Ok(wrapped)
9589}
9590
9591fn compare_op_to_filter_operator(
9592 op: llkv_expr::expr::CompareOp,
9593 literal: &Literal,
9594) -> Option<llkv_expr::expr::Operator<'static>> {
9595 if matches!(literal, Literal::Null) {
9596 return None;
9597 }
9598 let lit = literal.clone();
9599 tracing::debug!(?op, literal = ?literal, "compare_op_to_filter_operator input");
9600 match op {
9601 llkv_expr::expr::CompareOp::Eq => Some(llkv_expr::expr::Operator::Equals(lit)),
9602 llkv_expr::expr::CompareOp::Lt => Some(llkv_expr::expr::Operator::LessThan(lit)),
9603 llkv_expr::expr::CompareOp::LtEq => Some(llkv_expr::expr::Operator::LessThanOrEquals(lit)),
9604 llkv_expr::expr::CompareOp::Gt => Some(llkv_expr::expr::Operator::GreaterThan(lit)),
9605 llkv_expr::expr::CompareOp::GtEq => {
9606 Some(llkv_expr::expr::Operator::GreaterThanOrEquals(lit))
9607 }
9608 llkv_expr::expr::CompareOp::NotEq => None,
9609 }
9610}
9611
9612fn flip_compare_op(op: llkv_expr::expr::CompareOp) -> Option<llkv_expr::expr::CompareOp> {
9613 match op {
9614 llkv_expr::expr::CompareOp::Eq => Some(llkv_expr::expr::CompareOp::Eq),
9615 llkv_expr::expr::CompareOp::Lt => Some(llkv_expr::expr::CompareOp::Gt),
9616 llkv_expr::expr::CompareOp::LtEq => Some(llkv_expr::expr::CompareOp::GtEq),
9617 llkv_expr::expr::CompareOp::Gt => Some(llkv_expr::expr::CompareOp::Lt),
9618 llkv_expr::expr::CompareOp::GtEq => Some(llkv_expr::expr::CompareOp::LtEq),
9619 llkv_expr::expr::CompareOp::NotEq => None,
9620 }
9621}
9622fn translate_scalar_with_context(
9625 resolver: &IdentifierResolver<'_>,
9626 context: IdentifierContext,
9627 expr: &SqlExpr,
9628) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
9629 let mut tracker = SubqueryCorrelatedTracker::from_option(None);
9630 translate_scalar_internal(
9631 expr,
9632 Some(resolver),
9633 Some(&context),
9634 &[],
9635 &mut tracker,
9636 None,
9637 )
9638}
9639
9640fn translate_scalar_with_context_scoped(
9641 engine: &SqlEngine,
9642 resolver: &IdentifierResolver<'_>,
9643 context: IdentifierContext,
9644 expr: &SqlExpr,
9645 outer_scopes: &[IdentifierContext],
9646 correlated_tracker: Option<&mut SubqueryCorrelatedColumnTracker>,
9647 scalar_subqueries: Option<&mut Vec<llkv_plan::ScalarSubquery>>,
9648) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
9649 let mut tracker = SubqueryCorrelatedTracker::from_option(correlated_tracker);
9650 let mut planner = scalar_subqueries.map(|storage| ScalarSubqueryPlanner {
9651 engine,
9652 scalar_subqueries: storage,
9653 });
9654 let resolver_opt = planner
9655 .as_mut()
9656 .map(|builder| builder as &mut dyn ScalarSubqueryResolver);
9657 translate_scalar_internal(
9658 expr,
9659 Some(resolver),
9660 Some(&context),
9661 outer_scopes,
9662 &mut tracker,
9663 resolver_opt,
9664 )
9665}
9666
9667#[allow(dead_code)]
9668fn translate_scalar(expr: &SqlExpr) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
9669 let mut tracker = SubqueryCorrelatedTracker::from_option(None);
9670 translate_scalar_internal(expr, None, None, &[], &mut tracker, None)
9671}
9672
9673fn translate_scalar_internal(
9674 expr: &SqlExpr,
9675 resolver: Option<&IdentifierResolver<'_>>,
9676 context: Option<&IdentifierContext>,
9677 outer_scopes: &[IdentifierContext],
9678 tracker: &mut SubqueryCorrelatedTracker<'_>,
9679 mut subquery_resolver: Option<&mut dyn ScalarSubqueryResolver>,
9680) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
9681 enum ScalarExitContext {
9689 BinaryOp {
9690 op: BinaryOperator,
9691 },
9692 Compare {
9693 op: llkv_expr::expr::CompareOp,
9694 },
9695 UnaryNot,
9696 UnaryMinus,
9697 UnaryPlus,
9698 Nested,
9699 Cast(DataType),
9700 IsNull {
9701 negated: bool,
9702 },
9703 Between {
9704 negated: bool,
9705 },
9706 InList {
9707 list_len: usize,
9708 negated: bool,
9709 },
9710 Case {
9711 branch_count: usize,
9712 has_operand: bool,
9713 has_else: bool,
9714 },
9715 BuiltinFunction {
9716 func: BuiltinScalarFunction,
9717 arg_count: usize,
9718 },
9719 }
9720
9721 #[derive(Clone, Copy)]
9722 enum BuiltinScalarFunction {
9723 Abs,
9724 Coalesce,
9725 NullIf,
9726 Floor,
9727 }
9728
9729 type ScalarFrame<'a> =
9730 TransformFrame<'a, SqlExpr, llkv_expr::expr::ScalarExpr<String>, ScalarExitContext>;
9731
9732 let mut work_stack: Vec<ScalarFrame> = vec![ScalarFrame::Enter(expr)];
9733 let mut result_stack: Vec<llkv_expr::expr::ScalarExpr<String>> = Vec::new();
9734
9735 while let Some(frame) = work_stack.pop() {
9736 match frame {
9737 ScalarFrame::Enter(node) => match node {
9738 SqlExpr::Identifier(ident) => {
9739 if let (Some(resolver), Some(ctx)) = (resolver, context) {
9740 let parts = vec![ident.value.clone()];
9741 let tracker_view = tracker.reborrow();
9742 let expr = resolve_identifier_expr(
9743 resolver,
9744 ctx,
9745 parts,
9746 outer_scopes,
9747 tracker_view,
9748 )?;
9749 work_stack.push(ScalarFrame::Leaf(expr));
9750 } else {
9751 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::column(
9752 ident.value.clone(),
9753 )));
9754 }
9755 }
9756 SqlExpr::CompoundIdentifier(idents) => {
9757 if idents.is_empty() {
9758 return Err(Error::InvalidArgumentError(
9759 "invalid compound identifier".into(),
9760 ));
9761 }
9762
9763 if let (Some(resolver), Some(ctx)) = (resolver, context) {
9764 let parts: Vec<String> =
9765 idents.iter().map(|ident| ident.value.clone()).collect();
9766 let tracker_view = tracker.reborrow();
9767 let expr = resolve_identifier_expr(
9768 resolver,
9769 ctx,
9770 parts,
9771 outer_scopes,
9772 tracker_view,
9773 )?;
9774 work_stack.push(ScalarFrame::Leaf(expr));
9775 } else {
9776 let column_name = idents[0].value.clone();
9777 let mut result = llkv_expr::expr::ScalarExpr::column(column_name);
9778
9779 for part in &idents[1..] {
9780 let field_name = part.value.clone();
9781 result = llkv_expr::expr::ScalarExpr::get_field(result, field_name);
9782 }
9783
9784 work_stack.push(ScalarFrame::Leaf(result));
9785 }
9786 }
9787 SqlExpr::Value(value) => {
9788 let result = literal_from_value(value)?;
9789 work_stack.push(ScalarFrame::Leaf(result));
9790 }
9791 SqlExpr::Interval(interval) => {
9792 let parsed = parse_interval_literal(interval)?;
9793 let literal = llkv_expr::expr::ScalarExpr::literal(Literal::Interval(parsed));
9794 work_stack.push(ScalarFrame::Leaf(literal));
9795 }
9796 SqlExpr::BinaryOp { left, op, right } => match op {
9797 BinaryOperator::Plus
9798 | BinaryOperator::Minus
9799 | BinaryOperator::Multiply
9800 | BinaryOperator::Divide
9801 | BinaryOperator::Modulo
9802 | BinaryOperator::And
9803 | BinaryOperator::Or
9804 | BinaryOperator::PGBitwiseShiftLeft
9805 | BinaryOperator::PGBitwiseShiftRight => {
9806 work_stack.push(ScalarFrame::Exit(ScalarExitContext::BinaryOp {
9807 op: op.clone(),
9808 }));
9809 work_stack.push(ScalarFrame::Enter(right));
9810 work_stack.push(ScalarFrame::Enter(left));
9811 }
9812 BinaryOperator::Eq
9813 | BinaryOperator::NotEq
9814 | BinaryOperator::Lt
9815 | BinaryOperator::LtEq
9816 | BinaryOperator::Gt
9817 | BinaryOperator::GtEq => {
9818 let compare_op = match op {
9819 BinaryOperator::Eq => llkv_expr::expr::CompareOp::Eq,
9820 BinaryOperator::NotEq => llkv_expr::expr::CompareOp::NotEq,
9821 BinaryOperator::Lt => llkv_expr::expr::CompareOp::Lt,
9822 BinaryOperator::LtEq => llkv_expr::expr::CompareOp::LtEq,
9823 BinaryOperator::Gt => llkv_expr::expr::CompareOp::Gt,
9824 BinaryOperator::GtEq => llkv_expr::expr::CompareOp::GtEq,
9825 _ => unreachable!(),
9826 };
9827 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Compare {
9828 op: compare_op,
9829 }));
9830 work_stack.push(ScalarFrame::Enter(right));
9831 work_stack.push(ScalarFrame::Enter(left));
9832 }
9833 other => {
9834 return Err(Error::InvalidArgumentError(format!(
9835 "unsupported scalar binary operator: {other:?}"
9836 )));
9837 }
9838 },
9839 SqlExpr::UnaryOp {
9840 op: UnaryOperator::Not,
9841 expr: inner,
9842 } => {
9843 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryNot));
9844 work_stack.push(ScalarFrame::Enter(inner));
9845 }
9846 SqlExpr::UnaryOp {
9847 op: UnaryOperator::Minus,
9848 expr: inner,
9849 } => {
9850 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryMinus));
9851 work_stack.push(ScalarFrame::Enter(inner));
9852 }
9853 SqlExpr::UnaryOp {
9854 op: UnaryOperator::Plus,
9855 expr: inner,
9856 } => {
9857 work_stack.push(ScalarFrame::Exit(ScalarExitContext::UnaryPlus));
9858 work_stack.push(ScalarFrame::Enter(inner));
9859 }
9860 SqlExpr::Nested(inner) => {
9861 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Nested));
9862 work_stack.push(ScalarFrame::Enter(inner));
9863 }
9864 SqlExpr::Cast {
9865 expr: inner,
9866 data_type,
9867 ..
9868 } => {
9869 let target_type = arrow_type_from_sql(data_type)?;
9870 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Cast(target_type)));
9871 work_stack.push(ScalarFrame::Enter(inner));
9872 }
9873 SqlExpr::Case {
9874 operand,
9875 conditions,
9876 else_result,
9877 ..
9878 } => {
9879 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Case {
9880 branch_count: conditions.len(),
9881 has_operand: operand.is_some(),
9882 has_else: else_result.is_some(),
9883 }));
9884 if let Some(else_expr) = else_result.as_deref() {
9885 work_stack.push(ScalarFrame::Enter(else_expr));
9886 }
9887 for case_when in conditions.iter().rev() {
9888 work_stack.push(ScalarFrame::Enter(&case_when.result));
9889 work_stack.push(ScalarFrame::Enter(&case_when.condition));
9890 }
9891 if let Some(opnd) = operand.as_deref() {
9892 work_stack.push(ScalarFrame::Enter(opnd));
9893 }
9894 }
9895 SqlExpr::InList {
9896 expr: in_expr,
9897 list,
9898 negated,
9899 } => {
9900 if list.is_empty() {
9901 let literal_value = if *negated {
9902 llkv_expr::expr::ScalarExpr::literal(Literal::Int128(1))
9903 } else {
9904 llkv_expr::expr::ScalarExpr::literal(Literal::Int128(0))
9905 };
9906 work_stack.push(ScalarFrame::Leaf(literal_value));
9907 } else {
9908 work_stack.push(ScalarFrame::Exit(ScalarExitContext::InList {
9909 list_len: list.len(),
9910 negated: *negated,
9911 }));
9912 for value_expr in list.iter().rev() {
9913 work_stack.push(ScalarFrame::Enter(value_expr));
9914 }
9915 work_stack.push(ScalarFrame::Enter(in_expr));
9916 }
9917 }
9918 SqlExpr::IsNull(inner) => {
9919 work_stack.push(ScalarFrame::Exit(ScalarExitContext::IsNull {
9920 negated: false,
9921 }));
9922 work_stack.push(ScalarFrame::Enter(inner));
9923 }
9924 SqlExpr::IsNotNull(inner) => {
9925 work_stack.push(ScalarFrame::Exit(ScalarExitContext::IsNull {
9926 negated: true,
9927 }));
9928 work_stack.push(ScalarFrame::Enter(inner));
9929 }
9930 SqlExpr::Between {
9931 expr: between_expr,
9932 negated,
9933 low,
9934 high,
9935 } => {
9936 let (effective_negated, normalized_expr) =
9937 normalize_between_operand(between_expr, *negated);
9938 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Between {
9939 negated: effective_negated,
9940 }));
9941 work_stack.push(ScalarFrame::Enter(high));
9942 work_stack.push(ScalarFrame::Enter(low));
9943 work_stack.push(ScalarFrame::Enter(normalized_expr));
9944 }
9945 SqlExpr::Function(func) => {
9946 if let Some(agg_call) = try_parse_aggregate_function(
9947 func,
9948 resolver,
9949 context,
9950 outer_scopes,
9951 tracker,
9952 )? {
9953 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::aggregate(
9954 agg_call,
9955 )));
9956 } else {
9957 use sqlparser::ast::{
9958 FunctionArg, FunctionArgExpr, FunctionArguments, ObjectNamePart,
9959 };
9960
9961 if func.uses_odbc_syntax
9962 || !matches!(func.parameters, FunctionArguments::None)
9963 || func.filter.is_some()
9964 || func.null_treatment.is_some()
9965 || func.over.is_some()
9966 || !func.within_group.is_empty()
9967 {
9968 return Err(Error::InvalidArgumentError(format!(
9969 "unsupported function in scalar expression: {:?}",
9970 func.name
9971 )));
9972 }
9973
9974 let func_name = if func.name.0.len() == 1 {
9975 match &func.name.0[0] {
9976 ObjectNamePart::Identifier(ident) => {
9977 ident.value.to_ascii_lowercase()
9978 }
9979 _ => {
9980 return Err(Error::InvalidArgumentError(format!(
9981 "unsupported function in scalar expression: {:?}",
9982 func.name
9983 )));
9984 }
9985 }
9986 } else {
9987 return Err(Error::InvalidArgumentError(format!(
9988 "unsupported function in scalar expression: {:?}",
9989 func.name
9990 )));
9991 };
9992
9993 match func_name.as_str() {
9994 "abs" => {
9995 let args_slice: &[FunctionArg] = match &func.args {
9996 FunctionArguments::List(list) => {
9997 if list.duplicate_treatment.is_some()
9998 || !list.clauses.is_empty()
9999 {
10000 return Err(Error::InvalidArgumentError(
10001 "ABS does not support qualifiers".into(),
10002 ));
10003 }
10004 &list.args
10005 }
10006 _ => {
10007 return Err(Error::InvalidArgumentError(
10008 "ABS requires exactly one argument".into(),
10009 ));
10010 }
10011 };
10012
10013 if args_slice.len() != 1 {
10014 return Err(Error::InvalidArgumentError(
10015 "ABS requires exactly one argument".into(),
10016 ));
10017 }
10018
10019 let arg_expr = match &args_slice[0] {
10020 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
10021 _ => {
10022 return Err(Error::InvalidArgumentError(
10023 "ABS argument must be an expression".into(),
10024 ));
10025 }
10026 };
10027
10028 work_stack.push(ScalarFrame::Exit(
10029 ScalarExitContext::BuiltinFunction {
10030 func: BuiltinScalarFunction::Abs,
10031 arg_count: 1,
10032 },
10033 ));
10034 work_stack.push(ScalarFrame::Enter(arg_expr));
10035 continue;
10036 }
10037 "floor" => {
10038 let args_slice: &[FunctionArg] = match &func.args {
10039 FunctionArguments::List(list) => {
10040 if list.duplicate_treatment.is_some()
10041 || !list.clauses.is_empty()
10042 {
10043 return Err(Error::InvalidArgumentError(
10044 "FLOOR does not support qualifiers".into(),
10045 ));
10046 }
10047 &list.args
10048 }
10049 _ => {
10050 return Err(Error::InvalidArgumentError(
10051 "FLOOR requires exactly one argument".into(),
10052 ));
10053 }
10054 };
10055
10056 if args_slice.len() != 1 {
10057 return Err(Error::InvalidArgumentError(
10058 "FLOOR requires exactly one argument".into(),
10059 ));
10060 }
10061
10062 let arg_expr = match &args_slice[0] {
10063 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
10064 _ => {
10065 return Err(Error::InvalidArgumentError(
10066 "FLOOR argument must be an expression".into(),
10067 ));
10068 }
10069 };
10070
10071 work_stack.push(ScalarFrame::Exit(
10072 ScalarExitContext::BuiltinFunction {
10073 func: BuiltinScalarFunction::Floor,
10074 arg_count: 1,
10075 },
10076 ));
10077 work_stack.push(ScalarFrame::Enter(arg_expr));
10078 continue;
10079 }
10080 "coalesce" => {
10081 let args_slice: &[FunctionArg] = match &func.args {
10082 FunctionArguments::List(list) => {
10083 if list.duplicate_treatment.is_some()
10084 || !list.clauses.is_empty()
10085 {
10086 return Err(Error::InvalidArgumentError(
10087 "COALESCE does not support qualifiers".into(),
10088 ));
10089 }
10090 &list.args
10091 }
10092 _ => {
10093 return Err(Error::InvalidArgumentError(
10094 "COALESCE requires at least one argument".into(),
10095 ));
10096 }
10097 };
10098
10099 if args_slice.is_empty() {
10100 return Err(Error::InvalidArgumentError(
10101 "COALESCE requires at least one argument".into(),
10102 ));
10103 }
10104
10105 work_stack.push(ScalarFrame::Exit(
10106 ScalarExitContext::BuiltinFunction {
10107 func: BuiltinScalarFunction::Coalesce,
10108 arg_count: args_slice.len(),
10109 },
10110 ));
10111
10112 for arg in args_slice.iter().rev() {
10113 let arg_expr = match arg {
10114 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
10115 _ => {
10116 return Err(Error::InvalidArgumentError(
10117 "COALESCE arguments must be expressions".into(),
10118 ));
10119 }
10120 };
10121 work_stack.push(ScalarFrame::Enter(arg_expr));
10122 }
10123 continue;
10124 }
10125 "nullif" => {
10126 let args_slice: &[FunctionArg] = match &func.args {
10127 FunctionArguments::List(list) => {
10128 if list.duplicate_treatment.is_some()
10129 || !list.clauses.is_empty()
10130 {
10131 return Err(Error::InvalidArgumentError(
10132 "NULLIF does not support qualifiers".into(),
10133 ));
10134 }
10135 &list.args
10136 }
10137 _ => {
10138 return Err(Error::InvalidArgumentError(
10139 "NULLIF requires exactly two arguments".into(),
10140 ));
10141 }
10142 };
10143
10144 if args_slice.len() != 2 {
10145 return Err(Error::InvalidArgumentError(
10146 "NULLIF requires exactly two arguments".into(),
10147 ));
10148 }
10149
10150 work_stack.push(ScalarFrame::Exit(
10151 ScalarExitContext::BuiltinFunction {
10152 func: BuiltinScalarFunction::NullIf,
10153 arg_count: 2,
10154 },
10155 ));
10156
10157 for arg in args_slice.iter().rev() {
10158 let arg_expr = match arg {
10159 FunctionArg::Unnamed(FunctionArgExpr::Expr(expr)) => expr,
10160 _ => {
10161 return Err(Error::InvalidArgumentError(
10162 "NULLIF arguments must be expressions".into(),
10163 ));
10164 }
10165 };
10166 work_stack.push(ScalarFrame::Enter(arg_expr));
10167 }
10168 continue;
10169 }
10170 "random" | "rand" => {
10171 let args_slice: &[FunctionArg] = match &func.args {
10172 FunctionArguments::List(list) => {
10173 if list.duplicate_treatment.is_some()
10174 || !list.clauses.is_empty()
10175 {
10176 return Err(Error::InvalidArgumentError(
10177 "RANDOM does not support qualifiers".into(),
10178 ));
10179 }
10180 &list.args
10181 }
10182 FunctionArguments::None => &[],
10183 _ => {
10184 return Err(Error::InvalidArgumentError(
10185 "RANDOM does not accept arguments".into(),
10186 ));
10187 }
10188 };
10189
10190 if !args_slice.is_empty() {
10191 return Err(Error::InvalidArgumentError(
10192 "RANDOM does not accept arguments".into(),
10193 ));
10194 }
10195
10196 work_stack
10197 .push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::random()));
10198 continue;
10199 }
10200 _ => {
10201 return Err(Error::InvalidArgumentError(format!(
10202 "unsupported function in scalar expression: {:?}",
10203 func.name
10204 )));
10205 }
10206 }
10207 }
10208 }
10209 SqlExpr::Dictionary(fields) => {
10210 let mut struct_fields = Vec::new();
10212 for entry in fields {
10213 let key = entry.key.value.clone();
10214 let mut tracker_view = tracker.reborrow();
10217 let value_expr = translate_scalar_internal(
10218 &entry.value,
10219 resolver,
10220 context,
10221 outer_scopes,
10222 &mut tracker_view,
10223 None,
10224 )?;
10225 match value_expr {
10226 llkv_expr::expr::ScalarExpr::Literal(lit) => {
10227 struct_fields.push((key, Box::new(lit)));
10228 }
10229 _ => {
10230 return Err(Error::InvalidArgumentError(
10231 "Dictionary values must be literals".to_string(),
10232 ));
10233 }
10234 }
10235 }
10236 work_stack.push(ScalarFrame::Leaf(llkv_expr::expr::ScalarExpr::literal(
10237 Literal::Struct(struct_fields),
10238 )));
10239 }
10240 SqlExpr::Subquery(subquery) => {
10241 let handler = subquery_resolver.as_mut().ok_or_else(|| {
10242 Error::InvalidArgumentError(
10243 "Correlated scalar subqueries not yet fully implemented - requires plan-level support".
10244 to_string(),
10245 )
10246 })?;
10247 let resolver_ref = resolver.ok_or_else(|| {
10248 Error::InvalidArgumentError(
10249 "scalar subquery translation requires identifier resolver".into(),
10250 )
10251 })?;
10252 let context_ref = context.ok_or_else(|| {
10253 Error::InvalidArgumentError(
10254 "scalar subquery translation requires identifier context".into(),
10255 )
10256 })?;
10257 let translated = handler.handle_scalar_subquery(
10258 subquery.as_ref(),
10259 resolver_ref,
10260 context_ref,
10261 outer_scopes,
10262 )?;
10263 work_stack.push(ScalarFrame::Leaf(translated));
10264 }
10265 SqlExpr::Floor { expr, field } => {
10266 use sqlparser::ast::{CeilFloorKind, DateTimeField};
10269 if !matches!(
10270 field,
10271 CeilFloorKind::DateTimeField(DateTimeField::NoDateTime)
10272 ) {
10273 return Err(Error::InvalidArgumentError(format!(
10274 "FLOOR with datetime field or scale not supported: {field:?}"
10275 )));
10276 }
10277
10278 work_stack.push(ScalarFrame::Exit(ScalarExitContext::Cast(DataType::Int64)));
10281 work_stack.push(ScalarFrame::Enter(expr.as_ref()));
10282 }
10283 other => {
10284 return Err(Error::InvalidArgumentError(format!(
10285 "unsupported scalar expression: {other:?}"
10286 )));
10287 }
10288 },
10289 ScalarFrame::Leaf(translated) => {
10290 result_stack.push(translated);
10291 }
10292 ScalarFrame::Exit(exit_context) => match exit_context {
10293 ScalarExitContext::BinaryOp { op } => {
10294 let right_expr = result_stack.pop().ok_or_else(|| {
10295 Error::Internal(
10296 "translate_scalar: result stack underflow for BinaryOp right".into(),
10297 )
10298 })?;
10299 let left_expr = result_stack.pop().ok_or_else(|| {
10300 Error::Internal(
10301 "translate_scalar: result stack underflow for BinaryOp left".into(),
10302 )
10303 })?;
10304 match op {
10305 BinaryOperator::Plus => {
10306 let expr = llkv_expr::expr::ScalarExpr::binary(
10307 left_expr,
10308 llkv_expr::expr::BinaryOp::Add,
10309 right_expr,
10310 );
10311 result_stack.push(expr);
10312 }
10313 BinaryOperator::Minus => {
10314 let expr = llkv_expr::expr::ScalarExpr::binary(
10315 left_expr,
10316 llkv_expr::expr::BinaryOp::Subtract,
10317 right_expr,
10318 );
10319 result_stack.push(expr);
10320 }
10321 BinaryOperator::Multiply => {
10322 let expr = llkv_expr::expr::ScalarExpr::binary(
10323 left_expr,
10324 llkv_expr::expr::BinaryOp::Multiply,
10325 right_expr,
10326 );
10327 result_stack.push(expr);
10328 }
10329 BinaryOperator::Divide => {
10330 let expr = llkv_expr::expr::ScalarExpr::binary(
10331 left_expr,
10332 llkv_expr::expr::BinaryOp::Divide,
10333 right_expr,
10334 );
10335 result_stack.push(expr);
10336 }
10337 BinaryOperator::Modulo => {
10338 let expr = llkv_expr::expr::ScalarExpr::binary(
10339 left_expr,
10340 llkv_expr::expr::BinaryOp::Modulo,
10341 right_expr,
10342 );
10343 result_stack.push(expr);
10344 }
10345 BinaryOperator::And => {
10346 let expr = llkv_expr::expr::ScalarExpr::binary(
10347 left_expr,
10348 llkv_expr::expr::BinaryOp::And,
10349 right_expr,
10350 );
10351 result_stack.push(expr);
10352 }
10353 BinaryOperator::Or => {
10354 let expr = llkv_expr::expr::ScalarExpr::binary(
10355 left_expr,
10356 llkv_expr::expr::BinaryOp::Or,
10357 right_expr,
10358 );
10359 result_stack.push(expr);
10360 }
10361 BinaryOperator::PGBitwiseShiftLeft => {
10362 let expr = llkv_expr::expr::ScalarExpr::binary(
10363 left_expr,
10364 llkv_expr::expr::BinaryOp::BitwiseShiftLeft,
10365 right_expr,
10366 );
10367 result_stack.push(expr);
10368 }
10369 BinaryOperator::PGBitwiseShiftRight => {
10370 let expr = llkv_expr::expr::ScalarExpr::binary(
10371 left_expr,
10372 llkv_expr::expr::BinaryOp::BitwiseShiftRight,
10373 right_expr,
10374 );
10375 result_stack.push(expr);
10376 }
10377 other => {
10378 return Err(Error::InvalidArgumentError(format!(
10379 "unsupported scalar binary operator: {other:?}"
10380 )));
10381 }
10382 }
10383 }
10384 ScalarExitContext::Compare { op } => {
10385 let right_expr = result_stack.pop().ok_or_else(|| {
10386 Error::Internal(
10387 "translate_scalar: result stack underflow for Compare right".into(),
10388 )
10389 })?;
10390 let left_expr = result_stack.pop().ok_or_else(|| {
10391 Error::Internal(
10392 "translate_scalar: result stack underflow for Compare left".into(),
10393 )
10394 })?;
10395 result_stack.push(llkv_expr::expr::ScalarExpr::compare(
10396 left_expr, op, right_expr,
10397 ));
10398 }
10399 ScalarExitContext::BuiltinFunction { func, arg_count } => {
10400 if result_stack.len() < arg_count {
10401 return Err(Error::Internal(
10402 "translate_scalar: result stack underflow for builtin function".into(),
10403 ));
10404 }
10405
10406 let mut args: Vec<llkv_expr::expr::ScalarExpr<String>> =
10407 Vec::with_capacity(arg_count);
10408 for _ in 0..arg_count {
10409 if let Some(expr) = result_stack.pop() {
10410 args.push(expr);
10411 }
10412 }
10413 args.reverse();
10414
10415 let result_expr = match func {
10416 BuiltinScalarFunction::Abs => {
10417 debug_assert_eq!(args.len(), 1);
10418 build_abs_case_expr(args.pop().expect("ABS expects one argument"))
10419 }
10420 BuiltinScalarFunction::Coalesce => {
10421 llkv_expr::expr::ScalarExpr::coalesce(args)
10422 }
10423 BuiltinScalarFunction::NullIf => {
10424 debug_assert_eq!(args.len(), 2);
10425 let left = args.remove(0);
10426 let right = args.remove(0);
10427 let condition = llkv_expr::expr::ScalarExpr::compare(
10428 left.clone(),
10429 llkv_expr::expr::CompareOp::Eq,
10430 right,
10431 );
10432 llkv_expr::expr::ScalarExpr::Case {
10433 operand: None,
10434 branches: vec![(
10435 condition,
10436 llkv_expr::expr::ScalarExpr::literal(Literal::Null),
10437 )],
10438 else_expr: Some(Box::new(left)),
10439 }
10440 }
10441 BuiltinScalarFunction::Floor => {
10442 debug_assert_eq!(args.len(), 1);
10443 let arg = args.pop().expect("FLOOR expects one argument");
10444 llkv_expr::expr::ScalarExpr::cast(arg, DataType::Int64)
10448 }
10449 };
10450
10451 result_stack.push(result_expr);
10452 }
10453 ScalarExitContext::UnaryMinus => {
10454 let inner = result_stack.pop().ok_or_else(|| {
10455 Error::Internal(
10456 "translate_scalar: result stack underflow for UnaryMinus".into(),
10457 )
10458 })?;
10459 match inner {
10460 llkv_expr::expr::ScalarExpr::Literal(lit) => match lit {
10461 Literal::Int128(v) => {
10462 result_stack.push(llkv_expr::expr::ScalarExpr::literal(
10463 Literal::Int128(-v),
10464 ));
10465 }
10466 Literal::Float64(v) => {
10467 result_stack.push(llkv_expr::expr::ScalarExpr::literal(
10468 Literal::Float64(-v),
10469 ));
10470 }
10471 Literal::Boolean(_) => {
10472 return Err(Error::InvalidArgumentError(
10473 "cannot negate boolean literal".into(),
10474 ));
10475 }
10476 Literal::String(_) => {
10477 return Err(Error::InvalidArgumentError(
10478 "cannot negate string literal".into(),
10479 ));
10480 }
10481 Literal::Struct(_) => {
10482 return Err(Error::InvalidArgumentError(
10483 "cannot negate struct literal".into(),
10484 ));
10485 }
10486 Literal::Date32(_) => {
10487 return Err(Error::InvalidArgumentError(
10488 "cannot negate date literal".into(),
10489 ));
10490 }
10491 Literal::Interval(interval) => {
10492 let negated = interval.checked_neg().ok_or_else(|| {
10493 Error::InvalidArgumentError("interval overflow".into())
10494 })?;
10495 result_stack.push(llkv_expr::expr::ScalarExpr::literal(
10496 Literal::Interval(negated),
10497 ));
10498 }
10499 Literal::Null => {
10500 result_stack
10501 .push(llkv_expr::expr::ScalarExpr::literal(Literal::Null));
10502 }
10503 Literal::Decimal128(value) => {
10504 let negated_raw =
10505 value.raw_value().checked_neg().ok_or_else(|| {
10506 Error::InvalidArgumentError(
10507 "decimal overflow when applying unary minus".into(),
10508 )
10509 })?;
10510 let negated = DecimalValue::new(negated_raw, value.scale())
10511 .map_err(|err| {
10512 Error::InvalidArgumentError(format!(
10513 "failed to negate decimal literal: {err}"
10514 ))
10515 })?;
10516 result_stack.push(llkv_expr::expr::ScalarExpr::literal(
10517 Literal::Decimal128(negated),
10518 ));
10519 }
10520 },
10521 other => {
10522 let zero = llkv_expr::expr::ScalarExpr::literal(Literal::Int128(0));
10523 result_stack.push(llkv_expr::expr::ScalarExpr::binary(
10524 zero,
10525 llkv_expr::expr::BinaryOp::Subtract,
10526 other,
10527 ));
10528 }
10529 }
10530 }
10531 ScalarExitContext::UnaryNot => {
10532 let inner = result_stack.pop().ok_or_else(|| {
10533 Error::Internal(
10534 "translate_scalar: result stack underflow for UnaryNot".into(),
10535 )
10536 })?;
10537 result_stack.push(llkv_expr::expr::ScalarExpr::logical_not(inner));
10538 }
10539 ScalarExitContext::UnaryPlus => {
10540 let inner = result_stack.pop().ok_or_else(|| {
10544 Error::Internal(
10545 "translate_scalar: result stack underflow for UnaryPlus".into(),
10546 )
10547 })?;
10548 result_stack.push(inner);
10549 }
10550 ScalarExitContext::Nested => {
10551 }
10553 ScalarExitContext::Cast(target_type) => {
10554 let inner = result_stack.pop().ok_or_else(|| {
10555 Error::Internal("translate_scalar: result stack underflow for CAST".into())
10556 })?;
10557 result_stack.push(llkv_expr::expr::ScalarExpr::cast(inner, target_type));
10558 }
10559 ScalarExitContext::InList { list_len, negated } => {
10560 let mut list_exprs = Vec::with_capacity(list_len);
10561 for _ in 0..list_len {
10562 let value_expr = result_stack.pop().ok_or_else(|| {
10563 Error::Internal(
10564 "translate_scalar: result stack underflow for IN list value".into(),
10565 )
10566 })?;
10567 list_exprs.push(value_expr);
10568 }
10569 list_exprs.reverse();
10570
10571 let target_expr = result_stack.pop().ok_or_else(|| {
10572 Error::Internal(
10573 "translate_scalar: result stack underflow for IN list target".into(),
10574 )
10575 })?;
10576
10577 let mut comparisons: Vec<llkv_expr::expr::ScalarExpr<String>> =
10578 Vec::with_capacity(list_len);
10579 for value in &list_exprs {
10580 comparisons.push(llkv_expr::expr::ScalarExpr::compare(
10581 target_expr.clone(),
10582 llkv_expr::expr::CompareOp::Eq,
10583 value.clone(),
10584 ));
10585 }
10586
10587 let mut branches: Vec<(
10588 llkv_expr::expr::ScalarExpr<String>,
10589 llkv_expr::expr::ScalarExpr<String>,
10590 )> = Vec::with_capacity(list_len.saturating_mul(2));
10591
10592 for comparison in &comparisons {
10593 branches.push((
10594 comparison.clone(),
10595 llkv_expr::expr::ScalarExpr::literal(Literal::Int128(1)),
10596 ));
10597 }
10598
10599 for comparison in comparisons {
10600 let comparison_is_null =
10601 llkv_expr::expr::ScalarExpr::is_null(comparison, false);
10602 branches.push((
10603 comparison_is_null,
10604 llkv_expr::expr::ScalarExpr::literal(Literal::Null),
10605 ));
10606 }
10607
10608 let else_expr = Some(llkv_expr::expr::ScalarExpr::literal(Literal::Int128(0)));
10609 let in_result = llkv_expr::expr::ScalarExpr::case(None, branches, else_expr);
10610 let final_expr = if negated {
10611 llkv_expr::expr::ScalarExpr::logical_not(in_result)
10612 } else {
10613 in_result
10614 };
10615
10616 result_stack.push(final_expr);
10617 }
10618 ScalarExitContext::Case {
10619 branch_count,
10620 has_operand,
10621 has_else,
10622 } => {
10623 let else_expr = if has_else {
10624 Some(result_stack.pop().ok_or_else(|| {
10625 Error::Internal(
10626 "translate_scalar: result stack underflow for CASE ELSE".into(),
10627 )
10628 })?)
10629 } else {
10630 None
10631 };
10632
10633 let mut branches_rev = Vec::with_capacity(branch_count);
10634 for _ in 0..branch_count {
10635 let then_expr = result_stack.pop().ok_or_else(|| {
10636 Error::Internal(
10637 "translate_scalar: result stack underflow for CASE THEN".into(),
10638 )
10639 })?;
10640 let when_expr = result_stack.pop().ok_or_else(|| {
10641 Error::Internal(
10642 "translate_scalar: result stack underflow for CASE WHEN".into(),
10643 )
10644 })?;
10645 branches_rev.push((when_expr, then_expr));
10646 }
10647 branches_rev.reverse();
10648
10649 let operand_expr = if has_operand {
10650 Some(result_stack.pop().ok_or_else(|| {
10651 Error::Internal(
10652 "translate_scalar: result stack underflow for CASE operand".into(),
10653 )
10654 })?)
10655 } else {
10656 None
10657 };
10658
10659 let case_expr =
10660 llkv_expr::expr::ScalarExpr::case(operand_expr, branches_rev, else_expr);
10661 result_stack.push(case_expr);
10662 }
10663 ScalarExitContext::IsNull { negated } => {
10664 let inner = result_stack.pop().ok_or_else(|| {
10665 Error::Internal(
10666 "translate_scalar: result stack underflow for IS NULL operand".into(),
10667 )
10668 })?;
10669 result_stack.push(llkv_expr::expr::ScalarExpr::is_null(inner, negated));
10670 }
10671 ScalarExitContext::Between { negated } => {
10672 let high = result_stack.pop().ok_or_else(|| {
10673 Error::Internal(
10674 "translate_scalar: result stack underflow for BETWEEN upper".into(),
10675 )
10676 })?;
10677 let low = result_stack.pop().ok_or_else(|| {
10678 Error::Internal(
10679 "translate_scalar: result stack underflow for BETWEEN lower".into(),
10680 )
10681 })?;
10682 let expr_value = result_stack.pop().ok_or_else(|| {
10683 Error::Internal(
10684 "translate_scalar: result stack underflow for BETWEEN operand".into(),
10685 )
10686 })?;
10687
10688 let between_expr = if negated {
10689 let less_than = llkv_expr::expr::ScalarExpr::compare(
10690 expr_value.clone(),
10691 llkv_expr::expr::CompareOp::Lt,
10692 low.clone(),
10693 );
10694 let greater_than = llkv_expr::expr::ScalarExpr::compare(
10695 expr_value,
10696 llkv_expr::expr::CompareOp::Gt,
10697 high,
10698 );
10699 llkv_expr::expr::ScalarExpr::binary(
10700 less_than,
10701 llkv_expr::expr::BinaryOp::Or,
10702 greater_than,
10703 )
10704 } else {
10705 let greater_or_equal = llkv_expr::expr::ScalarExpr::compare(
10706 expr_value.clone(),
10707 llkv_expr::expr::CompareOp::GtEq,
10708 low,
10709 );
10710 let less_or_equal = llkv_expr::expr::ScalarExpr::compare(
10711 expr_value,
10712 llkv_expr::expr::CompareOp::LtEq,
10713 high,
10714 );
10715 llkv_expr::expr::ScalarExpr::binary(
10716 greater_or_equal,
10717 llkv_expr::expr::BinaryOp::And,
10718 less_or_equal,
10719 )
10720 };
10721 result_stack.push(between_expr);
10722 }
10723 },
10724 }
10725 }
10726
10727 result_stack
10728 .pop()
10729 .ok_or_else(|| Error::Internal("translate_scalar: empty result stack".into()))
10730}
10731
10732fn infer_query_output_type(
10733 engine: &SqlEngine,
10734 plan: &llkv_plan::SelectPlan,
10735) -> SqlResult<DataType> {
10736 if !plan.aggregates.is_empty() {
10737 if plan.aggregates.len() != 1 {
10738 return Err(Error::InvalidArgumentError(
10739 "Scalar subquery must return exactly one column".into(),
10740 ));
10741 }
10742 let agg = &plan.aggregates[0];
10743 return match agg {
10744 llkv_plan::AggregateExpr::CountStar { .. } => Ok(DataType::Int64),
10745 llkv_plan::AggregateExpr::Column { function, .. } => match function {
10746 llkv_plan::AggregateFunction::Count
10747 | llkv_plan::AggregateFunction::SumInt64
10748 | llkv_plan::AggregateFunction::TotalInt64
10749 | llkv_plan::AggregateFunction::MinInt64
10750 | llkv_plan::AggregateFunction::MaxInt64
10751 | llkv_plan::AggregateFunction::CountNulls => Ok(DataType::Int64),
10752 llkv_plan::AggregateFunction::GroupConcat => Ok(DataType::Utf8),
10753 },
10754 };
10755 }
10756
10757 if plan.projections.len() != 1 {
10758 return Err(Error::InvalidArgumentError(
10759 "Scalar subquery must return exactly one column".into(),
10760 ));
10761 }
10762 match &plan.projections[0] {
10763 llkv_plan::SelectProjection::Computed { expr, .. } => infer_expr_type(engine, expr, plan),
10764 llkv_plan::SelectProjection::Column { name, .. } => {
10765 resolve_column_type_in_plan(engine, name, plan)
10766 }
10767 _ => Ok(DataType::Int64),
10768 }
10769}
10770
10771fn infer_expr_type(
10772 engine: &SqlEngine,
10773 expr: &llkv_expr::expr::ScalarExpr<String>,
10774 plan: &llkv_plan::SelectPlan,
10775) -> SqlResult<DataType> {
10776 use llkv_expr::expr::ScalarExpr;
10777 match expr {
10778 ScalarExpr::Literal(lit) => Ok(match lit {
10779 Literal::Int128(_) => DataType::Int64,
10780 Literal::Float64(_) => DataType::Float64,
10781 Literal::Decimal128(v) => DataType::Decimal128(v.precision(), v.scale()),
10782 Literal::Boolean(_) => DataType::Boolean,
10783 Literal::String(_) => DataType::Utf8,
10784 Literal::Date32(_) => DataType::Date32,
10785 Literal::Null => DataType::Null,
10786 Literal::Struct(_) => DataType::Utf8,
10787 Literal::Interval(_) => {
10788 DataType::Interval(arrow::datatypes::IntervalUnit::MonthDayNano)
10789 }
10790 }),
10791 ScalarExpr::Column(name) => resolve_column_type_in_plan(engine, name, plan),
10792 ScalarExpr::Binary { left, right, .. } => {
10793 let l = infer_expr_type(engine, left, plan)?;
10794 let r = infer_expr_type(engine, right, plan)?;
10795 if matches!(l, DataType::Float64) || matches!(r, DataType::Float64) {
10796 Ok(DataType::Float64)
10797 } else if let DataType::Decimal128(_, s1) = l {
10798 if let DataType::Decimal128(_, s2) = r {
10799 Ok(DataType::Decimal128(38, s1.max(s2)))
10800 } else {
10801 Ok(DataType::Decimal128(38, s1))
10802 }
10803 } else if let DataType::Decimal128(_, s2) = r {
10804 Ok(DataType::Decimal128(38, s2))
10805 } else {
10806 Ok(l)
10807 }
10808 }
10809 ScalarExpr::Cast { data_type, .. } => Ok(data_type.clone()),
10810 ScalarExpr::ScalarSubquery(sub) => Ok(sub.data_type.clone()),
10811 ScalarExpr::Aggregate(_) => Ok(DataType::Float64),
10812 _ => Ok(DataType::Int64),
10813 }
10814}
10815
10816fn resolve_column_type_in_plan(
10817 engine: &SqlEngine,
10818 col_name: &str,
10819 plan: &llkv_plan::SelectPlan,
10820) -> SqlResult<DataType> {
10821 for table_ref in &plan.tables {
10822 if let Ok((_, canonical)) = llkv_table::resolvers::canonical_table_name(&table_ref.table)
10823 && let Ok(table) = engine.engine.context().lookup_table(&canonical)
10824 {
10825 let (target_col, matches_alias) = if let Some(alias) = &table_ref.alias {
10826 if col_name.starts_with(alias) && col_name.chars().nth(alias.len()) == Some('.') {
10827 (&col_name[alias.len() + 1..], true)
10828 } else {
10829 (col_name, false)
10830 }
10831 } else {
10832 (col_name, false)
10833 };
10834
10835 let target_col = if !matches_alias
10836 && col_name.starts_with(&table_ref.table)
10837 && col_name.chars().nth(table_ref.table.len()) == Some('.')
10838 {
10839 &col_name[table_ref.table.len() + 1..]
10840 } else {
10841 target_col
10842 };
10843
10844 if let Some(field) = table.schema.resolve(target_col) {
10845 return Ok(field.data_type.clone());
10846 }
10847 }
10848 }
10849 Ok(DataType::Int64)
10850}
10851struct ScalarSubqueryPlanner<'engine, 'vec> {
10852 engine: &'engine SqlEngine,
10853 scalar_subqueries: &'vec mut Vec<llkv_plan::ScalarSubquery>,
10854}
10855
10856impl<'engine, 'vec> ScalarSubqueryResolver for ScalarSubqueryPlanner<'engine, 'vec> {
10857 fn handle_scalar_subquery(
10858 &mut self,
10859 subquery: &Query,
10860 resolver: &IdentifierResolver<'_>,
10861 context: &IdentifierContext,
10862 outer_scopes: &[IdentifierContext],
10863 ) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
10864 let mut nested_scopes = outer_scopes.to_vec();
10865 nested_scopes.push(context.clone());
10866 let mut tracker = SubqueryCorrelatedColumnTracker::new();
10867 let mut nested_filter_subqueries = Vec::new();
10868
10869 let plan = self.engine.build_select_plan_internal(
10870 subquery.clone(),
10871 resolver,
10872 &nested_scopes,
10873 &mut nested_filter_subqueries,
10874 Some(&mut tracker),
10875 )?;
10876
10877 debug_assert!(nested_filter_subqueries.is_empty());
10878
10879 let id = u32::try_from(self.scalar_subqueries.len()).map_err(|_| {
10880 Error::InvalidArgumentError(
10881 "scalar subquery limit exceeded for current query".to_string(),
10882 )
10883 })?;
10884 let subquery_id = llkv_expr::SubqueryId(id);
10885 self.scalar_subqueries.push(llkv_plan::ScalarSubquery {
10886 id: subquery_id,
10887 plan: Box::new(plan.clone()),
10888 correlated_columns: tracker.into_columns(),
10889 });
10890
10891 let data_type = infer_query_output_type(self.engine, &plan)?;
10892 Ok(llkv_expr::expr::ScalarExpr::scalar_subquery(
10893 subquery_id,
10894 data_type,
10895 ))
10896 }
10897}
10898
10899fn build_abs_case_expr(
10900 arg: llkv_expr::expr::ScalarExpr<String>,
10901) -> llkv_expr::expr::ScalarExpr<String> {
10902 use llkv_expr::expr::{BinaryOp, CompareOp, ScalarExpr};
10903
10904 let zero = ScalarExpr::literal(Literal::Int128(0));
10905 let condition = ScalarExpr::compare(arg.clone(), CompareOp::Lt, zero.clone());
10906 let negated = ScalarExpr::binary(zero.clone(), BinaryOp::Subtract, arg.clone());
10907
10908 ScalarExpr::case(None, vec![(condition, negated)], Some(arg))
10909}
10910
10911fn literal_from_value(value: &ValueWithSpan) -> SqlResult<llkv_expr::expr::ScalarExpr<String>> {
10912 match &value.value {
10913 Value::Placeholder(name) => {
10914 let index = register_placeholder(name)?;
10915 Ok(llkv_expr::expr::ScalarExpr::literal(literal_placeholder(
10916 index,
10917 )))
10918 }
10919 Value::Number(text, _) => {
10920 if text.contains(['.', 'e', 'E']) {
10921 if let Ok(decimal) = text.parse::<DecimalValue>() {
10923 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Decimal128(
10924 decimal,
10925 )))
10926 } else {
10927 let parsed = text.parse::<f64>().map_err(|err| {
10928 Error::InvalidArgumentError(format!("invalid float literal: {err}"))
10929 })?;
10930 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Float64(
10931 parsed,
10932 )))
10933 }
10934 } else {
10935 let parsed = text.parse::<i128>().map_err(|err| {
10936 Error::InvalidArgumentError(format!("invalid integer literal: {err}"))
10937 })?;
10938 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Int128(
10939 parsed,
10940 )))
10941 }
10942 }
10943 Value::Boolean(value) => Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Boolean(
10944 *value,
10945 ))),
10946 Value::Null => Ok(llkv_expr::expr::ScalarExpr::literal(Literal::Null)),
10947 other => {
10948 if let Some(text) = other.clone().into_string() {
10949 Ok(llkv_expr::expr::ScalarExpr::literal(Literal::String(text)))
10950 } else {
10951 Err(Error::InvalidArgumentError(format!(
10952 "unsupported literal: {other:?}"
10953 )))
10954 }
10955 }
10956 }
10957}
10958
10959fn resolve_assignment_column_name(target: &AssignmentTarget) -> SqlResult<String> {
10960 match target {
10961 AssignmentTarget::ColumnName(name) => {
10962 if name.0.len() != 1 {
10963 return Err(Error::InvalidArgumentError(
10964 "qualified column names in UPDATE assignments are not supported yet".into(),
10965 ));
10966 }
10967 match &name.0[0] {
10968 ObjectNamePart::Identifier(ident) => Ok(ident.value.clone()),
10969 other => Err(Error::InvalidArgumentError(format!(
10970 "unsupported column reference in UPDATE assignment: {other:?}"
10971 ))),
10972 }
10973 }
10974 AssignmentTarget::Tuple(_) => Err(Error::InvalidArgumentError(
10975 "tuple assignments are not supported yet".into(),
10976 )),
10977 }
10978}
10979
10980fn arrow_type_from_sql(data_type: &SqlDataType) -> SqlResult<arrow::datatypes::DataType> {
10981 use arrow::datatypes::DataType;
10982 match data_type {
10983 SqlDataType::Int(_)
10984 | SqlDataType::Integer(_)
10985 | SqlDataType::BigInt(_)
10986 | SqlDataType::SmallInt(_)
10987 | SqlDataType::TinyInt(_) => Ok(DataType::Int64),
10988 SqlDataType::Float(_)
10989 | SqlDataType::Real
10990 | SqlDataType::Double(_)
10991 | SqlDataType::DoublePrecision => Ok(DataType::Float64),
10992 SqlDataType::Text
10993 | SqlDataType::String(_)
10994 | SqlDataType::Varchar(_)
10995 | SqlDataType::Char(_)
10996 | SqlDataType::Uuid => Ok(DataType::Utf8),
10997 SqlDataType::Date => Ok(DataType::Date32),
10998 SqlDataType::Decimal(exact_number_info) | SqlDataType::Numeric(exact_number_info) => {
10999 match exact_number_info {
11001 sqlparser::ast::ExactNumberInfo::PrecisionAndScale(p, s) => {
11002 Ok(DataType::Decimal128(*p as u8, *s as i8))
11003 }
11004 sqlparser::ast::ExactNumberInfo::Precision(p) => {
11005 Ok(DataType::Decimal128(*p as u8, 0))
11007 }
11008 sqlparser::ast::ExactNumberInfo::None => {
11009 Ok(DataType::Decimal128(38, 0))
11011 }
11012 }
11013 }
11014 SqlDataType::Boolean => Ok(DataType::Boolean),
11015 SqlDataType::Custom(name, args) => {
11016 if name.0.len() == 1
11017 && let ObjectNamePart::Identifier(ident) = &name.0[0]
11018 && ident.value.eq_ignore_ascii_case("row")
11019 {
11020 return row_type_to_arrow(data_type, args);
11021 }
11022 Err(Error::InvalidArgumentError(format!(
11023 "unsupported SQL data type: {data_type:?}"
11024 )))
11025 }
11026 other => Err(Error::InvalidArgumentError(format!(
11027 "unsupported SQL data type: {other:?}"
11028 ))),
11029 }
11030}
11031
11032fn row_type_to_arrow(
11033 data_type: &SqlDataType,
11034 tokens: &[String],
11035) -> SqlResult<arrow::datatypes::DataType> {
11036 use arrow::datatypes::{DataType, Field, FieldRef, Fields};
11037
11038 let row_str = data_type.to_string();
11039 if tokens.is_empty() {
11040 return Err(Error::InvalidArgumentError(
11041 "ROW type must define at least one field".into(),
11042 ));
11043 }
11044
11045 let dialect = GenericDialect {};
11046 let field_definitions = resolve_row_field_types(tokens, &dialect).map_err(|err| {
11047 Error::InvalidArgumentError(format!("unable to parse ROW type '{row_str}': {err}"))
11048 })?;
11049
11050 let mut fields: Vec<FieldRef> = Vec::with_capacity(field_definitions.len());
11051 for (field_name, field_type) in field_definitions {
11052 let arrow_field_type = arrow_type_from_sql(&field_type)?;
11053 fields.push(Arc::new(Field::new(field_name, arrow_field_type, true)));
11054 }
11055
11056 let struct_fields: Fields = fields.into();
11057 Ok(DataType::Struct(struct_fields))
11058}
11059
11060fn resolve_row_field_types(
11061 tokens: &[String],
11062 dialect: &GenericDialect,
11063) -> SqlResult<Vec<(String, SqlDataType)>> {
11064 if tokens.is_empty() {
11065 return Err(Error::InvalidArgumentError(
11066 "ROW type must define at least one field".into(),
11067 ));
11068 }
11069
11070 let mut start = 0;
11071 let mut end = tokens.len();
11072 if tokens[start] == "(" {
11073 if end == 0 || tokens[end - 1] != ")" {
11074 return Err(Error::InvalidArgumentError(
11075 "ROW type is missing closing ')'".into(),
11076 ));
11077 }
11078 start += 1;
11079 end -= 1;
11080 } else if tokens[end - 1] == ")" {
11081 return Err(Error::InvalidArgumentError(
11082 "ROW type contains unmatched ')'".into(),
11083 ));
11084 }
11085
11086 let slice = &tokens[start..end];
11087 if slice.is_empty() {
11088 return Err(Error::InvalidArgumentError(
11089 "ROW type did not provide any field definitions".into(),
11090 ));
11091 }
11092
11093 let mut fields = Vec::new();
11094 let mut index = 0;
11095
11096 while index < slice.len() {
11097 if slice[index] == "," {
11098 index += 1;
11099 continue;
11100 }
11101
11102 let field_name = normalize_row_field_name(&slice[index])?;
11103 index += 1;
11104
11105 if index >= slice.len() {
11106 return Err(Error::InvalidArgumentError(format!(
11107 "ROW field '{field_name}' is missing a type specification"
11108 )));
11109 }
11110
11111 let mut last_success: Option<(usize, SqlDataType)> = None;
11112 let mut type_end = index;
11113
11114 while type_end <= slice.len() {
11115 let candidate = slice[index..type_end].join(" ");
11116 if candidate.trim().is_empty() {
11117 type_end += 1;
11118 continue;
11119 }
11120
11121 if let Ok(parsed_type) = parse_sql_data_type(&candidate, dialect) {
11122 last_success = Some((type_end, parsed_type));
11123 }
11124
11125 if type_end == slice.len() {
11126 break;
11127 }
11128
11129 if slice[type_end] == "," && last_success.is_some() {
11130 break;
11131 }
11132
11133 type_end += 1;
11134 }
11135
11136 let Some((next_index, data_type)) = last_success else {
11137 return Err(Error::InvalidArgumentError(format!(
11138 "failed to parse ROW field type for '{field_name}'"
11139 )));
11140 };
11141
11142 fields.push((field_name, data_type));
11143 index = next_index;
11144
11145 if index < slice.len() && slice[index] == "," {
11146 index += 1;
11147 }
11148 }
11149
11150 if fields.is_empty() {
11151 return Err(Error::InvalidArgumentError(
11152 "ROW type did not provide any field definitions".into(),
11153 ));
11154 }
11155
11156 Ok(fields)
11157}
11158
11159fn parse_sql_with_recursion_limit(
11173 dialect: &GenericDialect,
11174 sql: &str,
11175) -> Result<Vec<Statement>, sqlparser::parser::ParserError> {
11176 Parser::new(dialect)
11177 .with_recursion_limit(PARSER_RECURSION_LIMIT)
11178 .try_with_sql(sql)?
11179 .parse_statements()
11180}
11181
11182fn normalize_row_field_name(raw: &str) -> SqlResult<String> {
11183 let trimmed = raw.trim();
11184 if trimmed.is_empty() {
11185 return Err(Error::InvalidArgumentError(
11186 "ROW field name must not be empty".into(),
11187 ));
11188 }
11189
11190 if let Some(stripped) = trimmed.strip_prefix('"') {
11191 let without_end = stripped.strip_suffix('"').ok_or_else(|| {
11192 Error::InvalidArgumentError(format!("unterminated quoted ROW field name: {trimmed}"))
11193 })?;
11194 let name = without_end.replace("\"\"", "\"");
11195 return Ok(name);
11196 }
11197
11198 Ok(trimmed.to_string())
11199}
11200
11201fn parse_sql_data_type(type_str: &str, dialect: &GenericDialect) -> SqlResult<SqlDataType> {
11202 let trimmed = type_str.trim();
11203 let sql = format!("CREATE TABLE __row(__field {trimmed});");
11204 let statements = parse_sql_with_recursion_limit(dialect, &sql).map_err(|err| {
11205 Error::InvalidArgumentError(format!("failed to parse ROW field type '{trimmed}': {err}"))
11206 })?;
11207
11208 let stmt = statements.into_iter().next().ok_or_else(|| {
11209 Error::InvalidArgumentError(format!(
11210 "ROW field type '{trimmed}' did not produce a statement"
11211 ))
11212 })?;
11213
11214 match stmt {
11215 Statement::CreateTable(table) => table
11216 .columns
11217 .first()
11218 .map(|col| col.data_type.clone())
11219 .ok_or_else(|| {
11220 Error::InvalidArgumentError(format!(
11221 "ROW field type '{trimmed}' missing column definition"
11222 ))
11223 }),
11224 other => Err(Error::InvalidArgumentError(format!(
11225 "unexpected statement while parsing ROW field type: {other:?}"
11226 ))),
11227 }
11228}
11229
11230type ExtractValuesResult = Option<(Vec<Vec<PlanValue>>, Vec<String>)>;
11233
11234#[allow(clippy::type_complexity)]
11235fn extract_values_from_derived_table(from: &[TableWithJoins]) -> SqlResult<ExtractValuesResult> {
11236 if from.len() != 1 {
11237 return Ok(None);
11238 }
11239
11240 let table_with_joins = &from[0];
11241 if !table_with_joins.joins.is_empty() {
11242 return Ok(None);
11243 }
11244
11245 match &table_with_joins.relation {
11246 TableFactor::Derived {
11247 subquery, alias, ..
11248 } => {
11249 let values = match subquery.body.as_ref() {
11251 SetExpr::Values(v) => v,
11252 _ => return Ok(None),
11253 };
11254
11255 let column_names = if let Some(alias) = alias {
11257 alias
11258 .columns
11259 .iter()
11260 .map(|col_def| col_def.name.value.clone())
11261 .collect::<Vec<_>>()
11262 } else {
11263 if values.rows.is_empty() {
11265 return Err(Error::InvalidArgumentError(
11266 "VALUES expression must have at least one row".into(),
11267 ));
11268 }
11269 let first_row = &values.rows[0];
11270 (0..first_row.len())
11271 .map(|i| format!("column{}", i))
11272 .collect()
11273 };
11274
11275 if values.rows.is_empty() {
11277 return Err(Error::InvalidArgumentError(
11278 "VALUES expression must have at least one row".into(),
11279 ));
11280 }
11281
11282 let mut rows = Vec::with_capacity(values.rows.len());
11283 for row in &values.rows {
11284 if row.len() != column_names.len() {
11285 return Err(Error::InvalidArgumentError(format!(
11286 "VALUES row has {} columns but table alias specifies {} columns",
11287 row.len(),
11288 column_names.len()
11289 )));
11290 }
11291
11292 let mut converted_row = Vec::with_capacity(row.len());
11293 for expr in row {
11294 let value = SqlValue::try_from_expr(expr)?;
11295 converted_row.push(PlanValue::from(value));
11296 }
11297 rows.push(converted_row);
11298 }
11299
11300 Ok(Some((rows, column_names)))
11301 }
11302 _ => Ok(None),
11303 }
11304}
11305
11306fn extract_constant_select_rows(select: &Select) -> SqlResult<Option<Vec<Vec<PlanValue>>>> {
11307 if !select.from.is_empty() {
11308 return Ok(None);
11309 }
11310
11311 if select.selection.is_some()
11312 || select.having.is_some()
11313 || !select.named_window.is_empty()
11314 || select.qualify.is_some()
11315 || select.distinct.is_some()
11316 || select.top.is_some()
11317 || select.into.is_some()
11318 || select.prewhere.is_some()
11319 || !select.lateral_views.is_empty()
11320 || select.value_table_mode.is_some()
11321 || !group_by_is_empty(&select.group_by)
11322 {
11323 return Err(Error::InvalidArgumentError(
11324 "constant SELECT statements do not support advanced clauses".into(),
11325 ));
11326 }
11327
11328 if select.projection.is_empty() {
11329 return Err(Error::InvalidArgumentError(
11330 "constant SELECT requires at least one projection".into(),
11331 ));
11332 }
11333
11334 let mut row: Vec<PlanValue> = Vec::with_capacity(select.projection.len());
11335 for item in &select.projection {
11336 let expr = match item {
11337 SelectItem::UnnamedExpr(expr) => expr,
11338 SelectItem::ExprWithAlias { expr, .. } => expr,
11339 other => {
11340 return Err(Error::InvalidArgumentError(format!(
11341 "unsupported projection in constant SELECT: {other:?}"
11342 )));
11343 }
11344 };
11345
11346 let value = SqlValue::try_from_expr(expr)?;
11347 row.push(PlanValue::from(value));
11348 }
11349
11350 Ok(Some(vec![row]))
11351}
11352
11353fn query_references_information_schema(query: &Query) -> bool {
11358 fn check_set_expr(expr: &SetExpr) -> bool {
11359 match expr {
11360 SetExpr::Select(select) => {
11361 for table_with_joins in &select.from {
11362 if check_table_with_joins(table_with_joins) {
11363 return true;
11364 }
11365 }
11366 false
11367 }
11368 SetExpr::Query(query) => check_set_expr(&query.body),
11369 SetExpr::SetOperation { left, right, .. } => {
11370 check_set_expr(left) || check_set_expr(right)
11371 }
11372 SetExpr::Values(_)
11373 | SetExpr::Insert(_)
11374 | SetExpr::Update(_)
11375 | SetExpr::Table(_)
11376 | SetExpr::Delete(_)
11377 | SetExpr::Merge(_) => false,
11378 }
11379 }
11380
11381 fn check_table_with_joins(item: &TableWithJoins) -> bool {
11382 if check_table_factor(&item.relation) {
11383 return true;
11384 }
11385 for join in &item.joins {
11386 if check_table_factor(&join.relation) {
11387 return true;
11388 }
11389 }
11390 false
11391 }
11392
11393 fn check_table_factor(factor: &TableFactor) -> bool {
11394 match factor {
11395 TableFactor::Table { name, .. } => {
11396 for part in &name.0 {
11398 if let ObjectNamePart::Identifier(ident) = part {
11399 let value_lower = ident.value.to_ascii_lowercase();
11400 tracing::debug!("Checking table name part: '{}'", value_lower);
11401 if value_lower == "information_schema" {
11403 tracing::debug!(" -> Found information_schema!");
11404 return true;
11405 }
11406 if value_lower.starts_with("information_schema.") {
11409 tracing::debug!(" -> Found information_schema. prefix!");
11410 return true;
11411 }
11412 }
11413 }
11414 false
11415 }
11416 TableFactor::NestedJoin {
11417 table_with_joins, ..
11418 } => check_table_with_joins(table_with_joins),
11419 TableFactor::Derived { subquery, .. } => check_set_expr(&subquery.body),
11420 _ => false,
11421 }
11422 }
11423
11424 check_set_expr(&query.body)
11425}
11426
11427fn extract_single_table(from: &[TableWithJoins]) -> SqlResult<(String, String)> {
11428 if from.len() != 1 {
11429 return Err(Error::InvalidArgumentError(
11430 "queries over multiple tables are not supported yet".into(),
11431 ));
11432 }
11433 let item = &from[0];
11434
11435 if table_with_joins_has_join(item) {
11436 return Err(Error::InvalidArgumentError(
11437 "JOIN clauses are not supported yet".into(),
11438 ));
11439 }
11440 match &item.relation {
11441 TableFactor::Table { name, .. } => canonical_object_name(name),
11442 TableFactor::Derived { alias, .. } => {
11443 let table_name = alias
11446 .as_ref()
11447 .map(|a| a.name.value.clone())
11448 .unwrap_or_else(|| "derived".to_string());
11449 let canonical = table_name.to_ascii_lowercase();
11450 Ok((table_name, canonical))
11451 }
11452 TableFactor::NestedJoin { .. } => Err(Error::InvalidArgumentError(
11453 "JOIN clauses are not supported yet".into(),
11454 )),
11455 _ => Err(Error::InvalidArgumentError(
11456 "queries require a plain table name or derived table".into(),
11457 )),
11458 }
11459}
11460
11461fn table_with_joins_has_join(item: &TableWithJoins) -> bool {
11463 if !item.joins.is_empty() {
11464 return true;
11465 }
11466 match &item.relation {
11467 TableFactor::NestedJoin {
11468 table_with_joins, ..
11469 } => table_with_joins_has_join(table_with_joins.as_ref()),
11470 _ => false,
11471 }
11472}
11473
11474type ExtractedJoinData = (
11478 Vec<llkv_plan::TableRef>,
11479 Vec<llkv_plan::JoinMetadata>,
11480 Vec<Option<SqlExpr>>,
11481);
11482
11483fn extract_tables(from: &[TableWithJoins]) -> SqlResult<ExtractedJoinData> {
11488 let mut tables = Vec::new();
11489 let mut join_metadata = Vec::new();
11490 let mut join_filters = Vec::new();
11491
11492 for item in from {
11493 let prior_table_len = tables.len();
11494 let prior_join_len = join_metadata.len();
11495
11496 flatten_table_with_joins(item, &mut tables, &mut join_metadata, &mut join_filters)?;
11497
11498 let new_table_len = tables.len();
11499 if prior_table_len > 0 && new_table_len > prior_table_len {
11500 join_metadata.insert(
11501 prior_join_len,
11502 llkv_plan::JoinMetadata {
11503 left_table_index: prior_table_len - 1,
11504 join_type: llkv_plan::JoinPlan::Inner,
11505 on_condition: None,
11506 },
11507 );
11508 join_filters.insert(prior_join_len, None);
11509 }
11510 }
11511
11512 Ok((tables, join_metadata, join_filters))
11513}
11514
11515fn push_table_factor(
11516 factor: &TableFactor,
11517 tables: &mut Vec<llkv_plan::TableRef>,
11518 join_metadata: &mut Vec<llkv_plan::JoinMetadata>,
11519 join_filters: &mut Vec<Option<SqlExpr>>,
11520) -> SqlResult<()> {
11521 match factor {
11522 TableFactor::Table { name, alias, .. } => {
11523 let (schema_opt, table) = parse_schema_qualified_name(name)?;
11526 let schema = schema_opt.unwrap_or_default();
11527 let alias_name = alias.as_ref().map(|a| a.name.value.clone());
11528 tables.push(llkv_plan::TableRef::with_alias(schema, table, alias_name));
11529 Ok(())
11530 }
11531 TableFactor::NestedJoin {
11532 table_with_joins,
11533 alias,
11534 } => {
11535 if alias.is_some() {
11536 return Err(Error::InvalidArgumentError(
11537 "parenthesized JOINs with aliases are not supported yet".into(),
11538 ));
11539 }
11540 flatten_table_with_joins(
11541 table_with_joins.as_ref(),
11542 tables,
11543 join_metadata,
11544 join_filters,
11545 )
11546 }
11547 TableFactor::Derived { .. } => Err(Error::InvalidArgumentError(
11548 "JOIN clauses require base tables; derived tables are not supported".into(),
11549 )),
11550 _ => Err(Error::InvalidArgumentError(
11551 "queries require a plain table name".into(),
11552 )),
11553 }
11554}
11555
11556fn flatten_table_with_joins(
11557 item: &TableWithJoins,
11558 tables: &mut Vec<llkv_plan::TableRef>,
11559 join_metadata: &mut Vec<llkv_plan::JoinMetadata>,
11560 join_filters: &mut Vec<Option<SqlExpr>>,
11561) -> SqlResult<()> {
11562 push_table_factor(&item.relation, tables, join_metadata, join_filters)?;
11563
11564 for join in &item.joins {
11565 let left_table_index = tables.len() - 1;
11566
11567 match &join.join_operator {
11568 JoinOperator::CrossJoin(JoinConstraint::None)
11569 | JoinOperator::Join(JoinConstraint::None)
11570 | JoinOperator::Inner(JoinConstraint::None) => {
11571 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
11572 join_metadata.push(llkv_plan::JoinMetadata {
11573 left_table_index,
11574 join_type: llkv_plan::JoinPlan::Inner,
11575 on_condition: None,
11576 });
11577 join_filters.push(None);
11578 }
11579 JoinOperator::Join(JoinConstraint::On(condition))
11580 | JoinOperator::Inner(JoinConstraint::On(condition)) => {
11581 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
11582 join_filters.push(Some(condition.clone()));
11583 join_metadata.push(llkv_plan::JoinMetadata {
11584 left_table_index,
11585 join_type: llkv_plan::JoinPlan::Inner,
11586 on_condition: None,
11587 });
11588 }
11589 JoinOperator::Left(JoinConstraint::On(condition))
11590 | JoinOperator::LeftOuter(JoinConstraint::On(condition)) => {
11591 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
11592 join_filters.push(Some(condition.clone()));
11593 join_metadata.push(llkv_plan::JoinMetadata {
11594 left_table_index,
11595 join_type: llkv_plan::JoinPlan::Left,
11596 on_condition: None,
11597 });
11598 }
11599 JoinOperator::Left(JoinConstraint::None)
11600 | JoinOperator::LeftOuter(JoinConstraint::None) => {
11601 push_table_factor(&join.relation, tables, join_metadata, join_filters)?;
11602 join_metadata.push(llkv_plan::JoinMetadata {
11603 left_table_index,
11604 join_type: llkv_plan::JoinPlan::Left,
11605 on_condition: None,
11606 });
11607 join_filters.push(None);
11608 }
11609 JoinOperator::CrossJoin(_) => {
11610 return Err(Error::InvalidArgumentError(
11611 "CROSS JOIN with constraints is not supported".into(),
11612 ));
11613 }
11614 JoinOperator::Join(JoinConstraint::Using(_))
11615 | JoinOperator::Inner(JoinConstraint::Using(_))
11616 | JoinOperator::Left(JoinConstraint::Using(_))
11617 | JoinOperator::LeftOuter(JoinConstraint::Using(_)) => {
11618 return Err(Error::InvalidArgumentError(
11619 "JOIN ... USING (...) is not supported yet".into(),
11620 ));
11621 }
11622 JoinOperator::Join(JoinConstraint::Natural)
11623 | JoinOperator::Inner(JoinConstraint::Natural)
11624 | JoinOperator::Left(JoinConstraint::Natural)
11625 | JoinOperator::LeftOuter(JoinConstraint::Natural)
11626 | JoinOperator::Right(_)
11627 | JoinOperator::RightOuter(_)
11628 | JoinOperator::FullOuter(_)
11629 | JoinOperator::Semi(_)
11630 | JoinOperator::LeftSemi(_)
11631 | JoinOperator::LeftAnti(_)
11632 | JoinOperator::RightSemi(_)
11633 | JoinOperator::RightAnti(_)
11634 | JoinOperator::CrossApply
11635 | JoinOperator::OuterApply
11636 | JoinOperator::Anti(_)
11637 | JoinOperator::StraightJoin(_) => {
11638 return Err(Error::InvalidArgumentError(
11639 "only INNER JOIN and LEFT JOIN with optional ON constraints are supported"
11640 .into(),
11641 ));
11642 }
11643 other => {
11644 return Err(Error::InvalidArgumentError(format!(
11645 "unsupported JOIN clause: {other:?}"
11646 )));
11647 }
11648 }
11649 }
11650
11651 Ok(())
11652}
11653
11654fn group_by_is_empty(expr: &GroupByExpr) -> bool {
11655 matches!(
11656 expr,
11657 GroupByExpr::Expressions(exprs, modifiers)
11658 if exprs.is_empty() && modifiers.is_empty()
11659 )
11660}
11661
11662fn convert_value_table_mode(mode: sqlparser::ast::ValueTableMode) -> llkv_plan::ValueTableMode {
11663 use llkv_plan::ValueTableMode as PlanMode;
11664 match mode {
11665 sqlparser::ast::ValueTableMode::AsStruct => PlanMode::AsStruct,
11666 sqlparser::ast::ValueTableMode::AsValue => PlanMode::AsValue,
11667 sqlparser::ast::ValueTableMode::DistinctAsStruct => PlanMode::DistinctAsStruct,
11668 sqlparser::ast::ValueTableMode::DistinctAsValue => PlanMode::DistinctAsValue,
11669 }
11670}
11671#[cfg(test)]
11672mod tests {
11673 use super::*;
11674 use arrow::array::{Array, Float64Array, Int32Array, Int64Array, StringArray};
11675 use arrow::record_batch::RecordBatch;
11676 use llkv_storage::pager::MemPager;
11677
11678 fn extract_string_options(batches: &[RecordBatch]) -> Vec<Option<String>> {
11679 let mut values: Vec<Option<String>> = Vec::new();
11680 for batch in batches {
11681 let column = batch
11682 .column(0)
11683 .as_any()
11684 .downcast_ref::<StringArray>()
11685 .expect("string column");
11686 for idx in 0..column.len() {
11687 if column.is_null(idx) {
11688 values.push(None);
11689 } else {
11690 values.push(Some(column.value(idx).to_string()));
11691 }
11692 }
11693 }
11694 values
11695 }
11696
11697 #[test]
11698 fn set_constraint_mode_updates_session() {
11699 let pager = Arc::new(MemPager::default());
11700 let engine = SqlEngine::new(pager);
11701
11702 assert_eq!(
11703 engine.session().constraint_enforcement_mode(),
11704 ConstraintEnforcementMode::Immediate
11705 );
11706
11707 engine
11708 .execute("SET constraint_enforcement_mode = deferred")
11709 .expect("set deferred mode");
11710
11711 assert_eq!(
11712 engine.session().constraint_enforcement_mode(),
11713 ConstraintEnforcementMode::Deferred
11714 );
11715
11716 engine
11717 .execute("SET constraint_enforcement_mode = IMMEDIATE")
11718 .expect("set immediate mode");
11719
11720 assert_eq!(
11721 engine.session().constraint_enforcement_mode(),
11722 ConstraintEnforcementMode::Immediate
11723 );
11724 }
11725
11726 #[test]
11727 fn set_constraint_mode_is_session_scoped() {
11728 let pager = Arc::new(MemPager::default());
11729 let engine = SqlEngine::new(Arc::clone(&pager));
11730 let shared_context = engine.runtime_context();
11731 let peer = SqlEngine::with_context(
11732 Arc::clone(&shared_context),
11733 engine.default_nulls_first_for_tests(),
11734 );
11735
11736 engine
11737 .execute("SET constraint_enforcement_mode = deferred")
11738 .expect("set deferred mode");
11739
11740 assert_eq!(
11741 engine.session().constraint_enforcement_mode(),
11742 ConstraintEnforcementMode::Deferred
11743 );
11744 assert_eq!(
11745 peer.session().constraint_enforcement_mode(),
11746 ConstraintEnforcementMode::Immediate
11747 );
11748 }
11749
11750 #[test]
11751 fn test_interval_expr_structure() {
11752 use sqlparser::ast::{BinaryOperator, Expr as SqlExprAst, Query, SetExpr, Statement};
11753 use sqlparser::dialect::GenericDialect;
11754
11755 let dialect = GenericDialect {};
11756 let sql = "SELECT CAST('1998-12-01' AS DATE) - INTERVAL '90' DAY";
11757 let statements = Parser::parse_sql(&dialect, sql).unwrap();
11758
11759 assert_eq!(statements.len(), 1, "expected single statement");
11760
11761 let Statement::Query(query) = &statements[0] else {
11762 panic!("expected Query statement");
11763 };
11764
11765 let Query { body, .. } = query.as_ref();
11766 let SetExpr::Select(select) = body.as_ref() else {
11767 panic!("expected Select body");
11768 };
11769
11770 assert_eq!(select.projection.len(), 1, "expected single projection");
11771
11772 match &select.projection[0] {
11774 sqlparser::ast::SelectItem::UnnamedExpr(SqlExprAst::BinaryOp { left, op, right }) => {
11775 assert!(
11777 matches!(left.as_ref(), SqlExprAst::Cast { .. }),
11778 "expected CAST on left"
11779 );
11780
11781 assert_eq!(*op, BinaryOperator::Minus, "expected Minus operator");
11783
11784 assert!(
11786 matches!(right.as_ref(), SqlExprAst::Interval(_)),
11787 "expected Interval on right"
11788 );
11789
11790 if let SqlExprAst::Interval(interval) = right.as_ref() {
11791 assert_eq!(
11792 interval.leading_field,
11793 Some(sqlparser::ast::DateTimeField::Day)
11794 );
11795 }
11796 }
11797 other => panic!("unexpected projection structure: {other:?}"),
11798 }
11799 }
11800
11801 #[test]
11802 fn test_insert_batching_across_calls() {
11803 let engine = SqlEngine::new(Arc::new(MemPager::default()));
11804
11805 engine.execute("CREATE TABLE test (id INTEGER)").unwrap();
11807
11808 engine.execute("INSERT INTO test VALUES (1)").unwrap();
11810 engine.execute("INSERT INTO test VALUES (2)").unwrap();
11811
11812 let result = engine.execute("SELECT * FROM test ORDER BY id").unwrap();
11814 let select_result = result
11815 .into_iter()
11816 .find_map(|res| match res {
11817 RuntimeStatementResult::Select { execution, .. } => {
11818 Some(execution.collect().unwrap())
11819 }
11820 _ => None,
11821 })
11822 .expect("expected SELECT result in response");
11823 let batches = select_result;
11824 assert_eq!(
11825 batches[0].num_rows(),
11826 2,
11827 "Should have 2 rows after cross-call batching"
11828 );
11829 }
11830
11831 #[test]
11832 fn create_insert_select_roundtrip() {
11833 let pager = Arc::new(MemPager::default());
11834 let engine = SqlEngine::new(pager);
11835
11836 let result = engine
11837 .execute("CREATE TABLE people (id INT NOT NULL, name TEXT NOT NULL)")
11838 .expect("create table");
11839 assert!(matches!(
11840 result[0],
11841 RuntimeStatementResult::CreateTable { .. }
11842 ));
11843
11844 let result = engine
11845 .execute("INSERT INTO people (id, name) VALUES (1, 'alice'), (2, 'bob')")
11846 .expect("insert rows");
11847 assert!(matches!(
11848 result[0],
11849 RuntimeStatementResult::Insert {
11850 rows_inserted: 2,
11851 ..
11852 }
11853 ));
11854
11855 let mut result = engine
11856 .execute("SELECT name FROM people WHERE id = 2")
11857 .expect("select rows");
11858 let select_result = result.remove(0);
11859 let batches = match select_result {
11860 RuntimeStatementResult::Select { execution, .. } => {
11861 execution.collect().expect("collect batches")
11862 }
11863 _ => panic!("expected select result"),
11864 };
11865 assert_eq!(batches.len(), 1);
11866 let column = batches[0]
11867 .column(0)
11868 .as_any()
11869 .downcast_ref::<StringArray>()
11870 .expect("string column");
11871 assert_eq!(column.len(), 1);
11872 assert_eq!(column.value(0), "bob");
11873 }
11874
11875 #[test]
11876 fn insert_select_constant_including_null() {
11877 let pager = Arc::new(MemPager::default());
11878 let engine = SqlEngine::new(pager);
11879
11880 engine
11881 .execute("CREATE TABLE integers(i INTEGER)")
11882 .expect("create table");
11883
11884 let result = engine
11885 .execute("INSERT INTO integers SELECT 42")
11886 .expect("insert literal");
11887 assert!(matches!(
11888 result[0],
11889 RuntimeStatementResult::Insert {
11890 rows_inserted: 1,
11891 ..
11892 }
11893 ));
11894
11895 let result = engine
11896 .execute("INSERT INTO integers SELECT CAST(NULL AS VARCHAR)")
11897 .expect("insert null literal");
11898 assert!(matches!(
11899 result[0],
11900 RuntimeStatementResult::Insert {
11901 rows_inserted: 1,
11902 ..
11903 }
11904 ));
11905
11906 let mut result = engine
11907 .execute("SELECT * FROM integers")
11908 .expect("select rows");
11909 let select_result = result.remove(0);
11910 let batches = match select_result {
11911 RuntimeStatementResult::Select { execution, .. } => {
11912 execution.collect().expect("collect batches")
11913 }
11914 _ => panic!("expected select result"),
11915 };
11916
11917 let mut values: Vec<Option<i64>> = Vec::new();
11918 for batch in &batches {
11919 let column = batch
11920 .column(0)
11921 .as_any()
11922 .downcast_ref::<Int64Array>()
11923 .expect("int column");
11924 for idx in 0..column.len() {
11925 if column.is_null(idx) {
11926 values.push(None);
11927 } else {
11928 values.push(Some(column.value(idx)));
11929 }
11930 }
11931 }
11932
11933 assert_eq!(values, vec![Some(42), None]);
11934 }
11935
11936 #[test]
11937 fn not_null_comparison_filters_all_rows() {
11938 let pager = Arc::new(MemPager::default());
11939 let engine = SqlEngine::new(pager);
11940
11941 engine
11942 .execute("CREATE TABLE single(col INTEGER)")
11943 .expect("create table");
11944 engine
11945 .execute("INSERT INTO single VALUES (1)")
11946 .expect("insert row");
11947
11948 let batches = engine
11949 .sql("SELECT * FROM single WHERE NOT ( NULL ) >= NULL")
11950 .expect("run constant null comparison");
11951
11952 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
11953 assert_eq!(total_rows, 0, "expected filter to remove all rows");
11954 }
11955
11956 #[test]
11957 fn not_null_in_list_filters_all_rows() {
11958 let pager = Arc::new(MemPager::default());
11959 let engine = SqlEngine::new(pager);
11960
11961 engine
11962 .execute("CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
11963 .expect("create table");
11964 engine
11965 .execute("INSERT INTO tab0 VALUES (1, 2, 3)")
11966 .expect("insert row");
11967
11968 let batches = engine
11969 .sql("SELECT * FROM tab0 WHERE NOT ( NULL ) IN ( - col2 * + col2 )")
11970 .expect("run IN list null comparison");
11971
11972 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
11973 assert_eq!(total_rows, 0, "expected IN list filter to remove all rows");
11974 }
11975
11976 #[test]
11977 fn empty_in_list_filters_all_rows() {
11978 let pager = Arc::new(MemPager::default());
11979 let engine = SqlEngine::new(pager);
11980
11981 engine
11982 .execute("CREATE TABLE test_table(col INTEGER)")
11983 .expect("create table");
11984 engine
11985 .execute("INSERT INTO test_table VALUES (1), (2), (3)")
11986 .expect("insert rows");
11987
11988 let batches = engine
11989 .sql("SELECT * FROM test_table WHERE col IN ()")
11990 .expect("run empty IN list");
11991
11992 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
11993 assert_eq!(total_rows, 0, "expected empty IN list to filter all rows");
11994 }
11995
11996 #[test]
11997 fn empty_not_in_list_preserves_all_rows() {
11998 let pager = Arc::new(MemPager::default());
11999 let engine = SqlEngine::new(pager);
12000
12001 engine
12002 .execute("CREATE TABLE test_table(col INTEGER)")
12003 .expect("create table");
12004 engine
12005 .execute("INSERT INTO test_table VALUES (1), (2), (3)")
12006 .expect("insert rows");
12007
12008 let batches = engine
12009 .sql("SELECT * FROM test_table WHERE col NOT IN () ORDER BY col")
12010 .expect("run empty NOT IN list");
12011
12012 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12013 assert_eq!(
12014 total_rows, 3,
12015 "expected empty NOT IN list to preserve all rows"
12016 );
12017
12018 let mut values: Vec<i64> = Vec::new();
12019 for batch in &batches {
12020 let column = batch
12021 .column(0)
12022 .as_any()
12023 .downcast_ref::<Int64Array>()
12024 .expect("int column");
12025 for idx in 0..column.len() {
12026 if !column.is_null(idx) {
12027 values.push(column.value(idx));
12028 }
12029 }
12030 }
12031
12032 assert_eq!(values, vec![1, 2, 3]);
12033 }
12034
12035 #[test]
12036 fn empty_in_list_with_constant_expression() {
12037 let pager = Arc::new(MemPager::default());
12038 let engine = SqlEngine::new(pager);
12039
12040 let batches = engine
12041 .sql("SELECT 1 IN ()")
12042 .expect("run constant empty IN list");
12043
12044 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12045 assert_eq!(total_rows, 1, "expected one result row");
12046
12047 let value = batches[0]
12048 .column(0)
12049 .as_any()
12050 .downcast_ref::<Int64Array>()
12051 .expect("int column")
12052 .value(0);
12053
12054 assert_eq!(value, 0, "expected 1 IN () to evaluate to 0 (false)");
12055 }
12056
12057 #[test]
12058 fn empty_not_in_list_with_constant_expression() {
12059 let pager = Arc::new(MemPager::default());
12060 let engine = SqlEngine::new(pager);
12061
12062 let batches = engine
12063 .sql("SELECT 1 NOT IN ()")
12064 .expect("run constant empty NOT IN list");
12065
12066 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12067 assert_eq!(total_rows, 1, "expected one result row");
12068
12069 let value = batches[0]
12070 .column(0)
12071 .as_any()
12072 .downcast_ref::<Int64Array>()
12073 .expect("int column")
12074 .value(0);
12075
12076 assert_eq!(value, 1, "expected 1 NOT IN () to evaluate to 1 (true)");
12077 }
12078
12079 #[test]
12080 fn not_in_with_cast_preserves_rows_for_self_comparison() {
12081 let pager = Arc::new(MemPager::default());
12082 let engine = SqlEngine::new(pager);
12083
12084 engine
12085 .execute("CREATE TABLE tab2(col1 INTEGER, col2 INTEGER)")
12086 .expect("create tab2");
12087 engine
12088 .execute("INSERT INTO tab2 VALUES (51, 51), (67, 67), (77, 77)")
12089 .expect("seed tab2");
12090
12091 let batches = engine
12092 .sql(
12093 "SELECT col1 FROM tab2 WHERE NOT col2 NOT IN ( + CAST ( + + col2 AS REAL ) ) ORDER BY col1",
12094 )
12095 .expect("run NOT IN self comparison query");
12096
12097 let mut values: Vec<i64> = Vec::new();
12098 for batch in &batches {
12099 let column = batch
12100 .column(0)
12101 .as_any()
12102 .downcast_ref::<Int64Array>()
12103 .expect("int column");
12104 for idx in 0..column.len() {
12105 if !column.is_null(idx) {
12106 values.push(column.value(idx));
12107 }
12108 }
12109 }
12110
12111 assert_eq!(values, vec![51, 67, 77]);
12112 }
12113
12114 #[test]
12115 fn cross_join_not_null_comparison_filters_all_rows() {
12116 let pager = Arc::new(MemPager::default());
12117 let engine = SqlEngine::new(pager);
12118
12119 engine
12120 .execute("CREATE TABLE left_side(col INTEGER)")
12121 .expect("create left table");
12122 engine
12123 .execute("CREATE TABLE right_side(col INTEGER)")
12124 .expect("create right table");
12125 engine
12126 .execute("INSERT INTO left_side VALUES (1)")
12127 .expect("insert left row");
12128 engine
12129 .execute("INSERT INTO right_side VALUES (2)")
12130 .expect("insert right row");
12131
12132 let batches = engine
12133 .sql("SELECT * FROM left_side CROSS JOIN right_side WHERE NOT ( NULL ) >= NULL")
12134 .expect("run cross join null comparison");
12135
12136 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12137 assert_eq!(
12138 total_rows, 0,
12139 "expected cross join filter to remove all rows"
12140 );
12141 }
12142
12143 #[test]
12144 fn extract_tables_inserts_cross_join_metadata_for_comma_lists() {
12145 use sqlparser::ast::{SetExpr, Statement};
12146 use sqlparser::dialect::GenericDialect;
12147 use sqlparser::parser::Parser;
12148
12149 let dialect = GenericDialect {};
12150 let sql = "SELECT * FROM tab0, tab1 AS cor0 CROSS JOIN tab2";
12151 let statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
12152 let Statement::Query(query) = &statements[0] else {
12153 panic!("expected SELECT query");
12154 };
12155 let select = match query.body.as_ref() {
12156 SetExpr::Select(select) => select.as_ref(),
12157 other => panic!("unexpected query body: {other:?}"),
12158 };
12159
12160 let (tables, join_metadata, join_filters) =
12161 extract_tables(&select.from).expect("extract tables");
12162
12163 assert_eq!(tables.len(), 3, "expected three table refs");
12164 assert_eq!(join_metadata.len(), 2, "expected two join edges");
12165 assert_eq!(join_filters.len(), 2, "join filters mirror metadata len");
12166
12167 assert_eq!(join_metadata[0].left_table_index, 0, "implicit comma join");
12168 assert_eq!(join_metadata[1].left_table_index, 1, "explicit cross join");
12169 }
12170
12171 #[test]
12172 fn implicit_cross_join_populates_literal_true_on_condition() {
12173 use sqlparser::ast::Statement;
12174 use sqlparser::dialect::SQLiteDialect;
12175 use sqlparser::parser::Parser;
12176
12177 let pager = Arc::new(MemPager::default());
12178 let engine = SqlEngine::new(pager);
12179
12180 engine
12181 .execute(
12182 "CREATE TABLE tab0(col0 INTEGER);
12183 CREATE TABLE tab1(col0 INTEGER);",
12184 )
12185 .expect("create tables");
12186
12187 let dialect = SQLiteDialect {};
12188 let mut statements =
12189 Parser::parse_sql(&dialect, "SELECT * FROM tab0, tab1").expect("parse sql");
12190 let Statement::Query(query) = statements.pop().expect("statement") else {
12191 unreachable!();
12192 };
12193 let plan = engine.build_select_plan(*query).expect("build select plan");
12194
12195 assert_eq!(plan.tables.len(), 2, "expected two tables");
12196 assert_eq!(plan.joins.len(), 1, "expected implicit join edge");
12197
12198 let join_meta = &plan.joins[0];
12199 match join_meta.on_condition.as_ref() {
12200 Some(llkv_expr::expr::Expr::Literal(value)) => {
12201 assert!(*value, "implicit join should be ON TRUE");
12202 }
12203 other => panic!("unexpected join predicate: {other:?}"),
12204 }
12205 }
12206
12207 #[test]
12208 fn not_between_null_bounds_matches_sqlite_behavior() {
12209 let pager = Arc::new(MemPager::default());
12210 let engine = SqlEngine::new(pager);
12211
12212 engine
12213 .execute("CREATE TABLE tab2(col1 INTEGER, col2 INTEGER)")
12214 .expect("create tab2");
12215 engine
12216 .execute("INSERT INTO tab2 VALUES (1, 2), (-5, 7), (NULL, 11)")
12217 .expect("seed rows");
12218
12219 let batches = engine
12220 .sql(
12221 "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )",
12222 )
12223 .expect("run NOT BETWEEN query with NULL bounds");
12224
12225 let mut values: Vec<i64> = Vec::new();
12226 for batch in &batches {
12227 let column = batch.column(0);
12228 match column.data_type() {
12229 arrow::datatypes::DataType::Int64 => {
12230 let array = column
12231 .as_any()
12232 .downcast_ref::<Int64Array>()
12233 .expect("int64 column");
12234 for idx in 0..array.len() {
12235 if !array.is_null(idx) {
12236 values.push(array.value(idx));
12237 }
12238 }
12239 }
12240 arrow::datatypes::DataType::Int32 => {
12241 let array = column
12242 .as_any()
12243 .downcast_ref::<Int32Array>()
12244 .expect("int32 column");
12245 for idx in 0..array.len() {
12246 if !array.is_null(idx) {
12247 values.push(array.value(idx) as i64);
12248 }
12249 }
12250 }
12251 other => panic!("unexpected data type: {other:?}"),
12252 }
12253 }
12254
12255 values.sort_unstable();
12256 assert_eq!(values, vec![-7, -2]);
12257 }
12258
12259 #[test]
12260 fn not_between_leading_not_matches_sqlite_behavior() {
12261 let pager = Arc::new(MemPager::default());
12262 let engine = SqlEngine::new(pager);
12263
12264 engine
12265 .execute("CREATE TABLE tab0(col1 INTEGER)")
12266 .expect("create tab0");
12267 engine
12268 .execute("INSERT INTO tab0 VALUES (10), (20), (30)")
12269 .expect("seed tab0");
12270
12271 use sqlparser::ast::Statement;
12272 use sqlparser::dialect::SQLiteDialect;
12273 use sqlparser::parser::Parser;
12274
12275 let dialect = SQLiteDialect {};
12276 let mut statements = Parser::parse_sql(
12277 &dialect,
12278 "SELECT COUNT ( * ) FROM tab0 WHERE NOT ( NOT ( 41 + + 79 ) NOT BETWEEN NULL AND col1 )",
12279 )
12280 .expect("parse NOT BETWEEN query");
12281 let Statement::Query(query_ast) = statements.pop().expect("statement") else {
12282 panic!("expected SELECT query");
12283 };
12284 let plan = engine
12285 .build_select_plan(*query_ast)
12286 .expect("build select plan");
12287 let filter_plan = plan.filter.expect("expected filter predicate");
12288 match &filter_plan.predicate {
12289 llkv_expr::expr::Expr::Or(children) => {
12290 assert_eq!(
12291 children.len(),
12292 2,
12293 "NOT BETWEEN should expand to two comparisons"
12294 );
12295 }
12296 other => panic!("unexpected filter shape: {other:?}"),
12297 }
12298
12299 let batches = engine
12300 .sql(
12301 "SELECT COUNT ( * ) FROM tab0 WHERE NOT ( NOT ( 41 + + 79 ) NOT BETWEEN NULL AND col1 )",
12302 )
12303 .expect("run NOT BETWEEN query with nested NOT tokens");
12304
12305 assert_eq!(batches.len(), 1, "expected single result batch");
12306 let batch = &batches[0];
12307 assert_eq!(batch.num_columns(), 1, "expected single column result");
12308 let array = batch
12309 .column(0)
12310 .as_any()
12311 .downcast_ref::<Int64Array>()
12312 .expect("count column as int64");
12313 assert_eq!(array.len(), 1, "expected scalar aggregate result");
12314 assert_eq!(
12315 array.value(0),
12316 3,
12317 "SQLite and engine should agree on row count"
12318 );
12319 }
12320
12321 #[test]
12322 fn not_chain_precedence_matches_sqlite_behavior() {
12323 let pager = Arc::new(MemPager::default());
12324 let engine = SqlEngine::new(pager);
12325
12326 engine
12327 .execute("CREATE TABLE tab1(col0 INTEGER)")
12328 .expect("create tab1");
12329 engine
12330 .execute("INSERT INTO tab1 VALUES (1), (2)")
12331 .expect("seed tab1");
12332
12333 use sqlparser::ast::Statement;
12334 use sqlparser::dialect::SQLiteDialect;
12335 use sqlparser::parser::Parser;
12336
12337 let dialect = SQLiteDialect {};
12338 let mut statements = Parser::parse_sql(
12339 &dialect,
12340 "SELECT DISTINCT 85 AS value FROM tab1 WHERE NOT + 84 < - + 69 GROUP BY col0, col0",
12341 )
12342 .expect("parse sql");
12343 let statement = statements.pop().expect("expected single statement");
12344 let Statement::Query(query_ast) = statement else {
12345 panic!("expected SELECT query");
12346 };
12347 let plan = engine
12348 .build_select_plan(*query_ast)
12349 .expect("build select plan");
12350 let filter_expr = plan.filter.expect("expected filter predicate").predicate;
12351 if let llkv_expr::expr::Expr::Not(inner) = &filter_expr {
12352 if !matches!(inner.as_ref(), llkv_expr::expr::Expr::Compare { .. }) {
12353 panic!("expected NOT to wrap comparison, got: {inner:?}");
12354 }
12355 } else {
12356 panic!("expected filter to be NOT-wrapped comparison: {filter_expr:?}");
12357 }
12358
12359 let batches = engine
12360 .sql(
12361 "SELECT DISTINCT 85 AS value FROM tab1 WHERE NOT + 84 < - + 69 GROUP BY col0, col0",
12362 )
12363 .expect("run NOT precedence query");
12364
12365 let mut values: Vec<i64> = Vec::new();
12366 for batch in &batches {
12367 let column = batch.column(0);
12368 match column.data_type() {
12369 arrow::datatypes::DataType::Int64 => {
12370 let array = column
12371 .as_any()
12372 .downcast_ref::<Int64Array>()
12373 .expect("int64 column");
12374 for idx in 0..array.len() {
12375 if !array.is_null(idx) {
12376 values.push(array.value(idx));
12377 }
12378 }
12379 }
12380 arrow::datatypes::DataType::Int32 => {
12381 let array = column
12382 .as_any()
12383 .downcast_ref::<Int32Array>()
12384 .expect("int32 column");
12385 for idx in 0..array.len() {
12386 if !array.is_null(idx) {
12387 values.push(array.value(idx) as i64);
12388 }
12389 }
12390 }
12391 arrow::datatypes::DataType::Float64 => {
12392 let array = column
12393 .as_any()
12394 .downcast_ref::<Float64Array>()
12395 .expect("float64 column");
12396 for idx in 0..array.len() {
12397 if !array.is_null(idx) {
12398 values.push(array.value(idx) as i64);
12399 }
12400 }
12401 }
12402 other => panic!("unexpected data type: {other:?}"),
12403 }
12404 }
12405
12406 values.sort_unstable();
12407 assert_eq!(values, vec![85]);
12408 }
12409
12410 #[test]
12411 fn not_between_null_bounds_matches_harness_fixture() {
12412 let pager = Arc::new(MemPager::default());
12413 let engine = SqlEngine::new(pager);
12414
12415 engine
12416 .execute("CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
12417 .expect("create tab2");
12418 engine
12419 .execute("INSERT INTO tab2 VALUES (7, 31, 27), (79, 17, 38), (78, 59, 26)")
12420 .expect("seed rows");
12421
12422 let batches = engine
12423 .sql(
12424 "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )",
12425 )
12426 .expect("run harness-matched NOT BETWEEN query");
12427
12428 let mut values: Vec<i64> = Vec::new();
12429 for batch in &batches {
12430 let column = batch
12431 .column(0)
12432 .as_any()
12433 .downcast_ref::<Int64Array>()
12434 .expect("integer column");
12435 for idx in 0..column.len() {
12436 if !column.is_null(idx) {
12437 values.push(column.value(idx));
12438 }
12439 }
12440 }
12441
12442 values.sort_unstable();
12443 assert_eq!(values, vec![-38, -27, -26]);
12444 }
12445
12446 #[test]
12447 fn not_between_null_bounds_parser_negated_flag() {
12448 use sqlparser::ast::{Expr as SqlExprAst, Statement};
12449 use sqlparser::dialect::SQLiteDialect;
12450 use sqlparser::parser::Parser;
12451
12452 let dialect = SQLiteDialect {};
12453 let sql = "SELECT DISTINCT - col2 AS col1 FROM tab2 WHERE NOT ( col1 ) BETWEEN ( NULL ) AND ( + col1 - col2 )";
12454
12455 let mut statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
12456 let statement = statements.pop().expect("expected single statement");
12457 let Statement::Query(query) = statement else {
12458 panic!("expected SELECT query");
12459 };
12460 let select = query.body.as_select().expect("expected SELECT body");
12461 let where_expr = select.selection.as_ref().expect("expected WHERE clause");
12462
12463 match where_expr {
12464 SqlExprAst::UnaryOp {
12465 op: sqlparser::ast::UnaryOperator::Not,
12466 expr,
12467 } => match expr.as_ref() {
12468 SqlExprAst::Between { negated, .. } => {
12469 assert!(
12470 !negated,
12471 "expected BETWEEN parser to treat leading NOT as part of expression"
12472 );
12473 }
12474 other => panic!("unexpected inner expression: {other:?}"),
12475 },
12476 other => panic!("unexpected where expression: {other:?}"),
12477 }
12478 }
12479
12480 #[test]
12481 fn double_negated_between_null_bounds_filters_all_rows() {
12482 let pager = Arc::new(MemPager::default());
12483 let engine = SqlEngine::new(pager);
12484
12485 engine
12486 .execute("CREATE TABLE tab2(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
12487 .expect("create tab2");
12488 engine
12489 .execute("INSERT INTO tab2 VALUES (1, 2, 3), (-2, -13, 19), (NULL, 5, 7)")
12490 .expect("seed rows");
12491
12492 let batches = engine
12493 .sql(
12494 "SELECT - col1 * + col2 FROM tab2 WHERE NOT ( col1 ) NOT BETWEEN ( NULL ) AND ( col0 )",
12495 )
12496 .expect("run double NOT BETWEEN query with NULL bounds");
12497
12498 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12499 assert_eq!(
12500 total_rows, 0,
12501 "expected double NOT BETWEEN to filter all rows"
12502 );
12503 }
12504
12505 #[test]
12506 fn not_with_empty_in_list_complements_all_rows() {
12507 let pager = Arc::new(MemPager::default());
12508 let engine = SqlEngine::new(pager);
12509
12510 engine
12511 .execute("CREATE TABLE tab(pk INTEGER PRIMARY KEY, col0 INTEGER)")
12512 .expect("create tab");
12513 engine
12514 .execute("INSERT INTO tab VALUES (0, 1), (1, 2), (2, 3)")
12515 .expect("seed rows");
12516
12517 let batches = engine
12518 .sql(
12519 "SELECT COUNT(*) AS cnt FROM tab WHERE NOT (col0 = 999 AND col0 IN (SELECT col0 FROM tab WHERE 0 = 1))",
12520 )
12521 .expect("run NOT query with empty IN list");
12522
12523 assert_eq!(batches.len(), 1, "expected single aggregate batch");
12524 let counts = batches[0]
12525 .column(0)
12526 .as_any()
12527 .downcast_ref::<Int64Array>()
12528 .expect("count column as int64");
12529 assert_eq!(counts.value(0), 3, "NOT should match every row");
12530
12531 let batches = engine
12532 .sql(
12533 "SELECT pk FROM tab WHERE NOT (col0 = 999 AND col0 IN (SELECT col0 FROM tab WHERE 0 = 1)) ORDER BY pk",
12534 )
12535 .expect("fetch rows from NOT query");
12536 let expected: Vec<i64> = vec![0, 1, 2];
12537 let mut actual = Vec::new();
12538 for batch in &batches {
12539 let column = batch
12540 .column(0)
12541 .as_any()
12542 .downcast_ref::<Int64Array>()
12543 .expect("pk column as int64");
12544 for idx in 0..column.len() {
12545 if !column.is_null(idx) {
12546 actual.push(column.value(idx));
12547 }
12548 }
12549 }
12550 assert_eq!(actual, expected, "NOT should preserve all primary keys");
12551 }
12552
12553 #[test]
12554 fn not_scalar_less_than_null_filters_all_rows() {
12555 let pager = Arc::new(MemPager::default());
12556 let engine = SqlEngine::new(pager);
12557
12558 engine
12559 .execute("CREATE TABLE tab(col0 INTEGER, col2 INTEGER)")
12560 .expect("create tab");
12561 engine
12562 .execute("INSERT INTO tab VALUES (1, 2), (5, 10), (-3, 7)")
12563 .expect("seed rows");
12564
12565 let batches = engine
12566 .sql("SELECT col0 FROM tab WHERE NOT ( - col0 / - col2 + - col0 ) < NULL")
12567 .expect("run NOT < NULL query");
12568
12569 let total_rows: usize = batches.iter().map(|batch| batch.num_rows()).sum();
12570 assert_eq!(total_rows, 0, "expected NOT < NULL to filter all rows");
12571 }
12572
12573 #[test]
12574 fn left_join_not_is_not_null_on_literal_flips_to_is_null() {
12575 use sqlparser::ast::Statement;
12576 use sqlparser::dialect::SQLiteDialect;
12577 use sqlparser::parser::Parser;
12578
12579 let pager = Arc::new(MemPager::default());
12580 let engine = SqlEngine::new(pager);
12581
12582 engine
12583 .execute(
12584 "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER);\
12585 CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
12586 )
12587 .expect("create tables");
12588
12589 let sql = "SELECT DISTINCT * FROM tab1 AS cor0 LEFT JOIN tab1 AS cor1 ON NOT 86 IS NOT NULL, tab0 AS cor2";
12590 let dialect = SQLiteDialect {};
12591 let mut statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
12592 let statement = statements.pop().expect("expected statement");
12593 let Statement::Query(query) = statement else {
12594 panic!("expected SELECT query");
12595 };
12596
12597 let plan = engine.build_select_plan(*query).expect("build select plan");
12598
12599 let left_join = plan
12600 .joins
12601 .iter()
12602 .find(|join| join.join_type == llkv_plan::JoinPlan::Left)
12603 .expect("expected explicit LEFT JOIN entry");
12604 let on_condition = left_join
12605 .on_condition
12606 .as_ref()
12607 .expect("left join should preserve ON predicate");
12608
12609 match on_condition {
12610 llkv_expr::expr::Expr::IsNull { expr, negated } => {
12611 assert!(!negated, "expected NOT to flip into IS NULL");
12612 assert!(matches!(
12613 expr,
12614 llkv_expr::expr::ScalarExpr::Literal(llkv_expr::literal::Literal::Int128(86))
12615 ));
12616 }
12617 other => panic!("unexpected ON predicate: {other:?}"),
12618 }
12619 }
12620
12621 #[test]
12622 fn left_join_constant_false_preserves_left_rows_with_null_right() {
12623 let pager = Arc::new(MemPager::default());
12624 let engine = SqlEngine::new(pager);
12625
12626 engine
12627 .execute(
12628 "CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER);\
12629 CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER);",
12630 )
12631 .expect("create tables");
12632
12633 engine
12634 .execute(
12635 "INSERT INTO tab0 VALUES (1, 2, 3), (4, 5, 6);\
12636 INSERT INTO tab1 VALUES (10, 11, 12), (13, 14, 15);",
12637 )
12638 .expect("seed rows");
12639
12640 let batches = engine
12641 .sql(
12642 "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",
12643 )
12644 .expect("execute join query");
12645
12646 let mut total_rows = 0;
12647 for batch in &batches {
12648 total_rows += batch.num_rows();
12649
12650 for row_idx in 0..batch.num_rows() {
12652 for col_idx in 3..6 {
12653 assert!(
12654 batch.column(col_idx).is_null(row_idx),
12655 "expected right table column {} to be NULL in row {}",
12656 col_idx,
12657 row_idx
12658 );
12659 }
12660 }
12661 }
12662
12663 assert_eq!(total_rows, 4, "expected Cartesian product with tab0 only");
12665 }
12666
12667 #[test]
12668 fn cross_join_duplicate_table_name_resolves_columns() {
12669 let pager = Arc::new(MemPager::default());
12670 let engine = SqlEngine::new(pager);
12671
12672 use sqlparser::ast::{SetExpr, Statement};
12673 use sqlparser::dialect::SQLiteDialect;
12674 use sqlparser::parser::Parser;
12675
12676 engine
12677 .execute("CREATE TABLE tab1(col0 INTEGER, col1 INTEGER, col2 INTEGER)")
12678 .expect("create tab1");
12679 engine
12680 .execute("INSERT INTO tab1 VALUES (7, 8, 9)")
12681 .expect("insert tab1 row");
12682
12683 let dialect = SQLiteDialect {};
12684 let ast = Parser::parse_sql(
12685 &dialect,
12686 "SELECT tab1.col2 FROM tab1 AS cor0 CROSS JOIN tab1",
12687 )
12688 .expect("parse cross join query");
12689 let Statement::Query(query) = &ast[0] else {
12690 panic!("expected SELECT query");
12691 };
12692 let select = match query.body.as_ref() {
12693 SetExpr::Select(select) => select.as_ref(),
12694 other => panic!("unexpected query body: {other:?}"),
12695 };
12696 assert_eq!(select.from.len(), 1);
12697 assert!(!select.from[0].joins.is_empty());
12698
12699 let batches = engine
12700 .sql("SELECT tab1.col2 FROM tab1 AS cor0 CROSS JOIN tab1")
12701 .expect("run cross join with alias and base table");
12702
12703 let mut values = Vec::new();
12704 for batch in &batches {
12705 let column = batch
12706 .column(0)
12707 .as_any()
12708 .downcast_ref::<Int64Array>()
12709 .expect("int64 column");
12710 for idx in 0..column.len() {
12711 if !column.is_null(idx) {
12712 values.push(column.value(idx));
12713 }
12714 }
12715 }
12716 assert_eq!(values, vec![9]);
12717
12718 engine
12719 .execute("CREATE TABLE strings(a TEXT)")
12720 .expect("create table");
12721
12722 engine
12723 .execute("INSERT INTO strings VALUES ('3'), ('4'), (NULL)")
12724 .expect("insert seed rows");
12725
12726 let result = engine
12727 .execute("UPDATE strings SET a = 13 WHERE a = '3'")
12728 .expect("update rows");
12729 assert!(matches!(
12730 result[0],
12731 RuntimeStatementResult::Update {
12732 rows_updated: 1,
12733 ..
12734 }
12735 ));
12736
12737 let mut result = engine
12738 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
12739 .expect("select rows");
12740 let select_result = result.remove(0);
12741 let batches = match select_result {
12742 RuntimeStatementResult::Select { execution, .. } => {
12743 execution.collect().expect("collect batches")
12744 }
12745 _ => panic!("expected select result"),
12746 };
12747
12748 let mut values: Vec<Option<String>> = Vec::new();
12749 for batch in &batches {
12750 let column = batch
12751 .column(0)
12752 .as_any()
12753 .downcast_ref::<StringArray>()
12754 .expect("string column");
12755 for idx in 0..column.len() {
12756 if column.is_null(idx) {
12757 values.push(None);
12758 } else {
12759 values.push(Some(column.value(idx).to_string()));
12760 }
12761 }
12762 }
12763
12764 values.sort_by(|a, b| match (a, b) {
12765 (None, None) => std::cmp::Ordering::Equal,
12766 (None, Some(_)) => std::cmp::Ordering::Less,
12767 (Some(_), None) => std::cmp::Ordering::Greater,
12768 (Some(av), Some(bv)) => {
12769 let a_val = av.parse::<i64>().unwrap_or_default();
12770 let b_val = bv.parse::<i64>().unwrap_or_default();
12771 a_val.cmp(&b_val)
12772 }
12773 });
12774
12775 assert_eq!(
12776 values,
12777 vec![None, Some("4".to_string()), Some("13".to_string())]
12778 );
12779 }
12780
12781 #[test]
12782 fn order_by_honors_configured_default_null_order() {
12783 let pager = Arc::new(MemPager::default());
12784 let engine = SqlEngine::new(pager);
12785
12786 engine
12787 .execute("CREATE TABLE strings(a VARCHAR)")
12788 .expect("create table");
12789 engine
12790 .execute("INSERT INTO strings VALUES ('3'), ('4'), (NULL)")
12791 .expect("insert values");
12792 engine
12793 .execute("UPDATE strings SET a = 13 WHERE a = '3'")
12794 .expect("update value");
12795
12796 let mut result = engine
12797 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
12798 .expect("select rows");
12799 let select_result = result.remove(0);
12800 let batches = match select_result {
12801 RuntimeStatementResult::Select { execution, .. } => {
12802 execution.collect().expect("collect batches")
12803 }
12804 _ => panic!("expected select result"),
12805 };
12806
12807 let values = extract_string_options(&batches);
12808 assert_eq!(
12809 values,
12810 vec![Some("4".to_string()), Some("13".to_string()), None]
12811 );
12812
12813 assert!(!engine.default_nulls_first_for_tests());
12814
12815 engine
12816 .execute("SET default_null_order='nulls_first'")
12817 .expect("set default null order");
12818
12819 assert!(engine.default_nulls_first_for_tests());
12820
12821 let mut result = engine
12822 .execute("SELECT * FROM strings ORDER BY cast(a AS INTEGER)")
12823 .expect("select rows");
12824 let select_result = result.remove(0);
12825 let batches = match select_result {
12826 RuntimeStatementResult::Select { execution, .. } => {
12827 execution.collect().expect("collect batches")
12828 }
12829 _ => panic!("expected select result"),
12830 };
12831
12832 let values = extract_string_options(&batches);
12833 assert_eq!(
12834 values,
12835 vec![None, Some("4".to_string()), Some("13".to_string())]
12836 );
12837 }
12838
12839 #[test]
12840 fn arrow_type_from_row_returns_struct_fields() {
12841 let dialect = GenericDialect {};
12842 let statements = parse_sql_with_recursion_limit(
12843 &dialect,
12844 "CREATE TABLE row_types(payload ROW(a INTEGER, b VARCHAR));",
12845 )
12846 .expect("parse ROW type definition");
12847
12848 let data_type = match &statements[0] {
12849 Statement::CreateTable(stmt) => stmt.columns[0].data_type.clone(),
12850 other => panic!("unexpected statement: {other:?}"),
12851 };
12852
12853 let arrow_type = arrow_type_from_sql(&data_type).expect("convert ROW type");
12854 match arrow_type {
12855 arrow::datatypes::DataType::Struct(fields) => {
12856 assert_eq!(fields.len(), 2, "unexpected field count");
12857 assert_eq!(fields[0].name(), "a");
12858 assert_eq!(fields[1].name(), "b");
12859 assert_eq!(fields[0].data_type(), &arrow::datatypes::DataType::Int64);
12860 assert_eq!(fields[1].data_type(), &arrow::datatypes::DataType::Utf8);
12861 }
12862 other => panic!("expected struct type, got {other:?}"),
12863 }
12864 }
12865}