1use rudb_catalog::{Catalog, Entry, QualifiedName, duplicate_check, same_name};
18use rudb_common::bounds::End;
19use rudb_common::{
20 Bound as ColumnBound, Clustering, Error, Field, LogicalType, Result, Session, Stat, Value,
21 Width,
22};
23use rudb_parse::ast::{self, Ast};
24use rudb_parse::{NONE, deparse, parse_ast};
25use rudb_plan::{Arm, Expr, ExprRef, Node, Plan, SortKey};
26
27use crate::binder::Binder;
28use crate::parameters::Parameters;
29
30#[derive(Debug)]
35pub enum Bound {
36 Query(Plan),
38 CreateTable(CreateTable),
40 CreateView(CreateView),
42 DropTable(DropTable),
44 Schema(SchemaChange),
46 Sequence(SequenceChange),
48 Type(TypeChange),
50 Alter(Alter),
52 Index(IndexChange),
54 Insert(Insert),
56 Setting(Setting),
58 Checkpoint(Option<String>),
60 Attach(Attach),
62 Detach { name: String, if_exists: bool },
64 Transaction(ast::Transaction),
66 Explain { plan: Plan, analyze: bool, statistics: bool, codegen: bool },
82 CopyTo(CopyTo),
84}
85
86#[derive(Debug)]
92pub struct CopyTo {
93 pub plan: Plan,
95 pub path: String,
97 pub header: bool,
99 pub delimiter: String,
101 pub quote: String,
103 pub escape: String,
105 pub null: String,
107 pub force_quote: Vec<String>,
109 pub force_quote_all: bool,
111 pub json: bool,
113 pub array: bool,
115}
116
117#[derive(Debug)]
129pub struct Setting {
130 pub name: String,
132 pub scope: ast::Scope,
134 pub value: Option<Value>,
136 pub pragma: bool,
138}
139
140#[derive(Debug)]
145pub struct Attach {
146 pub path: String,
148 pub alias: Option<String>,
150 pub or_replace: bool,
152 pub if_not_exists: bool,
154 pub options: Vec<(String, Option<Value>)>,
156}
157
158#[derive(Debug)]
160pub struct CreateTable {
161 pub name: QualifiedName,
163 pub columns: Vec<Field>,
166 pub source: Option<Plan>,
168 pub if_not_exists: bool,
170 pub or_replace: bool,
172 pub keys: Vec<rudb_catalog::Key>,
174 pub defaults: Vec<Option<String>>,
176 pub sequences: Vec<QualifiedName>,
178 pub checks: Vec<String>,
180 pub foreign: Vec<rudb_catalog::ForeignKey>,
182 pub order: Vec<rudb_catalog::Constraint>,
184}
185
186#[derive(Debug)]
193pub struct CreateView {
194 pub name: QualifiedName,
196 pub sql: String,
198 pub statement: String,
203 pub aliases: Vec<String>,
205 pub if_not_exists: bool,
207 pub or_replace: bool,
209 pub columns: Vec<Field>,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct SchemaChange {
224 pub catalog: String,
226 pub name: String,
228 pub drop: bool,
230 pub quiet: bool,
232 pub or_replace: bool,
235 pub cascade: bool,
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct SequenceChange {
242 pub name: Option<QualifiedName>,
244 pub drop: bool,
246 pub if_not_exists: bool,
248 pub or_replace: bool,
250 pub cascade: bool,
252 pub options: rudb_common::sequence::Options,
254 pub owner: Option<QualifiedName>,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct TypeChange {
262 pub name: Option<QualifiedName>,
264 pub ty: Option<LogicalType>,
266 pub uses: Vec<QualifiedName>,
268 pub if_not_exists: bool,
270 pub or_replace: bool,
272 pub cascade: bool,
274}
275
276#[derive(Debug)]
278pub struct Alter {
279 pub name: Option<QualifiedName>,
281 pub alteration: Option<rudb_catalog::Alteration>,
283 pub rewrite: Option<Plan>,
285}
286
287#[derive(Debug)]
289pub struct IndexChange {
290 pub table: Option<QualifiedName>,
292 pub index: Option<rudb_catalog::Index>,
294 pub name: Vec<String>,
296 pub quiet: bool,
298}
299
300#[derive(Debug)]
302pub struct DropTable {
303 pub names: Vec<QualifiedName>,
308 pub kind: Entry,
310}
311
312#[derive(Debug)]
314pub struct Insert {
315 pub name: QualifiedName,
317 pub source: Plan,
320 pub write: Write,
322 pub returning: Option<Box<Plan>>,
325 pub conflict: Option<Conflict>,
327 pub checks: Option<Checks>,
329}
330
331#[derive(Debug)]
337pub struct Checks {
338 pub plan: Box<Plan>,
340 pub messages: Vec<String>,
342}
343
344#[derive(Debug)]
346pub struct Conflict {
347 pub key: Option<usize>,
349 pub action: ConflictAction,
351}
352
353#[derive(Debug)]
355pub enum ConflictAction {
356 Nothing,
358 Replace(Vec<usize>),
360 Update {
364 columns: Vec<usize>,
366 plan: Box<Plan>,
368 },
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum Write {
374 Append,
376 Update,
379 Delete,
382}
383
384fn returning(
386 ast: &Ast,
387 catalog: &Catalog,
388 parameters: &Parameters,
389 session: &Session,
390 query: Option<ast::QueryRef>,
391) -> Result<Option<Box<Plan>>> {
392 let Some(query) = query else { return Ok(None) };
393 let mut binder = Binder::with(catalog, parameters, session);
394 let (root, _) = binder.bind_query(ast, query)?;
395 Ok(Some(Box::new(finish(binder, root)?)))
396}
397
398pub fn bind_statement(ast: &Ast, catalog: &Catalog) -> Result<Bound> {
405 bind_statement_with(ast, catalog, &Parameters::new(), &Session::new())
406}
407
408pub fn bind_statement_with(
418 ast: &Ast,
419 catalog: &Catalog,
420 parameters: &Parameters,
421 session: &Session,
422) -> Result<Bound> {
423 bind_one(ast, catalog, parameters, session, false)
424}
425
426pub fn bind_statement_outlined(
439 ast: &Ast,
440 catalog: &Catalog,
441 parameters: &Parameters,
442 session: &Session,
443) -> Result<Bound> {
444 bind_one(ast, catalog, parameters, session, true)
445}
446
447fn bind_one(
448 ast: &Ast,
449 catalog: &Catalog,
450 parameters: &Parameters,
451 session: &Session,
452 outlined: bool,
453) -> Result<Bound> {
454 let statement = match ast.statements.as_slice() {
455 [statement] => *statement,
456 [] => return Err(Error::binder("no statement to bind")),
457 _ => return Err(Error::not_implemented("a script of more than one statement")),
458 };
459 match statement {
460 ast::Statement::Query(query) => {
461 let mut binder = Binder::with(catalog, parameters, session);
462 binder.outlined = outlined;
463 let (root, _) = binder.bind_query(ast, query)?;
464 Ok(Bound::Query(finish(binder, root)?))
465 }
466 ast::Statement::CreateTable(index) => {
467 create_table(ast, catalog, parameters, session, index)
468 }
469 ast::Statement::CreateView(index) => create_view(ast, catalog, parameters, session, index),
470 ast::Statement::DropTable(index) => drop_table(ast, catalog, index),
471 ast::Statement::Schema(index) => {
472 let written = ast.schema(index);
473 if written.temporary {
474 return Err(Error::binder("Temporary schemas are not supported"));
475 }
476 let parts: Vec<&str> = ast.name(written.name).collect();
477 let (catalog, name) = catalog.schema_name(&parts)?;
478 Ok(Bound::Schema(SchemaChange {
479 catalog,
480 name,
481 drop: written.drop,
482 quiet: written.quiet,
483 or_replace: written.or_replace,
484 cascade: written.cascade,
485 }))
486 }
487 ast::Statement::Sequence(index) => {
488 let written = ast.sequence(index);
489 let parts: Vec<&str> = ast.name(written.name).collect();
490 let alter = !written.owner.is_empty();
491 let mut owner = None;
492 let name = if written.drop || alter {
493 match catalog.resolve_sequence(&parts) {
494 Ok(name) => Some(name),
495 Err(_) if written.quiet => None,
496 Err(error) => return Err(error),
497 }
498 } else if written.temporary {
499 Some(catalog.resolve_for_create_temporary(&parts)?)
500 } else {
501 Some(catalog.resolve_for_create(&parts)?)
502 };
503 if alter && name.is_some() {
504 let parts: Vec<&str> = ast.name(written.owner).collect();
505 owner = Some(catalog.resolve_owner(&parts)?);
506 }
507 Ok(Bound::Sequence(SequenceChange {
508 name,
509 drop: written.drop,
510 if_not_exists: written.quiet,
511 or_replace: written.or_replace,
512 cascade: written.cascade,
513 options: written.options,
514 owner,
515 }))
516 }
517 ast::Statement::Type(index) => {
518 let written = ast.type_def(index);
519 let parts: Vec<&str> = ast.name(written.name).collect();
520 let (name, ty, uses) = if written.drop {
521 let name = catalog.resolve_type(&parts).map(|made| made.name().clone());
522 if name.is_none() && !written.quiet {
523 return Err(Error::catalog(format!(
524 "Type with name {} does not exist!",
525 parts.last().copied().unwrap_or_default()
526 )));
527 }
528 (name, None, Vec::new())
529 } else {
530 let name = if written.temporary {
531 catalog.resolve_for_create_temporary(&parts)?
532 } else {
533 catalog.resolve_for_create(&parts)?
534 };
535 let (ty, uses) = written_type(catalog, ast.string(written.ty))?;
536 (Some(name), Some(ty), uses)
537 };
538 Ok(Bound::Type(TypeChange {
539 name,
540 ty,
541 uses,
542 if_not_exists: written.quiet,
543 or_replace: written.or_replace,
544 cascade: written.cascade,
545 }))
546 }
547 ast::Statement::Alter(index) => alter(ast, catalog, parameters, session, index),
548 ast::Statement::Index(index) => create_index(ast, catalog, parameters, session, index),
549 ast::Statement::Insert(index) => insert(ast, catalog, parameters, session, index),
550 ast::Statement::Update(index) => change(ast, catalog, parameters, session, index, false),
551 ast::Statement::Delete(index) => change(ast, catalog, parameters, session, index, true),
552 ast::Statement::Set(index) | ast::Statement::Reset(index) => {
553 setting(ast, catalog, parameters, session, index)
554 }
555 ast::Statement::Checkpoint(name) => {
556 Ok(Bound::Checkpoint((name != NONE).then(|| ast.string(name).to_string())))
557 }
558 ast::Statement::Attach(index) => attach(ast, catalog, parameters, session, index),
559 ast::Statement::Detach { name, if_exists } => {
560 Ok(Bound::Detach { name: ast.string(name).to_string(), if_exists })
561 }
562 ast::Statement::Transaction(kind) => Ok(Bound::Transaction(kind)),
563 ast::Statement::Explain { query, analyze, statistics, codegen } => {
564 let mut binder = Binder::with(catalog, parameters, session);
565 let (root, _) = binder.bind_query(ast, query)?;
566 Ok(Bound::Explain { plan: finish(binder, root)?, analyze, statistics, codegen })
567 }
568 ast::Statement::CopyTo(index) => {
569 let copy = &ast.copies[index as usize];
570 let mut binder = Binder::with(catalog, parameters, session);
571 let (root, _) = binder.bind_query(ast, copy.query)?;
572 copy_to(copy, finish(binder, root)?).map(Bound::CopyTo)
573 }
574 }
575}
576
577fn copy_to(copy: &ast::CopyTo, plan: Plan) -> Result<CopyTo> {
583 let lowered = copy.path.to_ascii_lowercase();
584 let mut format = if lowered.ends_with(".parquet") {
585 "parquet"
586 } else if [".json", ".ndjson", ".jsonl"].iter().any(|end| lowered.ends_with(end)) {
587 "json"
588 } else {
589 "csv"
590 }
591 .to_string();
592 if let Some((_, Some(written))) = copy.options.iter().rev().find(|(name, _)| name == "format") {
593 format = written.trim_matches('\'').to_ascii_lowercase();
594 }
595 match format.as_str() {
596 "csv" => {}
597 "json" => return json_to(copy, plan),
598 "parquet" => {
599 return Err(Error::not_implemented("COPY TO with FORMAT parquet is not supported yet"));
600 }
601 _ => {
602 return Err(Error::catalog(format!(
603 "Copy Function with name {format} does not exist!"
604 )));
605 }
606 }
607 let mut out = CopyTo {
608 plan,
609 path: copy.path.clone(),
610 header: true,
611 delimiter: ",".to_string(),
612 quote: "\"".to_string(),
613 escape: "\"".to_string(),
614 null: String::new(),
615 force_quote: Vec::new(),
616 force_quote_all: false,
617 json: false,
618 array: false,
619 };
620 for (name, value) in ©.options {
621 let text = || {
622 value.clone().ok_or_else(|| {
623 Error::binder(format!("\"{name}\" expects a single argument as a string value"))
624 })
625 };
626 match name.as_str() {
627 "format" => {}
628 "header" => {
629 out.header = match value.as_deref().map(str::to_ascii_lowercase).as_deref() {
630 None | Some("true" | "1" | "on") => true,
631 Some("false" | "0" | "off") => false,
632 Some(other) => {
633 return Err(Error::binder(format!(
634 "\"header\" expects a boolean value, not {other}"
635 )));
636 }
637 };
638 }
639 "delimiter" | "delim" | "sep" => {
641 out.delimiter = text()?.replace("\\t", "\t");
642 }
643 "quote" => out.quote = text()?,
644 "escape" => out.escape = text()?,
645 "null" | "nullstr" => out.null = text()?,
646 "force_quote" => {
647 let written = text()?;
648 let written = written.trim();
649 let list = written.strip_prefix('(').and_then(|rest| rest.strip_suffix(')'));
650 let list = list.unwrap_or(written);
651 if list.trim() == "*" {
652 out.force_quote_all = true;
653 } else {
654 for column in list.split(',') {
655 let column = column.trim();
656 let unquoted =
657 column.strip_prefix('"').and_then(|rest| rest.strip_suffix('"'));
658 out.force_quote.push(unquoted.unwrap_or(column).to_string());
659 }
660 }
661 }
662 "compression"
663 | "dateformat"
664 | "date_format"
665 | "timestampformat"
666 | "timestamp_format"
667 | "new_line"
668 | "prefix"
669 | "suffix"
670 | "per_thread_output"
671 | "file_size_bytes"
672 | "partition_by"
673 | "overwrite"
674 | "overwrite_or_ignore"
675 | "filename_pattern"
676 | "file_extension"
677 | "use_tmp_file"
678 | "return_files"
679 | "write_partition_columns"
680 | "preserve_order"
681 | "force_not_null"
682 | "encoding" => {
683 return Err(Error::not_implemented(format!(
684 "COPY TO with the option {name} is not supported yet"
685 )));
686 }
687 _ => {
688 return Err(Error::not_implemented(format!(
689 "Unrecognized option \"{name}\" for csv"
690 )));
691 }
692 }
693 }
694 if out.delimiter.is_empty() {
695 return Err(Error::binder("The delimiter option cannot be empty"));
696 }
697 Ok(out)
698}
699
700fn json_to(copy: &ast::CopyTo, plan: Plan) -> Result<CopyTo> {
703 let mut out = CopyTo {
704 plan,
705 path: copy.path.clone(),
706 header: false,
707 delimiter: String::new(),
708 quote: String::new(),
709 escape: String::new(),
710 null: String::new(),
711 force_quote: Vec::new(),
712 force_quote_all: false,
713 json: true,
714 array: false,
715 };
716 for (name, value) in ©.options {
717 match name.as_str() {
718 "format" => {}
719 "array" => {
720 out.array = match value.as_deref().map(str::to_ascii_lowercase).as_deref() {
721 None | Some("true" | "t" | "1" | "on" | "y" | "yes") => true,
722 Some("false" | "f" | "0" | "off" | "n" | "no") => false,
723 Some(_) => {
724 let written = value.as_deref().unwrap_or_default();
725 return Err(Error::invalid_input(format!(
726 "Failed to cast value: Could not convert string '{written}' to BOOL"
727 )));
728 }
729 };
730 }
731 "compression"
732 | "dateformat"
733 | "date_format"
734 | "timestampformat"
735 | "timestamp_format"
736 | "per_thread_output"
737 | "file_size_bytes"
738 | "partition_by"
739 | "overwrite"
740 | "overwrite_or_ignore"
741 | "filename_pattern"
742 | "file_extension"
743 | "use_tmp_file"
744 | "return_files"
745 | "write_partition_columns"
746 | "preserve_order" => {
747 return Err(Error::not_implemented(format!(
748 "COPY TO with the option {name} is not supported yet"
749 )));
750 }
751 _ => {
752 return Err(Error::binder(format!(
753 "Unknown option for COPY ... TO ... (FORMAT JSON): \"{name}\"."
754 )));
755 }
756 }
757 }
758 Ok(out)
759}
760
761pub fn bind_statement_sql(sql: &str, catalog: &Catalog) -> Result<Bound> {
767 let ast = parse_ast(sql)?;
768 bind_statement(&ast, catalog)
769}
770
771pub(crate) fn written_type(
775 catalog: &Catalog,
776 text: &str,
777) -> Result<(LogicalType, Vec<QualifiedName>)> {
778 let mut uses = Vec::new();
779 let ty = LogicalType::parse_with(text, &mut |parts| {
780 let parts: Vec<&str> = parts.iter().map(String::as_str).collect();
781 let made = catalog.resolve_type(&parts)?;
782 uses.push(made.name().clone());
783 Some(made.ty().clone())
784 })?;
785 Ok((ty, uses))
786}
787
788pub(crate) fn read_type(catalog: &Catalog, text: &str) -> Result<LogicalType> {
790 written_type(catalog, text).map(|(ty, _)| ty)
791}
792
793fn finish(binder: Binder<'_>, root: rudb_plan::NodeRef) -> Result<Plan> {
794 let mut plan = binder.into_plan();
795 plan.set_root(root);
796 plan.validate()?;
797 Ok(plan)
798}
799
800fn create_table(
801 ast: &Ast,
802 catalog: &Catalog,
803 parameters: &Parameters,
804 session: &Session,
805 index: ast::CreateTableRef,
806) -> Result<Bound> {
807 let written = ast.create_table(index);
808 let parts: Vec<&str> = ast.name(written.name).collect();
809 let name = if written.temporary {
810 catalog.resolve_for_create_temporary(&parts)?
811 } else {
812 catalog.resolve_for_create(&parts)?
813 };
814 let defs = ast.column_defs(written.columns);
815 let (mut columns, source) = if written.query == NONE {
816 let mut columns = Vec::with_capacity(defs.len());
817 for def in defs {
818 let text = ast.string(def.ty);
819 if text.is_empty() {
820 return Err(Error::binder(format!(
821 "Column \"{}\" was declared without a type",
822 ast.string(def.name)
823 )));
824 }
825 let ty = read_type(catalog, text)?;
826 let column = ast.string(def.name);
827 columns.push(if def.not_null {
828 Field::required(column, ty)
829 } else {
830 Field::new(column, ty)
831 });
832 }
833 (columns, None)
834 } else {
835 let mut binder = Binder::with(catalog, parameters, session);
836 let (root, scope) = binder.bind_query(ast, written.query)?;
837 if defs.len() > scope.len() {
838 return Err(Error::binder("Target table has more colum names than query result."));
841 }
842 let mut columns = Vec::with_capacity(scope.columns.len());
843 for (at, column) in scope.columns.iter().enumerate() {
844 let named = match defs.get(at) {
845 Some(def) => ast.string(def.name).to_string(),
846 None => column.name.clone(),
847 };
848 columns.push(Field::new(named, column.ty.clone()));
849 }
850 if defs.is_empty() {
851 deduplicate(&mut columns);
852 }
853 (columns, Some(finish(binder, root)?))
854 };
855 duplicate_check(&columns)?;
856 let mut defaults = Vec::with_capacity(defs.len());
857 let mut sequences = Vec::new();
858 for def in defs {
859 defaults.push(if def.default == NONE {
860 None
861 } else {
862 let (text, used) = default_text(ast, def.default, catalog, parameters, session)?;
863 for name in used {
864 if !sequences.contains(&name) {
865 sequences.push(name);
866 }
867 }
868 Some(text)
869 });
870 }
871 let mut checks = Vec::new();
872 for &expr in ast.expr_list(written.checks) {
873 checks.push(check_text(ast, expr, &columns, catalog, parameters, session)?);
874 }
875 let mut keys = Vec::new();
876 for (at, &names) in ast.name_list(written.keys).iter().enumerate() {
877 let mut places = Vec::new();
878 for wanted in ast.name(names) {
879 let Some(place) = columns.iter().position(|field| same_name(&field.name, wanted))
880 else {
881 return Err(Error::catalog(format!(
882 "table \"{}\" does not have a column named \"{wanted}\"",
883 name.table
884 )));
885 };
886 places.push(place);
887 }
888 let primary = at as u32 == written.primary;
889 if primary {
890 for &place in &places {
891 columns[place].not_null = true;
892 }
893 }
894 keys.push(rudb_catalog::Key { columns: places, primary });
895 }
896 let mut foreign = Vec::new();
897 let lists = ast.name_list(written.foreign).iter();
898 let tables = ast.name_list(written.foreign_tables).iter();
899 let referenced = ast.name_list(written.foreign_referenced).iter();
900 for ((&names, &table), &wanted) in lists.zip(tables).zip(referenced) {
901 let names: Vec<&str> = ast.name(names).collect();
902 let parts: Vec<&str> = ast.name(table).collect();
903 let wanted: Vec<&str> = ast.name(wanted).collect();
904 let key = (names.as_slice(), parts.as_slice(), wanted.as_slice());
905 foreign.push(foreign_key(catalog, &name, (&columns, &keys), key)?);
906 }
907 Ok(Bound::CreateTable(CreateTable {
908 name,
909 columns,
910 source,
911 if_not_exists: written.if_not_exists,
912 or_replace: written.or_replace,
913 keys,
914 defaults,
915 checks,
916 foreign,
917 sequences,
918 order: ast.constraint_list(written.order).iter().map(|&held| constraint(held)).collect(),
919 }))
920}
921
922fn constraint(held: ast::Constraint) -> rudb_catalog::Constraint {
924 match held {
925 ast::Constraint::Key(at) => rudb_catalog::Constraint::Key(at as usize),
926 ast::Constraint::Check(at) => rudb_catalog::Constraint::Check(at as usize),
927 ast::Constraint::Foreign(at) => rudb_catalog::Constraint::Foreign(at as usize),
928 ast::Constraint::NotNull(at) => rudb_catalog::Constraint::NotNull(at as usize),
929 }
930}
931
932fn foreign_key(
938 catalog: &Catalog,
939 made: &QualifiedName,
940 (columns, keys): (&[Field], &[rudb_catalog::Key]),
941 (names, parts, wanted): (&[&str], &[&str], &[&str]),
942) -> Result<rudb_catalog::ForeignKey> {
943 let mut places = Vec::with_capacity(names.len());
944 for &wanted in names {
945 let Some(place) = columns.iter().position(|field| same_name(&field.name, wanted)) else {
946 return Err(Error::binder(format!(
947 "Failed to create foreign key: referencing column \"{wanted}\" does not exist"
948 )));
949 };
950 places.push(place);
951 }
952 let own = parts.last().is_some_and(|last| same_name(last, &made.table))
953 && catalog.resolve(parts).map_or(true, |resolved| resolved == *made);
954 let (table, fields, held): (QualifiedName, Vec<Field>, Vec<rudb_catalog::Key>) = if own {
955 (made.clone(), columns.to_vec(), keys.to_vec())
956 } else {
957 let resolved = catalog.resolve(parts)?;
958 if catalog.view(&resolved).is_ok() {
959 return Err(Error::binder("cannot reference a VIEW with a FOREIGN KEY"));
960 }
961 let table = catalog.table(&resolved)?;
962 (resolved, table.columns().to_vec(), table.keys().to_vec())
963 };
964 let referenced = if wanted.is_empty() {
965 let Some(primary) = held.iter().find(|key| key.primary) else {
966 return Err(Error::binder(format!(
967 "Failed to create foreign key: there is no primary key for referenced table \"{}\"",
968 table.table
969 )));
970 };
971 if primary.columns.len() != places.len() {
972 return Err(Error::parser(
973 "The number of referencing and referenced columns for foreign keys must be the same",
974 ));
975 }
976 primary.columns.clone()
977 } else {
978 let mut referenced = Vec::with_capacity(wanted.len());
979 for &column in wanted {
980 let Some(place) = fields.iter().position(|field| same_name(&field.name, column)) else {
981 return Err(Error::binder(format!(
982 "Failed to create foreign key: referenced table \"{}\" does not have a column \
983 named \"{column}\"",
984 table.table
985 )));
986 };
987 referenced.push(place);
988 }
989 let mut sorted = referenced.clone();
990 sorted.sort_unstable();
991 let matched = held.iter().any(|key| {
992 let mut columns = key.columns.clone();
993 columns.sort_unstable();
994 columns == sorted
995 });
996 if !matched && held.is_empty() {
997 return Err(Error::binder(format!(
998 "Failed to create foreign key: there is no primary key or unique constraint for \
999 referenced table \"{}\"",
1000 table.table
1001 )));
1002 }
1003 if !matched {
1004 return Err(Error::binder(format!(
1005 "Failed to create foreign key: referenced table \"{}\" does not have a primary key \
1006 or unique constraint on the columns {}",
1007 table.table,
1008 wanted.join(", ")
1009 )));
1010 }
1011 referenced
1012 };
1013 for (&from, &to) in places.iter().zip(&referenced) {
1014 if columns[from].ty != fields[to].ty {
1015 return Err(Error::binder(format!(
1016 "Failed to create foreign key: incompatible types between column \"{}\" (\"{}\") \
1017 and column \"{}\" (\"{}\")",
1018 fields[to].name, fields[to].ty, columns[from].name, columns[from].ty
1019 )));
1020 }
1021 }
1022 Ok(rudb_catalog::ForeignKey { columns: places, table, referenced })
1023}
1024
1025fn check_text(
1027 ast: &Ast,
1028 expr: ast::ExprRef,
1029 columns: &[Field],
1030 catalog: &Catalog,
1031 parameters: &Parameters,
1032 session: &Session,
1033) -> Result<String> {
1034 if crate::expr::has_aggregate(ast, expr) {
1035 return Err(Error::binder("aggregate functions are not allowed in check constraints"));
1036 }
1037 let mut binder = Binder::with(catalog, parameters, session);
1038 let index = binder.fresh_index();
1039 let mut scope = crate::scope::Scope::empty();
1040 for (at, field) in columns.iter().enumerate() {
1041 scope.push(crate::scope::Visible {
1042 table: String::new(),
1043 name: field.name.clone(),
1044 binding: rudb_plan::ColumnBinding::new(index, at as u32),
1045 ty: field.ty.clone(),
1046 not_null: false,
1047 key: None,
1048 default: None,
1049 qualified: false,
1050 also: None,
1051 });
1052 }
1053 match binder.bind_expr(ast, expr, &scope) {
1054 Err(error) if error.message().starts_with("Referenced column \"") => {
1055 let column = error.message().split('"').nth(1).unwrap_or_default();
1056 Err(Error::binder(format!(
1057 "Table does not contain column \"{column}\" referenced in check constraint!"
1058 )))
1059 }
1060 Err(error) => Err(error),
1061 Ok(_) if !binder.windows.is_empty() => {
1062 Err(Error::binder("window functions are not allowed in check constraints"))
1063 }
1064 Ok(_) => Ok(deparse::expression(ast, expr)),
1065 }
1066}
1067
1068pub fn bind_checks(
1075 catalog: &Catalog,
1076 parameters: &Parameters,
1077 session: &Session,
1078 name: &QualifiedName,
1079) -> Result<Option<Checks>> {
1080 let table = catalog.table(name)?;
1081 if table.checks().is_empty() {
1082 return Ok(None);
1083 }
1084 let failed: Vec<String> =
1085 table.checks().iter().map(|text| format!("NOT CAST(({text}) AS BOOLEAN)")).collect();
1086 let ast = parse_ast(&format!("SELECT {}", failed.join(", ")))?;
1087 let ast::Statement::Query(query) = ast.statements[0] else {
1088 return Err(Error::internal("a check that is not an expression"));
1089 };
1090 let ast::QueryBody::Select(select) = ast.query(query).body else {
1091 return Err(Error::internal("a check that is not an expression"));
1092 };
1093 let mut binder = Binder::with(catalog, parameters, session);
1094 let (root, scope) =
1095 binder.bind_catalog_table(&ast, name, name.table.clone(), ast::Slice::default())?;
1096 let mut exprs = Vec::with_capacity(failed.len());
1097 let mut names = Vec::with_capacity(failed.len());
1098 for target in ast.target_list(ast.select(select).targets) {
1099 exprs.push(binder.bind_expr(&ast, target.expr, &scope)?);
1100 names.push(binder.plan_mut().intern("failed"));
1101 }
1102 let exprs = binder.plan_mut().add_expr_list(&exprs);
1103 let names = binder.plan_mut().add_name_list(&names);
1104 let index = binder.fresh_index();
1105 let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1106 let messages = table
1107 .checks()
1108 .iter()
1109 .map(|text| {
1110 format!(
1111 "CHECK constraint failed on table \"{}\" with expression CHECK({text})",
1112 name.table
1113 )
1114 })
1115 .collect();
1116 Ok(Some(Checks { plan: Box::new(finish(binder, root)?), messages }))
1117}
1118
1119fn default_text(
1123 ast: &Ast,
1124 expr: ast::ExprRef,
1125 catalog: &Catalog,
1126 parameters: &Parameters,
1127 session: &Session,
1128) -> Result<(String, Vec<QualifiedName>)> {
1129 if crate::expr::has_aggregate(ast, expr) {
1130 return Err(Error::binder("DEFAULT value cannot contain aggregates!"));
1131 }
1132 let mut binder = Binder::with(catalog, parameters, session);
1133 let before = binder.plan_mut().node_count();
1134 match binder.bind_expr(ast, expr, &crate::scope::Scope::empty()) {
1135 Err(error) if error.message().starts_with("Referenced ") => {
1136 Err(Error::binder("DEFAULT value cannot contain column names"))
1137 }
1138 Err(error) => Err(error),
1139 Ok(_) if binder.plan_mut().node_count() > before => {
1141 Err(Error::binder("DEFAULT value cannot contain subqueries"))
1142 }
1143 Ok(_) if !binder.windows.is_empty() => {
1144 Err(Error::binder("DEFAULT value cannot contain window functions!"))
1145 }
1146 Ok(_) => Ok((deparse::expression(ast, expr), binder.sequences)),
1147 }
1148}
1149
1150fn alter(
1154 ast: &Ast,
1155 catalog: &Catalog,
1156 parameters: &Parameters,
1157 session: &Session,
1158 index: ast::AlterRef,
1159) -> Result<Bound> {
1160 let written = ast.alter(index);
1161 let nothing = |name| Ok(Bound::Alter(Alter { name, alteration: None, rewrite: None }));
1162 let parts: Vec<&str> = ast.name(written.name).collect();
1163 let wanted = if written.view { Entry::View } else { Entry::Table };
1164 let name = match catalog.resolve_as(&parts, wanted) {
1165 Ok(name) => name,
1166 Err(_) if written.quiet => return nothing(None),
1167 Err(error) => return Err(error),
1168 };
1169 let kind = catalog.entry(&name)?;
1170 if written.view && kind == Entry::Table {
1171 return Err(Error::catalog("Can only modify table with ALTER TABLE statement"));
1172 }
1173 if let ast::AlterAction::Rename { to } = written.action {
1174 let alteration = rudb_catalog::Alteration::Rename(ast.string(to).to_string());
1175 return Ok(Bound::Alter(Alter {
1176 name: Some(name),
1177 alteration: Some(alteration),
1178 rewrite: None,
1179 }));
1180 }
1181 if kind == Entry::View {
1182 return Err(Error::catalog("Can only modify view with ALTER VIEW statement"));
1183 }
1184 let table = catalog.table(&name)?;
1185 let fields = table.columns();
1186 let place = |column: ast::StrRef| {
1187 fields.iter().position(|field| same_name(&field.name, ast.string(column)))
1188 };
1189 let missing = |column: ast::StrRef| {
1190 let names: Vec<String> = fields.iter().map(|field| format!("\"{}\"", field.name)).collect();
1191 Error::binder(format!(
1192 "Table \"{}\" does not have a column with name \"{}\"\n\nDid you mean: {}",
1193 name.table,
1194 ast.string(column),
1195 names.join(", ")
1196 ))
1197 };
1198 let found = |column: ast::StrRef| place(column).ok_or_else(|| missing(column));
1199 let checks = table.checks();
1200 let mut rewrite = None;
1201 let alteration = match written.action {
1202 ast::AlterAction::Rename { .. } => unreachable!("a rename is handled above"),
1203 ast::AlterAction::RenameColumn { column, to } => {
1204 let at = found(column)?;
1205 let (old, to) = (fields[at].name.as_str(), ast.string(to));
1206 if in_foreign_key(catalog, &name, table, at) {
1207 return Err(Error::catalog(format!(
1209 "Cannot rename column \"\"{old}\"\" because this is involved in the foreign key \
1210 constraint"
1211 )));
1212 }
1213 let checks =
1214 checks.iter().map(|text| rename_in(text, old, to)).collect::<Result<Vec<_>>>()?;
1215 rudb_catalog::Alteration::RenameColumn { column: at, to: to.to_string(), checks }
1216 }
1217 ast::AlterAction::AddColumn { column, quiet } => {
1218 if quiet && place(column.name).is_some() {
1219 return nothing(Some(name));
1220 }
1221 let ty = read_type(catalog, ast.string(column.ty))?;
1222 let field = Field {
1223 not_null: column.not_null,
1224 ..Field::new(ast.string(column.name), ty.clone())
1225 };
1226 let (default, sequences) = if column.default == NONE {
1227 (None, Vec::new())
1228 } else {
1229 let (text, used) = default_text(ast, column.default, catalog, parameters, session)?;
1230 (Some(text), used)
1231 };
1232 rewrite = Some(table_rewrite(
1233 ast,
1234 (catalog, parameters, session),
1235 &name,
1236 |binder, _, out| {
1237 let value = if column.default == NONE {
1238 let null = binder.add_constant(Value::Null);
1239 binder.cast_to(null, &ty)
1240 } else {
1241 let value =
1242 binder.bind_expr(ast, column.default, &crate::scope::Scope::empty())?;
1243 binder.checked_cast_to(value, &ty, false)?
1244 };
1245 out.push((value, field.name.clone()));
1246 Ok(())
1247 },
1248 )?);
1249 rudb_catalog::Alteration::AddColumn { field, default, sequences }
1250 }
1251 ast::AlterAction::DropColumn { column, quiet } => {
1252 let Some(at) = place(column) else {
1253 return if quiet { nothing(Some(name)) } else { Err(missing(column)) };
1254 };
1255 let dropped = fields[at].name.as_str();
1256 let mut kept = Vec::with_capacity(checks.len());
1257 for text in checks {
1258 let used = columns_in(text)?;
1259 if !used.iter().any(|used| same_name(used, dropped)) {
1260 kept.push(text.clone());
1261 } else if used.iter().any(|used| !same_name(used, dropped)) {
1262 return Err(Error::catalog(format!(
1263 "Cannot drop column \"{dropped}\" because there is a CHECK constraint that \
1264 depends on it"
1265 )));
1266 }
1267 }
1268 rewrite =
1269 Some(table_rewrite(ast, (catalog, parameters, session), &name, |_, _, out| {
1270 out.remove(at);
1271 Ok(())
1272 })?);
1273 rudb_catalog::Alteration::DropColumn { column: at, checks: kept }
1274 }
1275 ast::AlterAction::Default { column, default } => {
1276 let at = found(column)?;
1277 let (default, sequences) = if default == NONE {
1278 (None, Vec::new())
1279 } else {
1280 let (text, used) = default_text(ast, default, catalog, parameters, session)?;
1281 (Some(text), used)
1282 };
1283 rudb_catalog::Alteration::Default { column: at, default, sequences }
1284 }
1285 ast::AlterAction::NotNull { column, set } => {
1286 rudb_catalog::Alteration::NotNull { column: found(column)?, set }
1287 }
1288 ast::AlterAction::Type { column, ty, using } => {
1289 let at = found(column)?;
1290 let changed = fields[at].name.as_str();
1291 if table.keys().iter().any(|key| key.columns.contains(&at)) {
1292 return Err(Error::binder(
1293 "Cannot change the type of a column that has a UNIQUE or PRIMARY KEY \
1294 constraint specified",
1295 ));
1296 }
1297 for text in checks {
1298 if columns_in(text)?.iter().any(|used| same_name(used, changed)) {
1299 return Err(Error::binder(
1300 "Cannot change the type of a column that has a CHECK constraint specified",
1301 ));
1302 }
1303 }
1304 if in_foreign_key(catalog, &name, table, at) {
1305 return Err(Error::binder(
1306 "Cannot change the type of a column that has a FOREIGN KEY constraint specified",
1307 ));
1308 }
1309 let mut target =
1310 if ty == NONE { None } else { Some(read_type(catalog, ast.string(ty))?) };
1311 rewrite = Some(table_rewrite(
1312 ast,
1313 (catalog, parameters, session),
1314 &name,
1315 |binder, scope, out| {
1316 let value = if using == NONE {
1317 out[at].0
1318 } else {
1319 binder.bind_expr(ast, using, scope)?
1320 };
1321 let ty = target
1322 .get_or_insert_with(|| binder.plan_mut().expr_type(value).clone())
1323 .clone();
1324 out[at].0 = binder.checked_cast_to(value, &ty, false)?;
1325 Ok(())
1326 },
1327 )?);
1328 let ty = target.ok_or_else(|| Error::internal("an ALTER TYPE that settled no type"))?;
1329 rudb_catalog::Alteration::Type { column: at, ty }
1330 }
1331 };
1332 Ok(Bound::Alter(Alter { name: Some(name), alteration: Some(alteration), rewrite }))
1333}
1334
1335fn create_index(
1343 ast: &Ast,
1344 catalog: &Catalog,
1345 parameters: &Parameters,
1346 session: &Session,
1347 at: ast::IndexRef,
1348) -> Result<Bound> {
1349 let written = ast.index(at);
1350 let parts: Vec<String> = ast.name(written.name).map(str::to_string).collect();
1351 if written.drop {
1352 return Ok(Bound::Index(IndexChange {
1353 table: None,
1354 index: None,
1355 name: parts,
1356 quiet: written.quiet,
1357 }));
1358 }
1359 let written_table: Vec<&str> = ast.name(written.table).collect();
1360 let name = catalog.resolve_as(&written_table, Entry::Table)?;
1361 if catalog.entry(&name)? == Entry::View {
1362 return Err(Error::binder("can only create an index on a base table"));
1363 }
1364 if written.using != NONE && !same_name(ast.string(written.using), "art") {
1365 return Err(Error::binder(format!("Unknown index type: {}", ast.string(written.using))));
1366 }
1367 let fields = catalog.table(&name)?.columns();
1368 let mut binder = Binder::with(catalog, parameters, session);
1369 let (_, scope) =
1370 binder.bind_catalog_table(ast, &name, name.table.clone(), ast::Slice::default())?;
1371 let mut columns = Vec::new();
1372 let mut plain = true;
1373 let mut texts = Vec::new();
1374 for &expr in ast.expr_list(written.elements) {
1375 if let ast::Expr::Column { name: column } = ast.exprs[expr as usize] {
1376 let column: Vec<&str> = ast.name(column).collect();
1377 if let [only] = column[..]
1378 && !fields.iter().any(|field| same_name(&field.name, only))
1379 {
1380 let names: Vec<String> =
1381 fields.iter().map(|field| format!("\"{}\"", field.name)).collect();
1382 return Err(Error::binder(format!(
1384 "Table \"{}\" does not have a column named \"{only}\"\n\nCandidate bindings: \
1385 : {}",
1386 name.table,
1387 names.join(", ")
1388 )));
1389 }
1390 }
1391 if crate::expr::has_aggregate(ast, expr) {
1392 return Err(Error::binder("aggregate functions are not allowed in index expressions"));
1393 }
1394 let before = binder.plan_mut().node_count();
1395 let value = binder.bind_expr(ast, expr, &scope)?;
1396 if binder.plan_mut().node_count() > before {
1397 return Err(Error::binder("cannot use subquery in index expressions"));
1398 }
1399 if !binder.windows.is_empty() {
1400 return Err(Error::binder("window functions are not allowed in index expressions"));
1401 }
1402 let ty = binder.plan_mut().expr_type(value).clone();
1403 if ty.is_nested() {
1404 return Err(Error::invalid_type(format!(
1405 "Invalid Type [{ty}]: Invalid type for index key."
1406 )));
1407 }
1408 let bare = match binder.plan_mut().expr(value) {
1409 Expr::Column(binding) => scope.columns.iter().position(|held| held.binding == *binding),
1410 _ => None,
1411 };
1412 if let Some(at) = bare {
1413 columns.push(at);
1414 texts.push(rudb_parse::quoted(&fields[at].name));
1415 continue;
1416 }
1417 plain = false;
1418 let text = deparse::expression(ast, expr);
1419 let used = columns_in(&text)?;
1420 if used.is_empty() {
1421 return Err(Error::binder(
1422 "CREATE INDEX does not refer to any columns in the base table!",
1423 ));
1424 }
1425 for used in used {
1426 if let Some(at) = fields.iter().position(|field| same_name(&field.name, &used)) {
1427 columns.push(at);
1428 }
1429 }
1430 texts.push(format!("({text})"));
1431 }
1432 if written.unique && !plain {
1433 return Err(Error::not_implemented("A UNIQUE index over an expression is not supported"));
1434 }
1435 let expressions = Value::List {
1436 element: LogicalType::Varchar,
1437 values: texts.iter().map(|text| Value::Varchar(text.clone())).collect(),
1438 };
1439 let unique = if written.unique { "UNIQUE " } else { "" };
1440 let table: Vec<String> = written_table.iter().map(|part| rudb_parse::quoted(part)).collect();
1441 let using = if written.using == NONE {
1442 String::new()
1443 } else {
1444 format!(" USING {} ", ast.string(written.using))
1445 };
1446 let index_name = parts.last().cloned().unwrap_or_default();
1447 let sql = format!(
1448 "CREATE {unique}INDEX {} ON {}{using}({});",
1449 rudb_parse::quoted(&index_name),
1450 table.join("."),
1451 texts.join(", ")
1452 );
1453 if !plain {
1454 columns.sort_unstable();
1455 columns.dedup();
1456 }
1457 let index = rudb_catalog::Index {
1458 name: index_name,
1459 unique: written.unique,
1460 columns,
1461 plain,
1462 expressions: expressions.to_string(),
1463 sql,
1464 oid: 0,
1465 };
1466 Ok(Bound::Index(IndexChange {
1467 table: Some(name),
1468 index: Some(index),
1469 name: Vec::new(),
1470 quiet: written.quiet,
1471 }))
1472}
1473
1474fn in_foreign_key(
1477 catalog: &Catalog,
1478 name: &QualifiedName,
1479 table: &rudb_catalog::Table,
1480 at: usize,
1481) -> bool {
1482 table.foreign().iter().any(|key| key.columns.contains(&at))
1483 || catalog.tables().any(|held| {
1484 held.foreign().iter().any(|key| key.table == *name && key.referenced.contains(&at))
1485 })
1486}
1487
1488fn table_rewrite(
1491 ast: &Ast,
1492 (catalog, parameters, session): (&Catalog, &Parameters, &Session),
1493 name: &QualifiedName,
1494 change: impl FnOnce(
1495 &mut Binder<'_>,
1496 &crate::scope::Scope,
1497 &mut Vec<(ExprRef, String)>,
1498 ) -> Result<()>,
1499) -> Result<Plan> {
1500 let mut binder = Binder::with(catalog, parameters, session);
1501 let (root, scope) =
1502 binder.bind_catalog_table(ast, name, name.table.clone(), ast::Slice::default())?;
1503 let mut out = Vec::with_capacity(scope.columns.len() + 1);
1504 for column in &scope.columns {
1505 let expr = binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
1506 out.push((expr, column.name.clone()));
1507 }
1508 change(&mut binder, &scope, &mut out)?;
1509 let exprs: Vec<ExprRef> = out.iter().map(|(expr, _)| *expr).collect();
1510 let names: Vec<_> = out.iter().map(|(_, name)| binder.plan_mut().intern(name)).collect();
1511 let exprs = binder.plan_mut().add_expr_list(&exprs);
1512 let names = binder.plan_mut().add_name_list(&names);
1513 let index = binder.fresh_index();
1514 let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1515 finish(binder, root)
1516}
1517
1518fn check_ast(text: &str) -> Result<(Ast, ast::ExprRef)> {
1520 let ast = parse_ast(&format!("SELECT {text}"))?;
1521 let found = match ast.statements.first() {
1522 Some(&ast::Statement::Query(query)) => match ast.query(query).body {
1523 ast::QueryBody::Select(select) => {
1524 ast.target_list(ast.select(select).targets).first().map(|target| target.expr)
1525 }
1526 _ => None,
1527 },
1528 _ => None,
1529 };
1530 let expr = found.ok_or_else(|| Error::internal("a check that is not an expression"))?;
1531 Ok((ast, expr))
1532}
1533
1534fn columns_in(text: &str) -> Result<Vec<String>> {
1536 let (ast, _) = check_ast(text)?;
1537 let mut out = Vec::new();
1538 for expr in &ast.exprs {
1539 if let ast::Expr::Column { name } = *expr
1540 && let Some(last) = ast.name(name).last()
1541 {
1542 out.push(last.to_string());
1543 }
1544 }
1545 Ok(out)
1546}
1547
1548fn rename_in(text: &str, old: &str, to: &str) -> Result<String> {
1551 let (mut ast, expr) = check_ast(text)?;
1552 let mut renamed = false;
1553 for at in 0..ast.exprs.len() {
1554 let ast::Expr::Column { name } = ast.exprs[at] else { continue };
1555 if name.len == 0 {
1556 continue;
1557 }
1558 let last = (name.start + name.len - 1) as usize;
1559 if same_name(ast.string(ast.parts[last]), old) {
1560 let index = ast.strings.len() as u32;
1561 ast.strings.push(to.to_string());
1562 ast.parts[last] = index;
1563 renamed = true;
1564 }
1565 }
1566 Ok(if renamed { deparse::expression(&ast, expr) } else { text.to_string() })
1567}
1568
1569fn deduplicate(columns: &mut [Field]) {
1582 for at in 0..columns.len() {
1583 let taken = |name: &str, upto: usize, columns: &[Field]| {
1584 columns[..upto].iter().any(|held| same_name(&held.name, name))
1585 };
1586 if !taken(&columns[at].name, at, columns) {
1587 continue;
1588 }
1589 let mut suffix = 1;
1590 let mut candidate = format!("{}_{suffix}", columns[at].name);
1591 while taken(&candidate, at, columns) {
1592 suffix += 1;
1593 candidate = format!("{}_{suffix}", columns[at].name);
1594 }
1595 columns[at].name = candidate;
1596 }
1597}
1598
1599fn create_view(
1606 ast: &Ast,
1607 catalog: &Catalog,
1608 parameters: &Parameters,
1609 session: &Session,
1610 index: ast::CreateViewRef,
1611) -> Result<Bound> {
1612 let written = ast.create_view(index);
1613 let parts: Vec<&str> = ast.name(written.name).collect();
1614 let name = if written.temporary {
1615 catalog.resolve_for_create_temporary(&parts)?
1616 } else {
1617 catalog.resolve_for_create(&parts)?
1618 };
1619 let aliases: Vec<String> = ast.name(written.columns).map(str::to_string).collect();
1620
1621 let mut binder = Binder::with(catalog, parameters, session);
1622 binder.outlined = true;
1625 let (_, mut scope) = binder.bind_query(ast, written.query)?;
1626 if aliases.len() > scope.len() {
1627 return Err(Error::binder("More VIEW aliases than columns in query result"));
1628 }
1629 if !aliases.is_empty() {
1630 let written: Vec<&str> = aliases.iter().map(String::as_str).collect();
1631 scope.rename(&written, "unnamed_subquery")?;
1632 }
1633
1634 Ok(Bound::CreateView(CreateView {
1635 name,
1636 sql: ast.string(written.sql).to_string(),
1637 statement: deparse::create_view(ast, index),
1638 aliases,
1639 if_not_exists: written.if_not_exists,
1640 or_replace: written.or_replace,
1641 columns: scope.fields(),
1642 }))
1643}
1644
1645fn drop_table(ast: &Ast, catalog: &Catalog, index: ast::DropTableRef) -> Result<Bound> {
1646 let written = ast.drop_table(index);
1647 let kind = if written.view { Entry::View } else { Entry::Table };
1648 let mut names = Vec::new();
1649 for &name in ast.name_list(written.names) {
1650 let parts: Vec<&str> = ast.name(name).collect();
1651 match catalog.resolve_as(&parts, kind) {
1654 Ok(resolved) => names.push(resolved),
1655 Err(error) if written.if_exists => drop(error),
1656 Err(error) => return Err(error),
1657 }
1658 }
1659 Ok(Bound::DropTable(DropTable { names, kind }))
1660}
1661
1662fn setting(
1668 ast: &Ast,
1669 catalog: &Catalog,
1670 parameters: &Parameters,
1671 session: &Session,
1672 index: ast::SettingRef,
1673) -> Result<Bound> {
1674 let written = ast.setting(index);
1675 let name = ast.string(written.name).to_string();
1676 let value = if written.value == NONE {
1677 None
1678 } else {
1679 let mut binder = Binder::with(catalog, parameters, session);
1680 let bound = binder.bind_setting_value(ast, written.value)?;
1681 let Expr::Constant(value) = *binder.plan().expr(bound) else {
1682 return Err(Error::not_implemented(format!(
1683 "a value for {name} that is not a constant"
1684 )));
1685 };
1686 Some(binder.plan().value(value).clone())
1687 };
1688 Ok(Bound::Setting(Setting { name, scope: written.scope, value, pragma: written.pragma }))
1689}
1690
1691fn attach(
1693 ast: &Ast,
1694 catalog: &Catalog,
1695 parameters: &Parameters,
1696 session: &Session,
1697 index: ast::AttachRef,
1698) -> Result<Bound> {
1699 let written = ast.attach(index);
1700 let mut binder = Binder::with(catalog, parameters, session);
1701 let mut constant = |expr: ast::ExprRef, what: &str| -> Result<Value> {
1702 let bound = binder.bind_setting_value(ast, expr)?;
1703 let Expr::Constant(value) = *binder.plan().expr(bound) else {
1704 return Err(Error::not_implemented(format!("{what} of ATTACH that is not a constant")));
1705 };
1706 Ok(binder.plan().value(value).clone())
1707 };
1708 let path = match constant(written.path, "a path")? {
1709 Value::Null => {
1710 return Err(Error::binder("ATTACH path expression must not evaluate to NULL"));
1711 }
1712 value => value.to_string(),
1713 };
1714 let names = ast.name(written.names).map(str::to_string).collect::<Vec<_>>();
1715 let mut options = Vec::with_capacity(names.len());
1716 for (name, &value) in names.into_iter().zip(ast.expr_list(written.values)) {
1717 let value = if value == NONE { None } else { Some(constant(value, "an option")?) };
1718 options.push((name, value));
1719 }
1720 let alias = (written.alias != NONE).then(|| ast.string(written.alias).to_string());
1721 Ok(Bound::Attach(Attach {
1722 path,
1723 alias,
1724 or_replace: written.or_replace,
1725 if_not_exists: written.if_not_exists,
1726 options,
1727 }))
1728}
1729
1730fn clustered(
1742 binder: &mut Binder<'_>,
1743 input: rudb_plan::NodeRef,
1744 scope: &crate::scope::Scope,
1745 clustering: &Clustering,
1746 targets: &[usize],
1747 fields: &[Field],
1748) -> Result<rudb_plan::NodeRef> {
1749 let mut keys: Vec<SortKey> = Vec::with_capacity(clustering.columns().len());
1750 for (at, &column) in clustering.columns().iter().enumerate() {
1751 let Some(from) = targets.iter().position(|&target| target == column as usize) else {
1752 continue;
1753 };
1754 let source = &scope.columns[from];
1755 let expr = binder.plan_mut().add_expr(Expr::Column(source.binding), source.ty.clone());
1756 let expr = binder.checked_cast_to(expr, &fields[column as usize].ty, false)?;
1761 let expr =
1762 if at == 0 { bucketed(binder, expr, clustering.width(), fields, column) } else { expr };
1763 keys.push(SortKey { expr, descending: false, nulls_first: false });
1764 }
1765 if keys.is_empty() {
1766 return Ok(input);
1767 }
1768 let keys = binder.plan_mut().add_sort_keys(&keys);
1769 Ok(binder.plan_mut().add_node(Node::Sort { input, keys }))
1770}
1771
1772fn fitted(
1786 binder: &Binder<'_>,
1787 scope: &crate::scope::Scope,
1788 clustering: &Clustering,
1789 targets: &[usize],
1790) -> Clustering {
1791 if clustering.width() != Width::Auto {
1792 return clustering.clone();
1793 }
1794 let Some(from) = targets.iter().position(|&target| target == clustering.partition() as usize)
1795 else {
1796 return clustering.fitted(0, 0);
1797 };
1798 let source = &scope.columns[from];
1799 let Some(zones) = binder.plan().sole_zones() else {
1800 return clustering.fitted(0, 0);
1801 };
1802 let Some(at) = zones.column(&source.name) else {
1807 return clustering.fitted(0, 0);
1808 };
1809 let rows = zones.surviving(&[]).unwrap_or(0);
1810 let days = span(&zones.extreme(at, End::Low), &zones.extreme(at, End::High)).unwrap_or(0);
1811 clustering.fitted(rows, days)
1812}
1813
1814fn span(low: &Stat<ColumnBound>, high: &Stat<ColumnBound>) -> Option<u64> {
1820 let (Stat::Known { value: low, .. }, Stat::Known { value: high, .. }) = (low, high) else {
1821 return None;
1822 };
1823 let days = match (low, high) {
1824 (ColumnBound::Int(low), ColumnBound::Int(high)) => high.checked_sub(*low)?,
1826 (
1830 ColumnBound::Scaled { unscaled: low, scale: at },
1831 ColumnBound::Scaled { unscaled: high, scale: to },
1832 ) if at == to => {
1833 let day = 86_400_i128.checked_mul(10_i128.checked_pow(u32::from(*at))?)?;
1834 high.checked_sub(*low)? / day
1835 }
1836 _ => return None,
1837 };
1838 u64::try_from(days).ok()
1839}
1840
1841fn bucketed(
1843 binder: &mut Binder<'_>,
1844 expr: ExprRef,
1845 width: Width,
1846 fields: &[Field],
1847 column: u32,
1848) -> ExprRef {
1849 if width == Width::Exact {
1850 return expr;
1851 }
1852 let unit = binder.plan_mut().add_value(Value::Varchar(width.to_string().to_lowercase()));
1853 let unit = binder.plan_mut().add_expr(Expr::Constant(unit), LogicalType::Varchar);
1854 let args = binder.plan_mut().add_expr_list(&[unit, expr]);
1855 let name = binder.plan_mut().intern("date_trunc");
1856 let ty = fields[column as usize].ty.clone();
1857 binder.plan_mut().add_expr(Expr::Function { name, args }, ty)
1858}
1859
1860fn insert(
1861 ast: &Ast,
1862 catalog: &Catalog,
1863 parameters: &Parameters,
1864 session: &Session,
1865 index: ast::InsertRef,
1866) -> Result<Bound> {
1867 let written = ast.insert(index);
1868 let parts: Vec<&str> = ast.name(written.name).collect();
1869 let name = catalog.resolve(&parts)?;
1870 if catalog.entry(&name)? == Entry::View {
1871 return Err(Error::catalog(format!("{} is not an table", name.table)));
1874 }
1875 let target = catalog.table(&name)?;
1876 let fields: Vec<Field> = target.columns().to_vec();
1877 let clustering = target.clustering().cloned();
1878
1879 let targets: Vec<usize> = if written.columns.is_empty() {
1883 (0..fields.len()).collect()
1884 } else {
1885 let mut targets = Vec::new();
1886 for column in ast.name(written.columns) {
1887 let at = fields.iter().position(|field| same_name(&field.name, column)).ok_or_else(
1888 || {
1889 Error::binder(format!(
1890 "Table \"{}\" does not have a column with name \"{column}\"",
1891 name.table
1892 ))
1893 },
1894 )?;
1895 if targets.contains(&at) {
1896 return Err(Error::binder(format!("Duplicate column name \"{column}\" in INSERT")));
1897 }
1898 targets.push(at);
1899 }
1900 targets
1901 };
1902
1903 let defaults: Vec<(LogicalType, Option<String>)> = (0..fields.len())
1904 .map(|at| (fields[at].ty.clone(), target.default(at).map(str::to_owned)))
1905 .collect();
1906 let mut binder = Binder::with(catalog, parameters, session);
1907 let (root, scope) = if written.source == NONE {
1908 (binder.plan_mut().add_node(Node::Dummy), crate::scope::Scope::empty())
1911 } else {
1912 if matches!(ast.query(written.source).body, ast::QueryBody::Values(_)) {
1915 binder.insert_defaults = Some(targets.iter().map(|&at| defaults[at].clone()).collect());
1916 }
1917 if written.copy {
1921 binder.copy_into = Some(targets.iter().map(|&at| fields[at].clone()).collect());
1922 }
1923 let bound = binder.bind_query(ast, written.source)?;
1924 binder.copy_into = None;
1925 bound
1926 };
1927 let targets = if written.source == NONE { Vec::new() } else { targets };
1928 if scope.len() != targets.len() {
1930 return Err(Error::binder(if written.columns.is_empty() {
1931 format!(
1932 "table \"{}\" has {} columns but {} values were supplied",
1933 name.table,
1934 targets.len(),
1935 scope.len()
1936 )
1937 } else {
1938 format!(
1939 "Column name/value mismatch for insert on \"{}\": expected {} columns but {} \
1940 values were supplied",
1941 name.table,
1942 targets.len(),
1943 scope.len()
1944 )
1945 }));
1946 }
1947
1948 let root = match &clustering {
1954 None => root,
1955 Some(clustering) => {
1956 let fitted = fitted(&binder, &scope, clustering, &targets);
1961 clustered(&mut binder, root, &scope, &fitted, &targets, &fields)?
1962 }
1963 };
1964
1965 let mut exprs: Vec<ExprRef> = Vec::with_capacity(fields.len());
1969 let mut names = Vec::with_capacity(fields.len());
1970 for (at, field) in fields.iter().enumerate() {
1971 let expr = match targets.iter().position(|&target| target == at) {
1972 Some(from) => {
1973 let column = &scope.columns[from];
1974 let expr =
1975 binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
1976 binder.checked_cast_to(expr, &field.ty, false)?
1977 }
1978 None => binder.bind_default(defaults[at].1.as_deref(), &field.ty)?,
1980 };
1981 exprs.push(expr);
1982 let interned = binder.plan_mut().intern(&field.name);
1983 names.push(interned);
1984 }
1985 let exprs = binder.plan_mut().add_expr_list(&exprs);
1986 let names = binder.plan_mut().add_name_list(&names);
1987 let index = binder.fresh_index();
1988 let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1989 let source = finish(binder, root)?;
1990 let returning = returning(ast, catalog, parameters, session, written.returning)?;
1991 let conflict = match written.conflict {
1992 Some(conflict) => {
1993 Some(bind_conflict(ast, catalog, parameters, session, &name, &targets, conflict)?)
1994 }
1995 None => None,
1996 };
1997 let checks = bind_checks(catalog, parameters, session, &name)?;
1998 Ok(Bound::Insert(Insert { name, source, write: Write::Append, returning, conflict, checks }))
1999}
2000
2001fn bind_conflict(
2004 ast: &Ast,
2005 catalog: &Catalog,
2006 parameters: &Parameters,
2007 session: &Session,
2008 name: &QualifiedName,
2009 targets: &[usize],
2010 conflict: ast::Conflict,
2011) -> Result<Conflict> {
2012 let table = catalog.table(name)?;
2013 let fields = table.columns();
2014 let keys = table.keys();
2015 let key = if conflict.target.is_empty() {
2016 if keys.is_empty() {
2017 return Err(Error::binder(
2018 "There are no UNIQUE/PRIMARY KEY constraints that refer to this table, specify ON \
2019 CONFLICT columns manually",
2020 ));
2021 }
2022 match conflict.action {
2023 ast::ConflictAction::Nothing => None,
2024 _ if keys.len() > 1 => {
2025 return Err(Error::binder(
2026 "Conflict target has to be provided for a DO UPDATE operation when the table \
2027 has multiple UNIQUE/PRIMARY KEY constraints",
2028 ));
2029 }
2030 _ => Some(0),
2031 }
2032 } else {
2033 let mut wanted = Vec::new();
2034 for column in ast.name(conflict.target) {
2035 let Some(at) = fields.iter().position(|field| same_name(&field.name, column)) else {
2036 return Err(Error::binder(format!(
2037 "Table \"{}\" does not have a column with name \"{column}\"",
2038 name.table
2039 )));
2040 };
2041 wanted.push(at);
2042 }
2043 wanted.sort_unstable();
2044 wanted.dedup();
2045 let found = keys.iter().position(|key| {
2046 let mut held = key.columns.clone();
2047 held.sort_unstable();
2048 held == wanted
2049 });
2050 let Some(found) = found else {
2051 return Err(Error::binder(
2052 "The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY \
2053 KEY CONSTRAINT or INDEX",
2054 ));
2055 };
2056 Some(found)
2057 };
2058 let action = match conflict.action {
2059 ast::ConflictAction::Nothing => ConflictAction::Nothing,
2060 ast::ConflictAction::Replace => ConflictAction::Replace(targets.to_vec()),
2061 ast::ConflictAction::Update { columns: written, query } => {
2062 let mut columns = Vec::new();
2063 for column in ast.name(written) {
2064 let Some(at) = fields.iter().position(|field| same_name(&field.name, column))
2065 else {
2066 return Err(Error::binder(format!(
2067 "Referenced update column {column} not found in table!"
2068 )));
2069 };
2070 if columns.contains(&at) {
2071 return Err(Error::binder(format!(
2072 "Multiple assignments to same column \"\"{column}\"\""
2073 )));
2074 }
2075 columns.push(at);
2076 }
2077 let mut binder = Binder::with(catalog, parameters, session);
2078 binder.upsert = true;
2079 let (root, scope) = binder.bind_query(ast, query)?;
2080 let mut exprs = Vec::with_capacity(scope.columns.len());
2083 let mut names = Vec::with_capacity(scope.columns.len());
2084 for (at, column) in scope.columns.iter().enumerate() {
2085 let expr =
2086 binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
2087 let ty = columns.get(at).map_or(LogicalType::Boolean, |&to| fields[to].ty.clone());
2088 exprs.push(binder.checked_cast_to(expr, &ty, false)?);
2089 names.push(binder.plan_mut().intern(&column.name));
2090 }
2091 let exprs = binder.plan_mut().add_expr_list(&exprs);
2092 let names = binder.plan_mut().add_name_list(&names);
2093 let index = binder.fresh_index();
2094 let root =
2095 binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
2096 ConflictAction::Update { columns, plan: Box::new(finish(binder, root)?) }
2097 }
2098 };
2099 Ok(Conflict { key, action })
2100}
2101
2102fn change(
2111 ast: &Ast,
2112 catalog: &Catalog,
2113 parameters: &Parameters,
2114 session: &Session,
2115 index: ast::InsertRef,
2116 delete: bool,
2117) -> Result<Bound> {
2118 let written = ast.insert(index);
2119 let parts: Vec<&str> = ast.name(written.name).collect();
2120 let name = catalog.resolve(&parts)?;
2121 if catalog.entry(&name)? == Entry::View {
2122 return Err(Error::binder(if delete {
2123 "Can only delete from base table"
2124 } else {
2125 "Can only update base table"
2126 }));
2127 }
2128 let fields: Vec<Field> = catalog.table(&name)?.columns().to_vec();
2129 let mut targets: Vec<usize> = Vec::new();
2130 for column in ast.name(written.columns) {
2131 let at =
2132 fields.iter().position(|field| same_name(&field.name, column)).ok_or_else(|| {
2133 Error::binder(format!("Referenced update column {column} not found in table!"))
2134 })?;
2135 if targets.contains(&at) {
2136 return Err(Error::binder(format!(
2137 "Multiple assignments to same column \"\"{column}\"\""
2138 )));
2139 }
2140 targets.push(at);
2141 }
2142
2143 let mut defaulted = vec![false; targets.len()];
2145 if let ast::QueryBody::Select(select) = ast.query(written.source).body {
2146 let items = ast.target_list(ast.select(select).targets);
2147 let first = items.len().saturating_sub(targets.len());
2148 for (at, item) in items[first..].iter().enumerate() {
2149 defaulted[at] = matches!(ast.expr(item.expr), ast::Expr::Default);
2150 }
2151 }
2152 let table = catalog.table(&name)?;
2153 let mut binder = Binder::with(catalog, parameters, session);
2154 binder.default_as_null = defaulted.contains(&true);
2155 let (root, scope) = binder.bind_query(ast, written.source)?;
2156 binder.default_as_null = false;
2157 let width = fields.len();
2158 if scope.len() != width + 1 + targets.len() {
2159 return Err(Error::internal(format!(
2160 "an UPDATE source of {} columns over a table of {width}",
2161 scope.len()
2162 )));
2163 }
2164 let column = |binder: &mut Binder<'_>, at: usize| {
2165 let column = &scope.columns[at];
2166 binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone())
2167 };
2168 let hit = column(&mut binder, width);
2169 let hit = binder.checked_cast_to(hit, &LogicalType::Boolean, false)?;
2170 let mut exprs = Vec::with_capacity(width);
2171 let mut names = Vec::with_capacity(width);
2172 for (at, field) in fields.iter().enumerate() {
2173 let old = column(&mut binder, at);
2174 let expr = match targets.iter().position(|&target| target == at) {
2175 Some(from) => {
2176 let then = if defaulted[from] {
2177 binder.bind_default(table.default(at), &field.ty)?
2178 } else {
2179 let new = column(&mut binder, width + 1 + from);
2180 binder.checked_cast_to(new, &field.ty, false)?
2181 };
2182 let arms = binder.plan_mut().add_arms(&[Arm { when: hit, then }]);
2183 binder
2184 .plan_mut()
2185 .add_expr(Expr::Case { arms, otherwise: Some(old) }, field.ty.clone())
2186 }
2187 None => old,
2188 };
2189 exprs.push(expr);
2190 let interned = binder.plan_mut().intern(&field.name);
2191 names.push(interned);
2192 }
2193 let yes = binder.add_constant(Value::Boolean(true));
2196 let arms = binder.plan_mut().add_arms(&[Arm { when: hit, then: yes }]);
2197 let otherwise = Some(binder.add_constant(Value::Boolean(false)));
2198 exprs.push(binder.plan_mut().add_expr(Expr::Case { arms, otherwise }, LogicalType::Boolean));
2199 let interned = binder.plan_mut().intern("changed");
2200 names.push(interned);
2201 let exprs = binder.plan_mut().add_expr_list(&exprs);
2202 let names = binder.plan_mut().add_name_list(&names);
2203 let index = binder.fresh_index();
2204 let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
2205 let source = finish(binder, root)?;
2206 let returning = returning(ast, catalog, parameters, session, written.returning)?;
2207 let write = if delete { Write::Delete } else { Write::Update };
2208 let checks = if delete { None } else { bind_checks(catalog, parameters, session, &name)? };
2209 Ok(Bound::Insert(Insert { name, source, write, returning, conflict: None, checks }))
2210}