1use super::cte::CommonTableExpression;
4use super::types::{IndexRange, JoinType, SortOrder};
5use crate::expr::Expr;
6use featherdb_catalog::{Index, Table};
7use featherdb_core::Permissions;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
12pub enum LogicalPlan {
13 Scan {
15 table: Arc<Table>,
16 alias: Option<String>,
17 projection: Option<Vec<usize>>,
18 filter: Option<Expr>,
19 },
20
21 IndexScan {
24 table: Arc<Table>,
26 alias: Option<String>,
28 index: Index,
30 index_column: usize,
32 range: IndexRange,
34 residual_filter: Option<Expr>,
37 projection: Option<Vec<usize>>,
39 },
40
41 PkSeek {
44 table: Arc<Table>,
45 alias: Option<String>,
46 key_values: Vec<Expr>,
48 residual_filter: Option<Expr>,
50 projection: Option<Vec<usize>>,
52 },
53
54 PkRangeScan {
57 table: Arc<Table>,
58 alias: Option<String>,
59 range: IndexRange,
61 residual_filter: Option<Expr>,
63 projection: Option<Vec<usize>>,
65 },
66
67 Filter {
69 input: Box<LogicalPlan>,
70 predicate: Expr,
71 },
72
73 Project {
75 input: Box<LogicalPlan>,
76 exprs: Vec<(Expr, String)>,
77 },
78
79 Join {
81 left: Box<LogicalPlan>,
82 right: Box<LogicalPlan>,
83 join_type: JoinType,
84 condition: Option<Expr>,
85 },
86
87 Aggregate {
89 input: Box<LogicalPlan>,
90 group_by: Vec<Expr>,
91 aggregates: Vec<(Expr, String)>,
92 },
93
94 Sort {
96 input: Box<LogicalPlan>,
97 order_by: Vec<(Expr, SortOrder)>,
98 },
99
100 Limit {
102 input: Box<LogicalPlan>,
103 limit: Option<usize>,
104 offset: usize,
105 },
106
107 Distinct { input: Box<LogicalPlan> },
109
110 Insert {
112 table: Arc<Table>,
113 columns: Vec<String>,
114 values: Vec<Vec<Expr>>,
115 },
116
117 Update {
119 table: Arc<Table>,
120 assignments: Vec<(String, Expr)>,
121 filter: Option<Expr>,
122 },
123
124 Delete {
126 table: Arc<Table>,
127 filter: Option<Expr>,
128 },
129
130 CreateTable { table: Table },
132
133 DropTable { name: String, if_exists: bool },
135
136 EmptyRelation,
138
139 Explain {
141 input: Box<LogicalPlan>,
143 verbose: bool,
145 analyze: bool,
147 },
148
149 Window {
151 input: Box<LogicalPlan>,
153 window_exprs: Vec<(Expr, String)>,
155 },
156
157 AlterTable {
159 table_name: String,
161 operations: Vec<featherdb_catalog::AlterTableOp>,
163 },
164
165 WithCte {
168 ctes: Vec<CommonTableExpression>,
170 query: Box<LogicalPlan>,
172 },
173
174 CteScan {
176 cte_name: String,
178 alias: Option<String>,
180 columns: Vec<String>,
182 },
183
184 SubqueryScan {
186 subquery: Box<LogicalPlan>,
188 subquery_id: usize,
190 alias: Option<String>,
192 },
193
194 SemiJoin {
197 left: Box<LogicalPlan>,
198 right: Box<LogicalPlan>,
199 condition: Option<Expr>,
200 positive: bool,
202 },
203
204 AntiJoin {
207 left: Box<LogicalPlan>,
208 right: Box<LogicalPlan>,
209 condition: Option<Expr>,
210 },
211
212 Grant {
214 privileges: Permissions,
216 table: Option<String>,
218 grantee: String,
220 },
221
222 Revoke {
224 privileges: Permissions,
226 table: Option<String>,
228 grantee: String,
230 },
231
232 ShowGrants {
234 target: ShowGrantsTarget,
236 },
237
238 Union {
240 left: Box<LogicalPlan>,
241 right: Box<LogicalPlan>,
242 all: bool,
244 },
245
246 Intersect {
248 left: Box<LogicalPlan>,
249 right: Box<LogicalPlan>,
250 all: bool,
252 },
253
254 Except {
256 left: Box<LogicalPlan>,
257 right: Box<LogicalPlan>,
258 all: bool,
260 },
261
262 CreateView {
264 name: String,
265 query: Box<LogicalPlan>,
266 query_sql: String,
268 columns: Vec<String>,
270 },
271
272 DropView { name: String, if_exists: bool },
274
275 CreateIndex {
277 index_name: String,
278 table_name: String,
279 columns: Vec<String>,
280 unique: bool,
281 },
282}
283
284#[derive(Debug, Clone)]
286pub enum ShowGrantsTarget {
287 Table(String),
289 ApiKey(String),
291}
292
293impl LogicalPlan {
294 pub fn output_columns(&self) -> Vec<String> {
296 match self {
297 LogicalPlan::Scan { table, alias, .. } => {
298 let prefix = alias.as_ref().unwrap_or(&table.name);
299 table
300 .columns
301 .iter()
302 .map(|c| format!("{}.{}", prefix, c.name))
303 .collect()
304 }
305 LogicalPlan::IndexScan { table, alias, .. } => {
306 let prefix = alias.as_ref().unwrap_or(&table.name);
307 table
308 .columns
309 .iter()
310 .map(|c| format!("{}.{}", prefix, c.name))
311 .collect()
312 }
313 LogicalPlan::PkSeek { table, alias, .. } => {
314 let prefix = alias.as_ref().unwrap_or(&table.name);
315 table
316 .columns
317 .iter()
318 .map(|c| format!("{}.{}", prefix, c.name))
319 .collect()
320 }
321 LogicalPlan::PkRangeScan { table, alias, .. } => {
322 let prefix = alias.as_ref().unwrap_or(&table.name);
323 table
324 .columns
325 .iter()
326 .map(|c| format!("{}.{}", prefix, c.name))
327 .collect()
328 }
329 LogicalPlan::Project { exprs, .. } => {
330 exprs.iter().map(|(_, name)| name.clone()).collect()
331 }
332 LogicalPlan::Filter { input, .. } => input.output_columns(),
333 LogicalPlan::Join { left, right, .. } => {
334 let mut cols = left.output_columns();
335 cols.extend(right.output_columns());
336 cols
337 }
338 LogicalPlan::Aggregate {
339 group_by,
340 aggregates,
341 ..
342 } => {
343 let mut cols: Vec<String> = group_by
344 .iter()
345 .enumerate()
346 .map(|(i, _)| format!("group_{}", i))
347 .collect();
348 cols.extend(aggregates.iter().map(|(_, name)| name.clone()));
349 cols
350 }
351 LogicalPlan::Sort { input, .. } => input.output_columns(),
352 LogicalPlan::Limit { input, .. } => input.output_columns(),
353 LogicalPlan::Distinct { input } => input.output_columns(),
354 LogicalPlan::Window {
355 input,
356 window_exprs,
357 } => {
358 let mut cols = input.output_columns();
360 cols.extend(window_exprs.iter().map(|(_, name)| name.clone()));
361 cols
362 }
363 LogicalPlan::WithCte { query, .. } => query.output_columns(),
364 LogicalPlan::CteScan {
365 cte_name,
366 alias,
367 columns,
368 } => {
369 let prefix = alias.as_ref().unwrap_or(cte_name);
370 columns
371 .iter()
372 .map(|c| {
373 let bare = c.split('.').next_back().unwrap_or(c);
375 format!("{}.{}", prefix, bare)
376 })
377 .collect()
378 }
379 LogicalPlan::SubqueryScan {
380 subquery, alias, ..
381 } => {
382 let cols = subquery.output_columns();
383 if let Some(a) = alias {
384 cols.iter()
385 .map(|c| {
386 if let Some(name) = c.split('.').next_back() {
387 format!("{}.{}", a, name)
388 } else {
389 format!("{}.{}", a, c)
390 }
391 })
392 .collect()
393 } else {
394 cols
395 }
396 }
397 LogicalPlan::SemiJoin { left, .. } => left.output_columns(),
398 LogicalPlan::AntiJoin { left, .. } => left.output_columns(),
399 LogicalPlan::Union { left, .. } => left.output_columns(),
400 LogicalPlan::Intersect { left, .. } => left.output_columns(),
401 LogicalPlan::Except { left, .. } => left.output_columns(),
402 LogicalPlan::CreateView { columns, .. } if !columns.is_empty() => columns.clone(),
403 _ => vec![],
404 }
405 }
406
407 pub fn collect_column_references(&self) -> Vec<(Option<String>, String)> {
410 let mut refs = Vec::new();
411 self.collect_column_references_recursive(&mut refs);
412 refs
413 }
414
415 fn collect_column_references_recursive(&self, refs: &mut Vec<(Option<String>, String)>) {
416 match self {
417 LogicalPlan::Scan { filter, .. } | LogicalPlan::Delete { filter, .. } => {
418 if let Some(f) = filter {
419 refs.extend(f.collect_column_references());
420 }
421 }
422 LogicalPlan::IndexScan {
423 residual_filter: Some(f),
424 ..
425 } => {
426 refs.extend(f.collect_column_references());
427 }
428 LogicalPlan::IndexScan {
429 residual_filter: None,
430 ..
431 } => {}
432 LogicalPlan::PkSeek {
433 key_values,
434 residual_filter,
435 ..
436 } => {
437 for expr in key_values {
438 refs.extend(expr.collect_column_references());
439 }
440 if let Some(f) = residual_filter {
441 refs.extend(f.collect_column_references());
442 }
443 }
444 LogicalPlan::PkRangeScan {
445 residual_filter: Some(f),
446 ..
447 } => {
448 refs.extend(f.collect_column_references());
449 }
450 LogicalPlan::PkRangeScan {
451 residual_filter: None,
452 ..
453 } => {}
454 LogicalPlan::Filter { input, predicate } => {
455 refs.extend(predicate.collect_column_references());
456 input.collect_column_references_recursive(refs);
457 }
458 LogicalPlan::Project { input, exprs } => {
459 for (expr, _) in exprs {
460 refs.extend(expr.collect_column_references());
461 }
462 input.collect_column_references_recursive(refs);
463 }
464 LogicalPlan::Join {
465 left,
466 right,
467 condition,
468 ..
469 } => {
470 if let Some(c) = condition {
471 refs.extend(c.collect_column_references());
472 }
473 left.collect_column_references_recursive(refs);
474 right.collect_column_references_recursive(refs);
475 }
476 LogicalPlan::Aggregate {
477 input,
478 group_by,
479 aggregates,
480 } => {
481 for expr in group_by {
482 refs.extend(expr.collect_column_references());
483 }
484 for (expr, _) in aggregates {
485 refs.extend(expr.collect_column_references());
486 }
487 input.collect_column_references_recursive(refs);
488 }
489 LogicalPlan::Sort { input, order_by } => {
490 for (expr, _) in order_by {
491 refs.extend(expr.collect_column_references());
492 }
493 input.collect_column_references_recursive(refs);
494 }
495 LogicalPlan::Limit { input, .. }
496 | LogicalPlan::Distinct { input }
497 | LogicalPlan::Explain { input, .. }
498 | LogicalPlan::SubqueryScan {
499 subquery: input, ..
500 } => {
501 input.collect_column_references_recursive(refs);
502 }
503 LogicalPlan::Update {
504 filter,
505 assignments,
506 ..
507 } => {
508 if let Some(f) = filter {
509 refs.extend(f.collect_column_references());
510 }
511 for (_, expr) in assignments {
512 refs.extend(expr.collect_column_references());
513 }
514 }
515 LogicalPlan::Insert { values, .. } => {
516 for row in values {
517 for expr in row {
518 refs.extend(expr.collect_column_references());
519 }
520 }
521 }
522 LogicalPlan::Window {
523 input,
524 window_exprs,
525 } => {
526 for (expr, _) in window_exprs {
527 refs.extend(expr.collect_column_references());
528 }
529 input.collect_column_references_recursive(refs);
530 }
531 LogicalPlan::WithCte { ctes, query } => {
532 for cte in ctes {
533 cte.query.collect_column_references_recursive(refs);
534 }
535 query.collect_column_references_recursive(refs);
536 }
537 LogicalPlan::SemiJoin {
538 left,
539 right,
540 condition,
541 ..
542 }
543 | LogicalPlan::AntiJoin {
544 left,
545 right,
546 condition,
547 } => {
548 if let Some(c) = condition {
549 refs.extend(c.collect_column_references());
550 }
551 left.collect_column_references_recursive(refs);
552 right.collect_column_references_recursive(refs);
553 }
554 LogicalPlan::Union { left, right, .. }
555 | LogicalPlan::Intersect { left, right, .. }
556 | LogicalPlan::Except { left, right, .. } => {
557 left.collect_column_references_recursive(refs);
558 right.collect_column_references_recursive(refs);
559 }
560 _ => {}
561 }
562 }
563}