1use crate::{
94 Type, TypeOptions,
95 type_::{BaseType, FullType},
96 type_statement,
97 typer::resolve_table_name,
98};
99use alloc::{
100 borrow::Cow,
101 boxed::Box,
102 collections::{BTreeMap, BTreeSet},
103 rc::Rc,
104 sync::Arc,
105 vec::Vec,
106};
107use qusql_parse::{
108 AddColumn, AddIndex, AlterColumn, DataType, DataTypeProperty, DropColumn, Expression,
109 FunctionParam, Identifier, IdentifierPart, Issues, ModifyColumn, OptSpanned, QualifiedName,
110 Span, Spanned, Statement, parse_statements,
111};
112
113#[derive(Debug, Clone)]
115pub struct Column<'a> {
116 pub identifier: Identifier<'a>,
117 pub type_: FullType<'a>,
119 pub auto_increment: bool,
121 pub default: bool,
122 pub as_: Option<Expression<'a>>,
123 pub generated: bool,
124}
125
126#[derive(Debug)]
128pub struct Schema<'a> {
129 pub identifier_span: Span,
131 pub columns: Vec<Column<'a>>,
133 pub view: bool,
135}
136
137impl<'a> Schema<'a> {
138 pub fn get_column(&self, identifier: &str) -> Option<&Column<'a>> {
139 self.columns
140 .iter()
141 .find(|&column| column.identifier.value == identifier)
142 }
143 pub fn get_column_mut(&mut self, identifier: &str) -> Option<&mut Column<'a>> {
144 self.columns
145 .iter_mut()
146 .find(|column| column.identifier.value == identifier)
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct ProcedureDef<'a> {
153 pub name: Identifier<'a>,
154 pub params: Vec<FunctionParam<'a>>,
155 pub span: Span,
156 pub body: Option<Vec<Statement<'a>>>,
158}
159
160#[derive(Debug)]
163pub struct FunctionDefBody<'a> {
164 pub statements: Vec<Statement<'a>>,
166 pub src: &'a str,
168}
169
170#[derive(Debug)]
172pub struct FunctionDef<'a> {
173 pub name: Identifier<'a>,
174 pub params: Vec<FunctionParam<'a>>,
175 pub return_type: DataType<'a>,
176 pub span: Span,
177 pub body: Option<FunctionDefBody<'a>>,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
183pub struct IndexKey<'a> {
184 pub schema: Option<Identifier<'a>>,
186 pub table: Option<Identifier<'a>>,
187 pub index: Identifier<'a>,
188}
189
190#[derive(Debug)]
192pub enum TypeDef<'a> {
193 Enum {
195 values: Arc<Vec<Cow<'a, str>>>,
196 span: Span,
197 },
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
205pub enum QualifiedIdentifier<'a> {
206 Unqualified(Identifier<'a>),
208 Qualified(Identifier<'a>, Identifier<'a>),
210}
211
212impl<'a> core::fmt::Display for QualifiedIdentifier<'a> {
213 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 match self {
215 QualifiedIdentifier::Unqualified(id) => write!(f, "{}", id.value),
216 QualifiedIdentifier::Qualified(schema, table) => {
217 write!(f, "{}.{}", schema.value, table.value)
218 }
219 }
220 }
221}
222
223impl<'a> QualifiedIdentifier<'a> {
224 pub fn table_name(&self) -> &Identifier<'a> {
226 match self {
227 QualifiedIdentifier::Unqualified(id) | QualifiedIdentifier::Qualified(_, id) => id,
228 }
229 }
230 pub fn schema_name(&self) -> Option<&Identifier<'a>> {
232 match self {
233 QualifiedIdentifier::Unqualified(_) => None,
234 QualifiedIdentifier::Qualified(s, _) => Some(s),
235 }
236 }
237}
238
239pub trait SearchPath<'a>: Copy {
245 fn iter_schemas(self) -> impl Iterator<Item = &'a str>;
246}
247
248impl<'b> SearchPath<'b> for &[&'b str] {
249 fn iter_schemas(self) -> impl Iterator<Item = &'b str> {
250 self.iter().copied()
251 }
252}
253
254impl<'b> SearchPath<'b> for &[Identifier<'b>] {
255 fn iter_schemas(self) -> impl Iterator<Item = &'b str> {
256 self.iter().map(|id| id.value)
257 }
258}
259
260impl<'a> SearchPath<'a> for () {
261 fn iter_schemas(self) -> impl Iterator<Item = &'a str> {
262 core::iter::empty()
263 }
264}
265
266pub fn lookup_name_key<'a, 'b, T, S: SearchPath<'a>>(
269 map: &'b BTreeMap<QualifiedIdentifier<'a>, T>,
270 name: &QualifiedIdentifier<'a>,
271 search_path: S,
272) -> Option<(&'b QualifiedIdentifier<'a>, &'b T)> {
273 if let Some(kv) = map.get_key_value(name) {
274 return Some(kv);
275 }
276 if let QualifiedIdentifier::Unqualified(table) = name {
277 for schema in search_path.iter_schemas() {
278 let qualified =
279 QualifiedIdentifier::Qualified(Identifier::new(schema, 0..0), table.clone());
280 if let Some(kv) = map.get_key_value(&qualified) {
281 return Some(kv);
282 }
283 }
284 }
285 None
286}
287
288pub fn lookup_name<'a, 'b, T, S: SearchPath<'a>>(
296 map: &'b BTreeMap<QualifiedIdentifier<'a>, T>,
297 name: &QualifiedIdentifier<'a>,
298 search_path: S,
299) -> Option<&'b T> {
300 lookup_name_key(map, name, search_path).map(|(_, v)| v)
301}
302
303pub fn lookup_name_mut<'a, 'b, T, S: SearchPath<'a>>(
305 map: &'b mut BTreeMap<QualifiedIdentifier<'a>, T>,
306 name: &QualifiedIdentifier<'a>,
307 search_path: S,
308) -> Option<&'b mut T> {
309 if map.contains_key(name) {
310 return map.get_mut(name);
311 }
312 if let QualifiedIdentifier::Unqualified(table) = name {
313 for schema in search_path.iter_schemas() {
314 let qualified =
315 QualifiedIdentifier::Qualified(Identifier::new(schema, 0..0), table.clone());
316 if map.contains_key(&qualified) {
317 return map.get_mut(&qualified);
318 }
319 }
320 }
321 None
322}
323
324#[derive(Debug, Default)]
326pub struct Schemas<'a> {
327 pub schemas: BTreeMap<QualifiedIdentifier<'a>, Schema<'a>>,
332 pub schema_names: BTreeSet<Identifier<'a>>,
334 pub procedures: BTreeMap<QualifiedIdentifier<'a>, ProcedureDef<'a>>,
336 pub functions: BTreeMap<QualifiedIdentifier<'a>, FunctionDef<'a>>,
338 pub indices: BTreeMap<IndexKey<'a>, Span>,
340 pub types: BTreeMap<QualifiedIdentifier<'a>, TypeDef<'a>>,
342 pub sequences: BTreeMap<QualifiedIdentifier<'a>, ()>,
345}
346
347fn try_parse_body<'a>(
351 src: &'a str,
352 body_str: &qusql_parse::SString<'a>,
353 issues: &mut Issues<'a>,
354 options: &qusql_parse::ParseOptions,
355) -> Option<FunctionDefBody<'a>> {
356 let Cow::Borrowed(borrowed) = &body_str.value else {
357 return None;
358 };
359 let span_offset = borrowed.as_ptr() as usize - src.as_ptr() as usize;
360 let body_options = options.clone().function_body(true).span_offset(span_offset);
361 let statements = parse_statements(borrowed, issues, &body_options);
362 Some(FunctionDefBody {
363 statements,
364 src: borrowed,
365 })
366}
367
368pub(crate) fn range_subtype_to_type<'a>(sub: &qusql_parse::RangeSubtype) -> Type<'a> {
372 use qusql_parse::RangeSubtype;
373 match sub {
374 RangeSubtype::Int4 => Type::I32,
375 RangeSubtype::Int8 => Type::I64,
376 RangeSubtype::Num => BaseType::Float.into(),
377 RangeSubtype::Ts => BaseType::DateTime.into(),
378 RangeSubtype::Tstz => BaseType::TimeStamp.into(),
379 RangeSubtype::Date => BaseType::Date.into(),
380 }
381}
382
383fn type_kind_from_parse<'a, S: SearchPath<'a>>(
384 type_: qusql_parse::Type<'a>,
385 unsigned: bool,
386 is_sqlite: bool,
387 is_postgresql: bool,
388 types: Option<&BTreeMap<QualifiedIdentifier<'a>, TypeDef<'a>>>,
389 issues: &mut Issues<'a>,
390 search_path: S,
391) -> Type<'a> {
392 match type_ {
393 qusql_parse::Type::TinyInt(v) => {
394 if !unsigned && matches!(v, Some((1, _))) {
395 BaseType::Bool.into()
396 } else if unsigned {
397 Type::U8
398 } else {
399 Type::I8
400 }
401 }
402 qusql_parse::Type::SmallInt(_) => {
403 if unsigned {
404 Type::U16
405 } else {
406 Type::I16
407 }
408 }
409 qusql_parse::Type::MediumInt(_) => {
410 if unsigned {
411 Type::U24
412 } else {
413 Type::I24
414 }
415 }
416 qusql_parse::Type::Int(_) => {
417 if unsigned {
418 Type::U32
419 } else {
420 Type::I32
421 }
422 }
423 qusql_parse::Type::BigInt(_) => {
424 if unsigned {
425 Type::U64
426 } else {
427 Type::I64
428 }
429 }
430 qusql_parse::Type::Char(_) => BaseType::String.into(),
431 qusql_parse::Type::VarChar(_) => BaseType::String.into(),
432 qusql_parse::Type::TinyText(_) => BaseType::String.into(),
433 qusql_parse::Type::MediumText(_) => BaseType::String.into(),
434 qusql_parse::Type::Text(_) => BaseType::String.into(),
435 qusql_parse::Type::LongText(_) => BaseType::String.into(),
436 qusql_parse::Type::Enum(e) => {
437 Type::Enum(Arc::new(e.into_iter().map(|s| s.value).collect()))
438 }
439 qusql_parse::Type::Set(s) => Type::Set(Arc::new(s.into_iter().map(|s| s.value).collect())),
440 qusql_parse::Type::Float(_) => Type::F32,
441 qusql_parse::Type::Double(_) => Type::F64,
442 qusql_parse::Type::DateTime(_) => BaseType::DateTime.into(),
443 qusql_parse::Type::Timestamp(_) => BaseType::TimeStamp.into(),
444 qusql_parse::Type::Time(_) => BaseType::Time.into(),
445 qusql_parse::Type::TinyBlob(_) => BaseType::Bytes.into(),
446 qusql_parse::Type::MediumBlob(_) => BaseType::Bytes.into(),
447 qusql_parse::Type::Date => BaseType::Date.into(),
448 qusql_parse::Type::Blob(_) => BaseType::Bytes.into(),
449 qusql_parse::Type::LongBlob(_) => BaseType::Bytes.into(),
450 qusql_parse::Type::VarBinary(_) => BaseType::Bytes.into(),
451 qusql_parse::Type::Binary(_) => BaseType::Bytes.into(),
452 qusql_parse::Type::Boolean => BaseType::Bool.into(),
453 qusql_parse::Type::Integer(_) => {
454 if is_sqlite {
455 BaseType::Integer.into()
456 } else {
457 Type::I32
458 }
459 }
460 qusql_parse::Type::Float8 => BaseType::Float.into(),
461 qusql_parse::Type::Numeric(ref v) => {
462 let span = v.as_ref().map(|(_, _, s)| s.clone()).unwrap_or(0..0);
463 issues.err("NUMERIC type is not yet supported", &span);
464 BaseType::Float.into()
465 }
466 qusql_parse::Type::Decimal(ref v) => {
467 let span = v.as_ref().map(|(_, _, s)| s.clone()).unwrap_or(0..0);
468 issues.err("DECIMAL type is not yet supported", &span);
469 BaseType::Float.into()
470 }
471 qusql_parse::Type::Timestamptz => BaseType::TimeStamp.into(),
472 qusql_parse::Type::Json | qusql_parse::Type::Jsonb => {
473 if is_postgresql {
474 Type::JSON
477 } else {
478 BaseType::String.into()
479 }
480 }
481 qusql_parse::Type::Bit(_, _) => BaseType::Bytes.into(),
482 qusql_parse::Type::VarBit(_) => BaseType::Bytes.into(),
483 qusql_parse::Type::Bytea => BaseType::Bytes.into(),
484 qusql_parse::Type::Named(qname) => {
485 if let Some(types) = types {
487 let key = match qname.prefix.as_slice() {
488 [] => QualifiedIdentifier::Unqualified(qname.identifier.clone()),
489 [(schema, _)] if is_postgresql => {
490 QualifiedIdentifier::Qualified(schema.clone(), qname.identifier.clone())
491 }
492 _ => return BaseType::String.into(),
493 };
494 match lookup_name(types, &key, search_path) {
495 Some(TypeDef::Enum { values, .. }) => Type::Enum(values.clone()),
496 _ => BaseType::String.into(),
497 }
498 } else {
499 BaseType::String.into()
500 }
501 }
502 qusql_parse::Type::Inet4 => BaseType::String.into(),
503 qusql_parse::Type::Inet6 => BaseType::String.into(),
504 qusql_parse::Type::InetAddr => BaseType::String.into(),
505 qusql_parse::Type::Cidr => BaseType::String.into(),
506 qusql_parse::Type::Macaddr => BaseType::String.into(),
507 qusql_parse::Type::Macaddr8 => BaseType::String.into(),
508 qusql_parse::Type::Array(inner, _) => Type::Array(Box::new(type_kind_from_parse(
509 *inner,
510 unsigned,
511 is_sqlite,
512 is_postgresql,
513 types,
514 issues,
515 search_path,
516 ))),
517 qusql_parse::Type::Table(ref span, _) => {
518 issues.err("TABLE type is not yet supported", span);
519 BaseType::String.into()
520 }
521 qusql_parse::Type::Serial => Type::I32,
522 qusql_parse::Type::SmallSerial => Type::I16,
523 qusql_parse::Type::BigSerial => Type::I64,
524 qusql_parse::Type::Money => BaseType::Float.into(),
525 qusql_parse::Type::Timetz(_) => BaseType::Time.into(),
526 qusql_parse::Type::Interval(_) => BaseType::TimeInterval.into(),
527 qusql_parse::Type::TsQuery => BaseType::String.into(),
528 qusql_parse::Type::TsVector => BaseType::String.into(),
529 qusql_parse::Type::Uuid => BaseType::Uuid.into(),
530 qusql_parse::Type::Xml => BaseType::String.into(),
531 qusql_parse::Type::Range(sub) => Type::Range(Box::new(range_subtype_to_type(&sub))),
532 qusql_parse::Type::MultiRange(sub) => {
533 Type::MultiRange(Box::new(range_subtype_to_type(&sub)))
534 }
535 qusql_parse::Type::Point
536 | qusql_parse::Type::Line
537 | qusql_parse::Type::Lseg
538 | qusql_parse::Type::Box
539 | qusql_parse::Type::Path
540 | qusql_parse::Type::Polygon
541 | qusql_parse::Type::Circle => Type::Geometry,
542 }
543}
544
545pub(crate) fn parse_column<'a, S: SearchPath<'a>>(
546 data_type: DataType<'a>,
547 identifier: Identifier<'a>,
548 _issues: &mut Issues<'a>,
549 options: Option<&TypeOptions>,
550 types: Option<&BTreeMap<QualifiedIdentifier<'a>, TypeDef<'a>>>,
551 search_path: S,
552) -> Column<'a> {
553 let mut not_null = false;
554 let mut unsigned = false;
555 let mut auto_increment = false;
556 let mut default = false;
557 let mut as_ = None;
558 let mut generated = false;
559 let mut primary_key = false;
560 let is_sqlite = options
561 .map(|v| v.parse_options.get_dialect().is_sqlite())
562 .unwrap_or_default();
563 let is_postgresql = options
564 .map(|v| v.parse_options.get_dialect().is_postgresql())
565 .unwrap_or_default();
566 for p in data_type.properties {
567 match p {
568 DataTypeProperty::Signed(_) => unsigned = false,
569 DataTypeProperty::Unsigned(_) => unsigned = true,
570 DataTypeProperty::Null(_) => not_null = false,
571 DataTypeProperty::NotNull(_) => not_null = true,
572 DataTypeProperty::AutoIncrement(_) => auto_increment = true,
573 DataTypeProperty::As((_, e)) => as_ = Some(e),
574 DataTypeProperty::Default(_) => default = true,
575 DataTypeProperty::GeneratedAlways(_) => generated = true,
576 DataTypeProperty::GeneratedAlwaysAsExpr { .. } => generated = true,
577 DataTypeProperty::PrimaryKey(_) => primary_key = true,
578 _ => {}
579 }
580 }
581 if is_sqlite && primary_key && matches!(data_type.type_, qusql_parse::Type::Integer(_)) {
583 auto_increment = true;
584 }
585 if primary_key {
587 not_null = true;
588 }
589 let type_ = type_kind_from_parse(
590 data_type.type_,
591 unsigned,
592 is_sqlite,
593 is_postgresql,
594 types,
595 _issues,
596 search_path,
597 );
598 Column {
599 identifier,
600 type_: FullType {
601 t: type_,
602 not_null,
603 list_hack: false,
604 },
605 auto_increment,
606 as_,
607 default,
608 generated,
609 }
610}
611
612#[derive(Clone, Debug, PartialEq)]
614enum SqlValue<'a> {
615 Null,
616 Bool(bool),
617 Integer(i64),
618 SourceText(&'a str),
620 OwnedText(alloc::string::String),
622}
623
624impl<'a> SqlValue<'a> {
625 fn as_source_text(&self) -> Option<&'a str> {
626 if let SqlValue::SourceText(s) = self {
627 Some(s)
628 } else {
629 None
630 }
631 }
632
633 fn is_truthy(&self) -> bool {
634 match self {
635 SqlValue::Bool(b) => *b,
636 SqlValue::Integer(i) => *i != 0,
637 SqlValue::Null => false,
638 SqlValue::SourceText(_) | SqlValue::OwnedText(_) => true,
639 }
640 }
641
642 fn sql_eq(&self, other: &SqlValue<'a>) -> Option<bool> {
643 match (self, other) {
644 (SqlValue::Null, _) | (_, SqlValue::Null) => None,
645 (SqlValue::Bool(a), SqlValue::Bool(b)) => Some(a == b),
646 (SqlValue::Integer(a), SqlValue::Integer(b)) => Some(a == b),
647 (SqlValue::SourceText(a), SqlValue::SourceText(b)) => Some(a == b),
648 (SqlValue::SourceText(a), SqlValue::OwnedText(b)) => Some(*a == b.as_str()),
649 (SqlValue::OwnedText(a), SqlValue::SourceText(b)) => Some(a.as_str() == *b),
650 (SqlValue::OwnedText(a), SqlValue::OwnedText(b)) => Some(a == b),
651 _ => None,
652 }
653 }
654
655 fn sql_lte(&self, other: &SqlValue<'a>) -> Option<bool> {
656 match (self, other) {
657 (SqlValue::Null, _) | (_, SqlValue::Null) => None,
658 (SqlValue::Integer(a), SqlValue::Integer(b)) => Some(a <= b),
659 _ => None,
660 }
661 }
662}
663
664type Row<'a> = Rc<Vec<(&'a str, SqlValue<'a>)>>;
666
667struct SchemaCtx<'a, 'b> {
670 schemas: &'b mut Schemas<'a>,
671 issues: &'b mut Issues<'a>,
672 src: &'a str,
674 options: &'b TypeOptions,
675 bindings: BTreeMap<&'a str, SqlValue<'a>>,
678 rows: BTreeMap<QualifiedIdentifier<'a>, Vec<Row<'a>>>,
682 current_table_rows: Vec<Row<'a>>,
685 current_row: Option<Row<'a>>,
688 return_value: Option<SqlValue<'a>>,
691 search_path: Vec<Identifier<'a>>,
695}
696
697impl<'a, 'b> SchemaCtx<'a, 'b> {
698 fn new(
699 schemas: &'b mut Schemas<'a>,
700 issues: &'b mut Issues<'a>,
701 src: &'a str,
702 options: &'b TypeOptions,
703 ) -> Self {
704 Self {
705 schemas,
706 issues,
707 src,
708 options,
709 bindings: Default::default(),
710 rows: Default::default(),
711 current_table_rows: Default::default(),
712 current_row: Default::default(),
713 return_value: None,
714 search_path: if options.parse_options.get_dialect().is_postgresql() {
715 alloc::vec![Identifier::new("public", 0..0)]
716 } else {
717 Vec::new()
718 },
719 }
720 }
721
722 fn make_table_key(
726 &self,
727 schema: Option<Identifier<'a>>,
728 table: Identifier<'a>,
729 ) -> QualifiedIdentifier<'a> {
730 if self.options.parse_options.get_dialect().is_postgresql() {
731 QualifiedIdentifier::Qualified(
732 schema.unwrap_or_else(|| Identifier::new("public", 0..0)),
733 table,
734 )
735 } else {
736 QualifiedIdentifier::Unqualified(table)
737 }
738 }
739
740 fn parse_qname(&mut self, qname: &QualifiedName<'a>) -> Option<QualifiedIdentifier<'a>> {
743 let is_pg = self.options.parse_options.get_dialect().is_postgresql();
744 match qname.prefix.as_slice() {
745 [] => Some(self.make_table_key(None, qname.identifier.clone())),
746 [(schema, _)] if is_pg => {
747 Some(self.make_table_key(Some(schema.clone()), qname.identifier.clone()))
748 }
749 _ => {
750 let msg = if is_pg {
751 "Expected at most schema.table qualified name"
752 } else {
753 "Schema-qualified names are not supported in MySQL"
754 };
755 self.issues.err(msg, &qname.prefix.opt_span().unwrap());
756 None
757 }
758 }
759 }
760
761 fn parse_qname_for_lookup(
765 &mut self,
766 qname: &QualifiedName<'a>,
767 ) -> Option<QualifiedIdentifier<'a>> {
768 let is_pg = self.options.parse_options.get_dialect().is_postgresql();
769 match qname.prefix.as_slice() {
770 [] => Some(QualifiedIdentifier::Unqualified(qname.identifier.clone())),
771 [(schema, _)] if is_pg => Some(QualifiedIdentifier::Qualified(
772 schema.clone(),
773 qname.identifier.clone(),
774 )),
775 _ => {
776 let msg = if is_pg {
777 "Expected at most schema.table qualified name"
778 } else {
779 "Schema-qualified names are not supported in MySQL"
780 };
781 self.issues.err(msg, &qname.prefix.opt_span().unwrap());
782 None
783 }
784 }
785 }
786
787 fn process_top_level_statements(&mut self, statements: Vec<qusql_parse::Statement<'a>>) {
790 for statement in statements {
791 let _ = self.process_statement(statement);
792 }
793 }
794
795 fn process_statements(
798 &mut self,
799 statements: Vec<qusql_parse::Statement<'a>>,
800 ) -> Result<(), ()> {
801 for statement in statements {
802 self.process_statement(statement)?;
803 }
804 Ok(())
805 }
806
807 fn process_statement(&mut self, statement: qusql_parse::Statement<'a>) -> Result<(), ()> {
808 match statement {
809 qusql_parse::Statement::CreateTable(t) => {
810 self.process_create_table(*t);
811 Ok(())
812 }
813 qusql_parse::Statement::CreateView(v) => {
814 self.process_create_view(*v);
815 Ok(())
816 }
817 qusql_parse::Statement::CreateFunction(f) => {
818 self.process_create_function(*f);
819 Ok(())
820 }
821 qusql_parse::Statement::CreateProcedure(p) => {
822 self.process_create_procedure(*p);
823 Ok(())
824 }
825 qusql_parse::Statement::CreateIndex(ci) => {
826 self.process_create_index(*ci);
827 Ok(())
828 }
829 qusql_parse::Statement::CreateTrigger(_) => Ok(()),
830 qusql_parse::Statement::CreateTypeEnum(s) => {
831 self.process_create_type_enum(*s);
832 Ok(())
833 }
834 qusql_parse::Statement::AlterTable(a) => {
835 self.process_alter_table(*a);
836 Ok(())
837 }
838 qusql_parse::Statement::AlterSequence(a) => {
839 self.process_alter_sequence(*a);
840 Ok(())
841 }
842 qusql_parse::Statement::DropTable(t) => {
843 self.process_drop_table(*t);
844 Ok(())
845 }
846 qusql_parse::Statement::DropView(v) => {
847 self.process_drop_view(*v);
848 Ok(())
849 }
850 qusql_parse::Statement::DropFunction(f) => {
851 self.process_drop_function(*f);
852 Ok(())
853 }
854 qusql_parse::Statement::DropProcedure(p) => {
855 self.process_drop_procedure(*p);
856 Ok(())
857 }
858 qusql_parse::Statement::DropIndex(ci) => {
859 self.process_drop_index(*ci);
860 Ok(())
861 }
862 qusql_parse::Statement::CreateSchema(s) => {
863 self.process_create_schema(*s);
864 Ok(())
865 }
866 qusql_parse::Statement::DropDatabase(s) => {
867 self.issues.err("not implemented", &s);
868 Err(())
869 }
870 qusql_parse::Statement::DropSchema(s) => {
871 if self.options.parse_options.get_dialect().is_postgresql() {
872 self.process_drop_schema(*s);
873 } else {
874 self.issues.err("not implemented", &s);
875 }
876 Ok(())
877 }
878 qusql_parse::Statement::DropServer(s) => {
879 self.issues.err("not implemented", &s);
880 Err(())
881 }
882 qusql_parse::Statement::DropTrigger(_) => Ok(()),
883 qusql_parse::Statement::DropType(s) => {
884 self.process_drop_type(*s);
885 Ok(())
886 }
887 qusql_parse::Statement::Do(d) => self.process_do(*d),
889 qusql_parse::Statement::Block(b) => self.process_statements(b.statements),
890 qusql_parse::Statement::If(i) => self.process_if(*i),
891 qusql_parse::Statement::Select(s) => self.process_select(*s),
893 qusql_parse::Statement::InsertReplace(i) => self.process_insert(*i),
895 qusql_parse::Statement::Commit(_) => Ok(()),
897 qusql_parse::Statement::Begin(_) => Ok(()),
898 qusql_parse::Statement::Grant(_) => Ok(()),
900 qusql_parse::Statement::CommentOn(_) => Ok(()),
901 qusql_parse::Statement::Analyze(_) => Ok(()),
902 qusql_parse::Statement::CreateSequence(s) => {
903 self.process_create_sequence(*s);
904 Ok(())
905 }
906 qusql_parse::Statement::DropSequence(s) => {
907 self.process_drop_sequence(*s);
908 Ok(())
909 }
910 qusql_parse::Statement::Set(s) => {
912 self.process_set(*s);
913 Ok(())
914 }
915 qusql_parse::Statement::Assign(a) => {
917 for se in a.value.select_exprs {
918 self.process_expression(se.expr)?;
919 }
920 Ok(())
921 }
922 qusql_parse::Statement::Perform(p) => self.process_expression(p.expr),
923 qusql_parse::Statement::DeclareVariable(d) => {
924 if let Some((_, select)) = d.default {
926 self.eval_condition(&select)?;
927 }
928 Ok(())
929 }
930 qusql_parse::Statement::DeclareHandler(_) => Ok(()),
932 qusql_parse::Statement::ExecuteFunction(s) => {
933 self.issues.err("not implemented", &s);
934 Err(())
935 }
936 qusql_parse::Statement::Raise(r) => {
938 if matches!(r.level, Some(qusql_parse::RaiseLevel::Exception(_))) {
939 Err(())
940 } else {
941 Ok(())
942 }
943 }
944 qusql_parse::Statement::Return(r) => {
945 self.return_value = self.eval_expr(&r.expr).ok();
946 Err(())
947 }
948 qusql_parse::Statement::PlpgsqlExecute(e) => self.process_plpgsql_execute(*e),
949 qusql_parse::Statement::Update(u) => self.process_update(*u),
950 qusql_parse::Statement::Delete(d) => self.process_delete(*d),
951 qusql_parse::Statement::AlterType(a) => {
952 self.process_alter_type(*a);
953 Ok(())
954 }
955 qusql_parse::Statement::TruncateTable(t) => {
956 self.process_truncate_table(*t);
957 Ok(())
958 }
959 qusql_parse::Statement::RenameTable(r) => {
960 self.process_rename_table(*r);
961 Ok(())
962 }
963 qusql_parse::Statement::Call(c) => self.process_call(*c),
964 s => {
965 self.issues.err(
966 alloc::format!("Unsupported statement {s:?} in schema definition"),
967 &s,
968 );
969 Err(())
970 }
971 }
972 }
973
974 fn process_create_sequence(&mut self, s: qusql_parse::CreateSequence<'a>) {
975 let Some(key) = self.parse_qname(&s.name) else {
976 return;
977 };
978 if s.if_not_exists.is_some()
979 && lookup_name(&self.schemas.sequences, &key, self.search_path.as_slice()).is_some()
980 {
981 return;
982 }
983 self.schemas.sequences.insert(key, ());
984 }
985
986 fn process_drop_sequence(&mut self, s: qusql_parse::DropSequence<'a>) {
987 for name in s.sequences {
988 let Some(key) = self.parse_qname(&name) else {
989 continue;
990 };
991 if s.if_exists.is_none()
992 && lookup_name(&self.schemas.sequences, &key, self.search_path.as_slice()).is_none()
993 {
994 self.issues.err(
995 alloc::format!("Unknown sequence `{}`", name.identifier.value),
996 &name.identifier,
997 );
998 }
999 self.schemas.sequences.remove(&key);
1000 }
1001 }
1002
1003 fn process_create_table(&mut self, t: qusql_parse::CreateTable<'a>) {
1004 let mut replace = false;
1005 let Some(key) = self.parse_qname(&t.identifier) else {
1006 return;
1007 };
1008 let id = key.table_name().clone();
1009 let identifier_span = id.span.clone();
1010 let mut schema = Schema {
1011 view: false,
1012 identifier_span,
1013 columns: Default::default(),
1014 };
1015 for o in t.create_options {
1016 match o {
1017 qusql_parse::CreateOption::OrReplace(_) => replace = true,
1018 qusql_parse::CreateOption::Temporary { temporary_span, .. } => {
1019 self.issues.err("Not supported", &temporary_span);
1020 }
1021 qusql_parse::CreateOption::Materialized(s) => {
1022 self.issues.err("Not supported", &s);
1023 }
1024 qusql_parse::CreateOption::Concurrently(s) => {
1025 self.issues.err("Not supported", &s);
1026 }
1027 qusql_parse::CreateOption::Unique(s) => {
1028 self.issues.err("Not supported", &s);
1029 }
1030 _ => {}
1031 }
1032 }
1033 for d in t.create_definitions {
1034 match d {
1035 qusql_parse::CreateDefinition::ColumnDefinition {
1036 identifier,
1037 data_type,
1038 } => {
1039 let column = parse_column(
1040 data_type,
1041 identifier.clone(),
1042 self.issues,
1043 Some(self.options),
1044 Some(&self.schemas.types),
1045 self.search_path.as_slice(),
1046 );
1047 if let Some(oc) = schema.get_column(column.identifier.value) {
1048 self.issues
1049 .err("Column already defined", &identifier)
1050 .frag("Defined here", &oc.identifier);
1051 } else {
1052 schema.columns.push(column);
1053 }
1054 }
1055 qusql_parse::CreateDefinition::IndexDefinition {
1056 index_type,
1057 index_name,
1058 cols,
1059 ..
1060 } => {
1061 for col in &cols {
1063 if let qusql_parse::IndexColExpr::Column(cname) = &col.expr
1064 && schema.get_column(cname.value).is_none()
1065 {
1066 self.issues
1067 .err("No such column in table", col)
1068 .frag("Table defined here", &schema.identifier_span);
1069 }
1070 }
1071 if matches!(index_type, qusql_parse::IndexType::Primary(_)) {
1073 for col in &cols {
1074 if let qusql_parse::IndexColExpr::Column(cname) = &col.expr
1075 && let Some(c) = schema.get_column_mut(cname.value)
1076 {
1077 c.type_.not_null = true;
1078 }
1079 }
1080 }
1081 if let Some(name) = index_name {
1083 let ident = if self.options.parse_options.get_dialect().is_postgresql() {
1084 IndexKey {
1085 schema: key.schema_name().cloned(),
1086 table: None,
1087 index: name.clone(),
1088 }
1089 } else {
1090 IndexKey {
1091 schema: None,
1092 table: Some(id.clone()),
1093 index: name.clone(),
1094 }
1095 };
1096 let span = name.span();
1097 if let Some(old) = self.schemas.indices.insert(ident, span) {
1098 self.issues
1099 .err("Multiple indices with the same identifier", &name)
1100 .frag("Already defined here", &old);
1101 }
1102 }
1103 }
1104 qusql_parse::CreateDefinition::ForeignKeyDefinition { .. } => {}
1105 qusql_parse::CreateDefinition::CheckConstraintDefinition { .. } => {}
1106 qusql_parse::CreateDefinition::LikeTable { source_table, .. } => {
1107 let source_key = match self.parse_qname_for_lookup(&source_table) {
1108 Some(k) => k,
1109 None => continue,
1110 };
1111 if let Some(src) = lookup_name(
1112 &self.schemas.schemas,
1113 &source_key,
1114 self.search_path.as_slice(),
1115 ) {
1116 let cols: Vec<Column<'a>> = src.columns.to_vec();
1117 for col in cols {
1118 if schema.get_column(col.identifier.value).is_none() {
1119 schema.columns.push(col);
1120 }
1121 }
1122 } else {
1123 self.issues.err("Table not found", &source_table);
1124 }
1125 }
1126 }
1127 }
1128 match self.schemas.schemas.entry(key.clone()) {
1129 alloc::collections::btree_map::Entry::Occupied(mut e) => {
1130 if replace {
1131 e.insert(schema);
1132 } else if t.if_not_exists.is_none() {
1133 self.issues
1134 .err("Table already defined", &t.identifier)
1135 .frag("Defined here", &e.get().identifier_span);
1136 }
1137 }
1138 alloc::collections::btree_map::Entry::Vacant(e) => {
1139 e.insert(schema);
1140 }
1141 }
1142 }
1143
1144 fn process_create_view(&mut self, v: qusql_parse::CreateView<'a>) {
1145 let mut replace = false;
1146 let mut schema = Schema {
1147 view: true,
1148 identifier_span: v.name.span(),
1149 columns: Default::default(),
1150 };
1151 for o in v.create_options {
1152 match o {
1153 qusql_parse::CreateOption::OrReplace(_) => replace = true,
1154 qusql_parse::CreateOption::Temporary { temporary_span, .. } => {
1155 self.issues.err("Not supported", &temporary_span);
1156 }
1157 qusql_parse::CreateOption::Materialized(s) => {
1158 self.issues.err("Not supported", &s);
1159 }
1160 qusql_parse::CreateOption::Concurrently(s) => {
1161 self.issues.err("Not supported", &s);
1162 }
1163 qusql_parse::CreateOption::Unique(s) => {
1164 self.issues.err("Not supported", &s);
1165 }
1166 _ => {}
1167 }
1168 }
1169 {
1170 let mut typer: crate::typer::Typer<'a, '_> = crate::typer::Typer {
1171 schemas: self.schemas,
1172 issues: self.issues,
1173 reference_types: Vec::new(),
1174 outer_reference_types: Vec::new(),
1175 arg_types: Default::default(),
1176 options: self.options,
1177 with_schemas: Default::default(),
1178 };
1179 let t = type_statement::type_statement(&mut typer, &v.select);
1180 let s = if let type_statement::InnerStatementType::Select(s) = t {
1181 s
1182 } else {
1183 self.issues.err("Not supported", &v.select.span());
1184 return;
1185 };
1186 for column in s.columns {
1187 let Some(name) = column.name else {
1188 self.issues.err(
1189 "View column has no name; add an alias with AS",
1190 &v.select.span(),
1191 );
1192 continue;
1193 };
1194 schema.columns.push(Column {
1195 identifier: name,
1196 type_: column.type_,
1197 auto_increment: false,
1198 default: false,
1199 as_: None,
1200 generated: false,
1201 });
1202 }
1203 }
1204 let view_key = match self.parse_qname(&v.name) {
1205 Some(k) => k,
1206 None => return,
1207 };
1208 match self.schemas.schemas.entry(view_key) {
1209 alloc::collections::btree_map::Entry::Occupied(mut e) => {
1210 if replace {
1211 e.insert(schema);
1212 } else if v.if_not_exists.is_none() {
1213 self.issues
1214 .err("View already defined", &v.name)
1215 .frag("Defined here", &e.get().identifier_span);
1216 }
1217 }
1218 alloc::collections::btree_map::Entry::Vacant(e) => {
1219 e.insert(schema);
1220 }
1221 }
1222 }
1223
1224 fn process_create_function(&mut self, f: qusql_parse::CreateFunction<'a>) {
1225 let mut replace = false;
1226 for o in &f.create_options {
1227 if matches!(o, qusql_parse::CreateOption::OrReplace(_)) {
1228 replace = true;
1229 }
1230 }
1231 let body = f
1232 .body
1233 .as_ref()
1234 .and_then(|b| b.strings.first())
1235 .and_then(|s| try_parse_body(self.src, s, self.issues, &self.options.parse_options));
1236 let Some(key) = self.parse_qname(&f.name) else {
1237 return;
1238 };
1239 let def = FunctionDef {
1240 name: f.name.identifier.clone(),
1241 params: f.params,
1242 return_type: f.return_type,
1243 span: f.create_span.join_span(&f.function_span),
1244 body,
1245 };
1246 match self.schemas.functions.entry(key) {
1247 alloc::collections::btree_map::Entry::Occupied(mut e) => {
1248 if replace {
1249 e.insert(def);
1250 } else if f.if_not_exists.is_none() {
1251 self.issues
1252 .err("Function already defined", &f.name.identifier)
1253 .frag("Defined here", &e.get().span);
1254 }
1255 }
1256 alloc::collections::btree_map::Entry::Vacant(e) => {
1257 e.insert(def);
1258 }
1259 }
1260 }
1261
1262 fn process_create_procedure(&mut self, p: qusql_parse::CreateProcedure<'a>) {
1263 let mut replace = false;
1264 for o in &p.create_options {
1265 if matches!(o, qusql_parse::CreateOption::OrReplace(_)) {
1266 replace = true;
1267 }
1268 }
1269 let key = self.make_table_key(None, p.name.clone());
1270 let body = p.body.map(|stmt| match stmt {
1271 qusql_parse::Statement::Block(b) => b.statements,
1272 other => alloc::vec![other],
1273 });
1274 let def = ProcedureDef {
1275 name: p.name.clone(),
1276 params: p.params,
1277 span: p.create_span.join_span(&p.procedure_span),
1278 body,
1279 };
1280 match self.schemas.procedures.entry(key) {
1281 alloc::collections::btree_map::Entry::Occupied(mut e) => {
1282 if replace {
1283 e.insert(def);
1284 } else if p.if_not_exists.is_none() {
1285 self.issues
1286 .err("Procedure already defined", &p.name)
1287 .frag("Defined here", &e.get().span);
1288 }
1289 }
1290 alloc::collections::btree_map::Entry::Vacant(e) => {
1291 e.insert(def);
1292 }
1293 }
1294 }
1295
1296 fn process_call(&mut self, c: qusql_parse::Call<'a>) -> Result<(), ()> {
1297 let Some(key) = self.parse_qname_for_lookup(&c.name) else {
1298 return Ok(());
1299 };
1300 let body = lookup_name(&self.schemas.procedures, &key, self.search_path.as_slice())
1302 .and_then(|p| p.body.clone());
1303 let Some(statements) = body else {
1304 return Ok(());
1306 };
1307 let _ = self.process_statements(statements);
1309 Ok(())
1310 }
1311
1312 fn process_set(&mut self, s: qusql_parse::Set<'a>) {
1313 if !self.options.parse_options.get_dialect().is_postgresql() {
1314 return;
1315 }
1316 for (var, exprs) in s.values {
1317 let qusql_parse::SetVariable::Named(qname) = var else {
1318 continue;
1319 };
1320 if !qname.identifier.value.eq_ignore_ascii_case("search_path") {
1322 continue;
1323 }
1324 let mut new_path: Vec<Identifier<'a>> = Vec::new();
1326 for expr in exprs {
1327 match expr {
1328 Expression::Identifier(id) => {
1329 if let Some(part) = id.parts.last()
1330 && let qusql_parse::IdentifierPart::Name(name) = part
1331 {
1332 new_path.push(name.clone());
1333 }
1334 }
1335 Expression::String(s) => {
1336 if let alloc::borrow::Cow::Borrowed(b) = &s.value {
1338 new_path.push(Identifier::new(b, 0..0));
1339 } else {
1340 }
1342 }
1343 _ => {}
1344 }
1345 }
1346 self.search_path = new_path;
1347 }
1348 }
1349
1350 fn process_create_index(&mut self, ci: qusql_parse::CreateIndex<'a>) {
1351 let Some(lookup_key) = self.parse_qname_for_lookup(&ci.table_name) else {
1352 return;
1353 };
1354 let t = lookup_key.table_name().clone();
1355 let resolved_schema = match lookup_name_key(
1357 &self.schemas.schemas,
1358 &lookup_key,
1359 self.search_path.as_slice(),
1360 ) {
1361 Some((resolved_key, table)) => {
1362 let schema = resolved_key.schema_name().cloned();
1363 for col in &ci.column_names {
1364 if let qusql_parse::IndexColExpr::Column(name) = &col.expr
1365 && table.get_column(name.value).is_none()
1366 {
1367 self.issues
1368 .err("No such column in table", col)
1369 .frag("Table defined here", &table.identifier_span);
1370 }
1371 }
1372 schema
1373 }
1374 None => {
1375 self.issues.err("No such table", &ci.table_name);
1376 None
1377 }
1378 };
1379 let index_name = match &ci.index_name {
1380 Some(name) => name.clone(),
1381 None => return,
1382 };
1383 let ident = if self.options.parse_options.get_dialect().is_postgresql() {
1384 IndexKey {
1385 schema: resolved_schema,
1386 table: None,
1387 index: index_name.clone(),
1388 }
1389 } else {
1390 IndexKey {
1391 schema: None,
1392 table: Some(t.clone()),
1393 index: index_name.clone(),
1394 }
1395 };
1396 if let Some(old) = self.schemas.indices.insert(ident, ci.span())
1397 && ci.if_not_exists.is_none()
1398 {
1399 self.issues
1400 .err("Multiple indices with the same identifier", &ci)
1401 .frag("Already defined here", &old);
1402 }
1403 }
1404
1405 fn process_create_type_enum(&mut self, s: qusql_parse::CreateTypeEnum<'a>) {
1406 let Some(key) = self.parse_qname(&s.name) else {
1407 return;
1408 };
1409 let mut replace = false;
1410 for o in &s.create_options {
1411 if matches!(o, qusql_parse::CreateOption::OrReplace(_)) {
1412 replace = true;
1413 }
1414 }
1415 let values = Arc::new(s.values.into_iter().map(|v| v.value).collect());
1416 let typedef = TypeDef::Enum {
1417 values,
1418 span: s.as_enum_span,
1419 };
1420 match self.schemas.types.entry(key) {
1421 alloc::collections::btree_map::Entry::Occupied(mut e) => {
1422 if replace {
1423 e.insert(typedef);
1424 }
1425 }
1427 alloc::collections::btree_map::Entry::Vacant(e) => {
1428 e.insert(typedef);
1429 }
1430 }
1431 }
1432
1433 fn process_drop_type(&mut self, s: qusql_parse::DropType<'a>) {
1434 let if_exists = s.if_exists;
1435 for name in s.names {
1436 let Some(key) = self.parse_qname(&name) else {
1437 continue;
1438 };
1439 if self.schemas.types.remove(&key).is_none() && if_exists.is_none() {
1440 self.issues.err("Type not found", &name);
1441 }
1442 }
1443 }
1444
1445 fn process_alter_table(&mut self, a: qusql_parse::AlterTable<'a>) {
1446 let key = match self.parse_qname(&a.table) {
1447 Some(k) => k,
1448 None => return,
1449 };
1450 let e = match self.schemas.schemas.entry(key) {
1451 alloc::collections::btree_map::Entry::Occupied(e) => {
1452 let e = e.into_mut();
1453 if e.view {
1454 self.issues.err("Cannot alter view", &a.table);
1455 return;
1456 }
1457 e
1458 }
1459 alloc::collections::btree_map::Entry::Vacant(_) => {
1460 if a.if_exists.is_none() {
1461 self.issues.err("Table not found", &a.table);
1462 }
1463 return;
1464 }
1465 };
1466 for s in a.alter_specifications {
1467 process_alter_specification(
1468 s,
1469 e,
1470 &a.table,
1471 self.issues,
1472 &mut self.schemas.indices,
1473 self.options,
1474 &self.schemas.types,
1475 self.search_path.as_slice(),
1476 );
1477 }
1478 }
1479
1480 fn process_alter_sequence(&mut self, a: qusql_parse::AlterSequence<'a>) {
1481 let lookup_key = match self.parse_qname_for_lookup(&a.name) {
1486 Some(k) => k,
1487 None => return,
1488 };
1489 let key = match lookup_name_key(
1490 &self.schemas.sequences,
1491 &lookup_key,
1492 self.search_path.as_slice(),
1493 ) {
1494 Some((resolved_key, _)) => resolved_key.clone(),
1495 None => {
1496 if a.if_exists.is_none() {
1497 self.issues.err(
1498 alloc::format!("Unknown sequence `{}`", a.name.identifier.value),
1499 &a.name,
1500 );
1501 }
1502 return;
1503 }
1504 };
1505 match a.action {
1506 qusql_parse::AlterSequenceAction::RenameTo { new_name, .. } => {
1507 let new_key = self.make_table_key(key.schema_name().cloned(), new_name);
1508 if self.schemas.sequences.contains_key(&new_key) {
1509 self.issues
1510 .err("Sequence already exists", new_key.table_name());
1511 return;
1512 }
1513 if let Some(entry) = self.schemas.sequences.remove(&key) {
1514 self.schemas.sequences.insert(new_key, entry);
1515 }
1516 }
1517 qusql_parse::AlterSequenceAction::SetSchema { new_schema, .. } => {
1518 let new_key = self.make_table_key(Some(new_schema), key.table_name().clone());
1519 if new_key != key && self.schemas.sequences.contains_key(&new_key) {
1520 self.issues
1521 .err("Sequence already exists in target schema", &a.name);
1522 return;
1523 }
1524 if let Some(entry) = self.schemas.sequences.remove(&key) {
1525 self.schemas.sequences.insert(new_key, entry);
1526 }
1527 }
1528 qusql_parse::AlterSequenceAction::OwnerTo { .. }
1530 | qusql_parse::AlterSequenceAction::Options(_) => {}
1531 }
1532 }
1533}
1534
1535#[allow(clippy::too_many_lines)]
1536#[allow(clippy::too_many_arguments)]
1537fn process_alter_specification<'a, S: SearchPath<'a>>(
1538 s: qusql_parse::AlterSpecification<'a>,
1539 e: &mut Schema<'a>,
1540 table_ref: &qusql_parse::QualifiedName<'a>,
1541 issues: &mut Issues<'a>,
1542 indices: &mut alloc::collections::BTreeMap<IndexKey<'a>, Span>,
1543 options: &TypeOptions,
1544 types: &BTreeMap<QualifiedIdentifier<'a>, TypeDef<'a>>,
1545 search_path: S,
1546) {
1547 match s {
1548 qusql_parse::AlterSpecification::AddIndex(AddIndex {
1549 if_not_exists,
1550 name,
1551 cols,
1552 index_type,
1553 ..
1554 }) => {
1555 for col in &cols {
1556 if let qusql_parse::IndexColExpr::Column(cname) = &col.expr
1557 && e.get_column(cname.value).is_none()
1558 {
1559 issues
1560 .err("No such column in table", col)
1561 .frag("Table defined here", table_ref);
1562 }
1563 }
1564 if matches!(index_type, qusql_parse::IndexType::Primary(_)) {
1566 for col in &cols {
1567 if let qusql_parse::IndexColExpr::Column(cname) = &col.expr
1568 && let Some(c) = e.get_column_mut(cname.value)
1569 {
1570 c.type_.not_null = true;
1571 }
1572 }
1573 }
1574 if let Some(name) = &name {
1575 let ident = if options.parse_options.get_dialect().is_postgresql() {
1576 let (schema, _) = resolve_table_name(issues, options, table_ref);
1577 IndexKey {
1578 schema: Some(
1579 schema
1580 .cloned()
1581 .unwrap_or_else(|| Identifier::new("public", 0..0)),
1582 ),
1583 table: None,
1584 index: name.clone(),
1585 }
1586 } else {
1587 IndexKey {
1588 schema: None,
1589 table: Some(resolve_table_name(issues, options, table_ref).1.clone()),
1590 index: name.clone(),
1591 }
1592 };
1593 if let Some(old) = indices.insert(ident, name.span())
1594 && if_not_exists.is_none()
1595 {
1596 issues
1597 .err("Multiple indices with the same identifier", &name.span())
1598 .frag("Already defined here", &old);
1599 }
1600 }
1601 }
1602 qusql_parse::AlterSpecification::AddForeignKey { .. } => {}
1603 qusql_parse::AlterSpecification::Modify(ModifyColumn {
1604 if_exists,
1605 col,
1606 definition,
1607 ..
1608 }) => match e.get_column_mut(col.value) {
1609 Some(c) => {
1610 let new_col = parse_column(
1611 definition,
1612 c.identifier.clone(),
1613 issues,
1614 Some(options),
1615 Some(types),
1616 search_path,
1617 );
1618 *c = new_col;
1619 }
1620 None if if_exists.is_none() => {
1621 issues
1622 .err("No such column in table", &col)
1623 .frag("Table defined here", &e.identifier_span);
1624 }
1625 None => {}
1626 },
1627 qusql_parse::AlterSpecification::AddColumn(AddColumn {
1628 identifier,
1629 data_type,
1630 if_not_exists_span,
1631 ..
1632 }) => {
1633 if e.get_column(identifier.value).is_some() {
1634 if if_not_exists_span.is_none() {
1635 issues
1636 .err("Column already exists in table", &identifier)
1637 .frag("Table defined here", &e.identifier_span);
1638 }
1639 } else {
1640 e.columns.push(parse_column(
1641 data_type,
1642 identifier,
1643 issues,
1644 Some(options),
1645 Some(types),
1646 search_path,
1647 ));
1648 }
1649 }
1650 qusql_parse::AlterSpecification::OwnerTo { .. } => {}
1651 qusql_parse::AlterSpecification::DropColumn(DropColumn {
1652 column, if_exists, ..
1653 }) => {
1654 let cnt = e.columns.len();
1655 e.columns.retain(|c| c.identifier != column);
1656 if cnt == e.columns.len() && if_exists.is_none() {
1657 issues
1658 .err("No such column in table", &column)
1659 .frag("Table defined here", &e.identifier_span);
1660 }
1661 }
1662 qusql_parse::AlterSpecification::AlterColumn(AlterColumn {
1663 column,
1664 alter_column_action,
1665 ..
1666 }) => match e.get_column_mut(column.value) {
1667 Some(c) => match alter_column_action {
1668 qusql_parse::AlterColumnAction::SetDefault { .. } => c.default = true,
1669 qusql_parse::AlterColumnAction::DropDefault { .. } => c.default = false,
1670 qusql_parse::AlterColumnAction::Type { type_, .. } => {
1671 *c = parse_column(
1672 type_,
1673 column,
1674 issues,
1675 Some(options),
1676 Some(types),
1677 search_path,
1678 );
1679 }
1680 qusql_parse::AlterColumnAction::SetNotNull { .. } => c.type_.not_null = true,
1681 qusql_parse::AlterColumnAction::DropNotNull { .. } => c.type_.not_null = false,
1682 a @ qusql_parse::AlterColumnAction::AddGenerated { .. } => {
1683 issues.err("not implemented", &a);
1684 }
1685 },
1686 None => {
1687 issues
1688 .err("No such column in table", &column)
1689 .frag("Table defined here", &e.identifier_span);
1690 }
1691 },
1692 qusql_parse::AlterSpecification::DropIndex(drop_idx) => {
1693 let is_postgresql = options.parse_options.get_dialect().is_postgresql();
1694 let key = if is_postgresql {
1695 let (schema, _) = resolve_table_name(issues, options, table_ref);
1696 IndexKey {
1697 schema: Some(
1698 schema
1699 .cloned()
1700 .unwrap_or_else(|| Identifier::new("public", 0..0)),
1701 ),
1702 table: None,
1703 index: drop_idx.name.clone(),
1704 }
1705 } else {
1706 IndexKey {
1707 schema: None,
1708 table: Some(resolve_table_name(issues, options, table_ref).1.clone()),
1709 index: drop_idx.name.clone(),
1710 }
1711 };
1712 if indices.remove(&key).is_none() {
1713 issues.err("No such index to drop", &drop_idx.name);
1714 }
1715 }
1716 qusql_parse::AlterSpecification::RenameColumn(qusql_parse::RenameColumn {
1717 old_col_name,
1718 new_col_name,
1719 ..
1720 }) => match e.get_column_mut(old_col_name.value) {
1721 Some(c) => c.identifier = new_col_name,
1722 None => {
1723 issues
1724 .err("No such column in table", &old_col_name)
1725 .frag("Table defined here", &e.identifier_span);
1726 }
1727 },
1728 qusql_parse::AlterSpecification::RenameIndex(qusql_parse::RenameIndex {
1729 old_index_name,
1730 new_index_name,
1731 ..
1732 }) => {
1733 let is_postgresql = options.parse_options.get_dialect().is_postgresql();
1734 let (schema_ref, table_id_ref) = resolve_table_name(issues, options, table_ref);
1735 let schema_id = schema_ref
1736 .cloned()
1737 .unwrap_or_else(|| Identifier::new("public", 0..0));
1738 let table_id = table_id_ref.clone();
1739 let old_key = if is_postgresql {
1740 IndexKey {
1741 schema: Some(schema_id.clone()),
1742 table: None,
1743 index: old_index_name.clone(),
1744 }
1745 } else {
1746 IndexKey {
1747 schema: None,
1748 table: Some(table_id.clone()),
1749 index: old_index_name.clone(),
1750 }
1751 };
1752 match indices.remove(&old_key) {
1753 Some(span) => {
1754 let new_key = if is_postgresql {
1755 IndexKey {
1756 schema: Some(schema_id),
1757 table: None,
1758 index: new_index_name,
1759 }
1760 } else {
1761 IndexKey {
1762 schema: None,
1763 table: Some(table_id),
1764 index: new_index_name,
1765 }
1766 };
1767 indices.insert(new_key, span);
1768 }
1769 None => {
1770 issues.err("No such index to rename", &old_index_name);
1771 }
1772 }
1773 }
1774 qusql_parse::AlterSpecification::RenameConstraint(_) => {}
1776 qusql_parse::AlterSpecification::RenameTo(_) => {}
1780 qusql_parse::AlterSpecification::Change(qusql_parse::Change {
1781 column,
1782 new_column,
1783 definition,
1784 ..
1785 }) => match e.get_column_mut(column.value) {
1786 Some(c) => {
1787 *c = parse_column(
1788 definition,
1789 new_column,
1790 issues,
1791 Some(options),
1792 Some(types),
1793 search_path,
1794 );
1795 }
1796 None => {
1797 issues
1798 .err("No such column in table", &column)
1799 .frag("Table defined here", &e.identifier_span);
1800 }
1801 },
1802 qusql_parse::AlterSpecification::Lock { .. }
1803 | qusql_parse::AlterSpecification::DropForeignKey { .. }
1804 | qusql_parse::AlterSpecification::DropPrimaryKey { .. }
1805 | qusql_parse::AlterSpecification::Algorithm { .. }
1806 | qusql_parse::AlterSpecification::AutoIncrement { .. }
1807 | qusql_parse::AlterSpecification::ReplicaIdentity(_)
1808 | qusql_parse::AlterSpecification::ValidateConstraint(_)
1809 | qusql_parse::AlterSpecification::AddTableConstraint(_)
1810 | qusql_parse::AlterSpecification::DisableTrigger(_)
1811 | qusql_parse::AlterSpecification::EnableTrigger(_)
1812 | qusql_parse::AlterSpecification::DisableRule(_)
1813 | qusql_parse::AlterSpecification::EnableRule(_)
1814 | qusql_parse::AlterSpecification::DisableRowLevelSecurity(_)
1815 | qusql_parse::AlterSpecification::EnableRowLevelSecurity(_)
1816 | qusql_parse::AlterSpecification::ForceRowLevelSecurity(_)
1817 | qusql_parse::AlterSpecification::NoForceRowLevelSecurity(_) => {}
1818 }
1819}
1820
1821impl<'a, 'b> SchemaCtx<'a, 'b> {
1822 fn process_drop_table(&mut self, t: qusql_parse::DropTable<'a>) {
1823 for i in t.tables {
1824 let key = match self.parse_qname(&i) {
1825 Some(k) => k,
1826 None => continue,
1827 };
1828 match self.schemas.schemas.entry(key) {
1829 alloc::collections::btree_map::Entry::Occupied(e) => {
1830 if e.get().view {
1831 self.issues
1832 .err("Name defines a view not a table", &i)
1833 .frag("View defined here", &e.get().identifier_span);
1834 } else {
1835 e.remove();
1836 }
1837 }
1838 alloc::collections::btree_map::Entry::Vacant(_) => {
1839 if t.if_exists.is_none() {
1840 self.issues
1841 .err("A table with this name does not exist to drop", &i);
1842 }
1843 }
1844 }
1845 }
1846 }
1847
1848 fn process_drop_view(&mut self, v: qusql_parse::DropView<'a>) {
1849 for i in v.views {
1850 let key = match self.parse_qname(&i) {
1851 Some(k) => k,
1852 None => continue,
1853 };
1854 match self.schemas.schemas.entry(key) {
1855 alloc::collections::btree_map::Entry::Occupied(e) => {
1856 if !e.get().view {
1857 self.issues
1858 .err("Name defines a table not a view", &i)
1859 .frag("Table defined here", &e.get().identifier_span);
1860 } else {
1861 e.remove();
1862 }
1863 }
1864 alloc::collections::btree_map::Entry::Vacant(_) => {
1865 if v.if_exists.is_none() {
1866 self.issues
1867 .err("A view with this name does not exist to drop", &i);
1868 }
1869 }
1870 }
1871 }
1872 }
1873
1874 fn process_drop_function(&mut self, f: qusql_parse::DropFunction<'a>) {
1875 for (func_name, _args) in &f.functions {
1876 let Some(key) = self.parse_qname(func_name) else {
1877 continue;
1878 };
1879 if self.schemas.functions.remove(&key).is_none() && f.if_exists.is_none() {
1880 self.issues.err(
1881 "A function with this name does not exist to drop",
1882 func_name,
1883 );
1884 }
1885 }
1886 }
1887
1888 fn process_drop_procedure(&mut self, p: qusql_parse::DropProcedure<'a>) {
1889 let Some(key) = self.parse_qname(&p.procedure) else {
1890 return;
1891 };
1892 if self.schemas.procedures.remove(&key).is_none() && p.if_exists.is_none() {
1893 self.issues.err(
1894 "A procedure with this name does not exist to drop",
1895 &p.procedure,
1896 );
1897 }
1898 }
1899
1900 fn process_drop_index(&mut self, ci: qusql_parse::DropIndex<'a>) {
1901 let is_pg = self.options.parse_options.get_dialect().is_postgresql();
1902 let key = if is_pg {
1903 let schema = ci
1904 .on
1905 .as_ref()
1906 .and_then(|(_, t)| t.prefix.first().map(|(s, _)| s.clone()))
1907 .unwrap_or_else(|| Identifier::new("public", 0..0));
1908 IndexKey {
1909 schema: Some(schema),
1910 table: None,
1911 index: ci.index_name.clone(),
1912 }
1913 } else {
1914 IndexKey {
1915 schema: None,
1916 table: ci.on.as_ref().map(|(_, t)| t.identifier.clone()),
1917 index: ci.index_name.clone(),
1918 }
1919 };
1920 if self.schemas.indices.remove(&key).is_none() && ci.if_exists.is_none() {
1921 self.issues.err("No such index", &ci);
1922 }
1923 }
1924 fn process_do(&mut self, d: qusql_parse::Do<'a>) -> Result<(), ()> {
1926 match d.body {
1927 qusql_parse::DoBody::Statements(stmts) => self.process_statements(stmts)?,
1928 qusql_parse::DoBody::String(s, _) => {
1929 let span_offset = s.as_ptr() as usize - self.src.as_ptr() as usize;
1930 let body_opts = self
1931 .options
1932 .parse_options
1933 .clone()
1934 .function_body(true)
1935 .span_offset(span_offset);
1936 let stmts = parse_statements(s, self.issues, &body_opts);
1937 self.process_statements(stmts)?;
1938 }
1939 }
1940 Ok(())
1941 }
1942
1943 fn process_if(&mut self, i: qusql_parse::If<'a>) -> Result<(), ()> {
1945 for cond in i.conditions {
1946 if self.eval_condition(&cond.search_condition)? {
1947 return self.process_statements(cond.then);
1948 }
1949 }
1950 if let Some((_, stmts)) = i.else_ {
1951 return self.process_statements(stmts);
1952 }
1953 Ok(())
1954 }
1955
1956 fn process_select(&mut self, s: qusql_parse::Select<'a>) -> Result<(), ()> {
1960 if let Some(span) = &s.from_span {
1962 self.issues
1963 .err("SELECT with FROM is not supported at schema level", span);
1964 return Err(());
1965 }
1966 if let Some((_, span)) = &s.where_ {
1967 self.issues
1968 .err("SELECT with WHERE is not supported at schema level", span);
1969 return Err(());
1970 }
1971 if let Some((span, _)) = &s.group_by {
1972 self.issues.err(
1973 "SELECT with GROUP BY is not supported at schema level",
1974 span,
1975 );
1976 return Err(());
1977 }
1978 if let Some((_, span)) = &s.having {
1979 self.issues
1980 .err("SELECT with HAVING is not supported at schema level", span);
1981 return Err(());
1982 }
1983 if let Some((span, _, _)) = &s.limit {
1984 self.issues
1985 .err("SELECT with LIMIT is not supported at schema level", span);
1986 return Err(());
1987 }
1988 if let Some((span, _)) = &s.order_by {
1989 self.issues.err(
1990 "SELECT with ORDER BY is not supported at schema level",
1991 span,
1992 );
1993 return Err(());
1994 }
1995 for se in s.select_exprs {
1996 self.process_expression(se.expr)?;
1997 }
1998 Ok(())
1999 }
2000
2001 fn process_expression(&mut self, expr: Expression<'a>) -> Result<(), ()> {
2002 self.eval_expr(&expr).map(|_| ())
2003 }
2004
2005 fn process_update(&mut self, u: qusql_parse::Update<'a>) -> Result<(), ()> {
2006 let span = u.update_span;
2010 for tref in u.tables {
2011 if let qusql_parse::TableReference::Table { identifier, .. } = tref {
2012 let Some(key) = self.parse_qname(&identifier) else {
2013 continue;
2014 };
2015 if lookup_name(&self.rows, &key, ()).is_some_and(|r| !r.is_empty()) {
2016 self.issues.err(
2017 "UPDATE on a table with tracked rows is not supported in schema evaluator",
2018 &span,
2019 );
2020 return Err(());
2021 }
2022 }
2023 }
2024 Ok(())
2025 }
2026
2027 fn process_delete(&mut self, d: qusql_parse::Delete<'a>) -> Result<(), ()> {
2028 let qusql_parse::Delete {
2029 tables,
2030 using,
2031 where_,
2032 order_by,
2033 limit,
2034 ..
2035 } = d;
2036
2037 let has_unsupported = !using.is_empty() || order_by.is_some() || limit.is_some();
2040 if has_unsupported {
2041 for table in &tables {
2042 let Some(key) = self.parse_qname(table) else {
2043 continue;
2044 };
2045 if lookup_name(&self.rows, &key, ()).is_some_and(|r| !r.is_empty()) {
2046 self.issues.err(
2047 "DELETE with USING/ORDER BY/LIMIT on a table with tracked rows \
2048 is not supported in schema evaluator",
2049 table,
2050 );
2051 return Err(());
2052 }
2053 }
2054 }
2055
2056 if let Some((where_expr, _)) = where_ {
2057 for table in &tables {
2059 let Some(key) = self.parse_qname(table) else {
2060 continue;
2061 };
2062 let Some(source_rows) = self.rows.get(&key) else {
2063 continue;
2064 };
2065 let source_rows = source_rows.clone();
2066 let mut new_rows = Vec::new();
2067 for row in source_rows {
2068 let saved = self.current_row.replace(row.clone());
2069 let matches = self.eval_expr(&where_expr).map(|v| v.is_truthy());
2070 self.current_row = saved;
2071 match matches {
2072 Ok(true) => {} Ok(false) => new_rows.push(row),
2074 Err(()) => return Err(()),
2075 }
2076 }
2077 self.rows.insert(key, new_rows);
2078 }
2079 } else {
2080 for table in tables {
2082 let Some(key) = self.parse_qname(&table) else {
2083 continue;
2084 };
2085 self.rows.remove(&key);
2086 }
2087 }
2088 Ok(())
2089 }
2090
2091 fn process_alter_type(&mut self, a: qusql_parse::AlterType<'a>) {
2092 let Some(key) = self.parse_qname(&a.name) else {
2093 return;
2094 };
2095 match a.action {
2096 qusql_parse::AlterTypeAction::AddValue {
2097 if_not_exists_span,
2098 new_enum_value,
2099 ..
2100 } => {
2101 let Some(TypeDef::Enum { values, .. }) = self.schemas.types.get_mut(&key) else {
2102 self.issues.err("Type not found", &a.name);
2103 return;
2104 };
2105 let new_val: Cow<'a, str> = new_enum_value.value;
2106 if values.contains(&new_val) {
2107 if if_not_exists_span.is_none() {
2108 self.issues.err("Enum value already exists", &a.name);
2109 }
2110 } else {
2112 Arc::make_mut(values).push(new_val);
2113 }
2114 }
2115 qusql_parse::AlterTypeAction::RenameTo { new_name, .. } => {
2116 let new_key = self.make_table_key(key.schema_name().cloned(), new_name);
2117 if let Some(typedef) = self.schemas.types.remove(&key) {
2118 self.schemas.types.insert(new_key, typedef);
2119 } else {
2120 self.issues.err("Type not found", &a.name);
2121 }
2122 }
2123 qusql_parse::AlterTypeAction::RenameValue {
2124 existing_enum_value,
2125 new_enum_value,
2126 ..
2127 } => {
2128 let Some(TypeDef::Enum { values, .. }) = self.schemas.types.get_mut(&key) else {
2129 self.issues.err("Type not found", &a.name);
2130 return;
2131 };
2132 let old_val: Cow<'a, str> = existing_enum_value.value;
2133 let new_val: Cow<'a, str> = new_enum_value.value;
2134 if let Some(entry) = Arc::make_mut(values).iter_mut().find(|v| **v == old_val) {
2135 *entry = new_val;
2136 } else {
2137 self.issues.err("Enum value not found", &a.name);
2138 }
2139 }
2140 _ => {}
2143 }
2144 }
2145
2146 fn process_truncate_table(&mut self, t: qusql_parse::TruncateTable<'a>) {
2147 for spec in t.tables {
2148 let Some(key) = self.parse_qname(&spec.table_name) else {
2149 continue;
2150 };
2151 self.rows.remove(&key);
2152 }
2153 }
2154
2155 fn process_create_schema(&mut self, s: qusql_parse::CreateSchema<'a>) {
2156 if !self.options.parse_options.get_dialect().is_postgresql() {
2157 self.issues.err(
2158 "CREATE SCHEMA is not supported in MySQL; use CREATE DATABASE",
2159 &s,
2160 );
2161 return;
2162 }
2163 let Some(name) = s.name else {
2164 return;
2166 };
2167 match self.schemas.schema_names.get(&name) {
2168 Some(_) if s.if_not_exists.is_none() => {
2169 self.issues.err("Schema already exists", &name);
2170 }
2171 _ => {
2172 self.schemas.schema_names.insert(name);
2173 }
2174 }
2175 }
2176
2177 fn process_drop_schema(&mut self, s: qusql_parse::DropSchema<'a>) {
2178 if !self.schemas.schema_names.remove(&s.schema) && s.if_exists.is_none() {
2179 self.issues.err("Schema does not exist", &s.schema);
2180 }
2181 self.schemas
2183 .schemas
2184 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2185 self.schemas
2187 .functions
2188 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2189 self.schemas
2191 .procedures
2192 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2193 self.schemas
2195 .types
2196 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2197 self.schemas
2199 .indices
2200 .retain(|k, _| k.schema.as_ref() != Some(&s.schema));
2201 self.schemas
2203 .sequences
2204 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2205 self.rows
2207 .retain(|k, _| k.schema_name().map(|n| n == &s.schema) != Some(true));
2208 }
2209
2210 fn process_rename_table(&mut self, r: qusql_parse::RenameTable<'a>) {
2211 for pair in r.table_to_tables {
2212 let old_key = match self.parse_qname(&pair.table) {
2213 Some(k) => k,
2214 None => continue,
2215 };
2216 let new_key = match self.parse_qname(&pair.new_table) {
2217 Some(k) => k,
2218 None => continue,
2219 };
2220 if let Some(schema) = self.schemas.schemas.remove(&old_key) {
2222 self.schemas.schemas.insert(new_key.clone(), schema);
2223 } else {
2224 self.issues.err("Table not found", &pair.table);
2225 }
2226 if let Some(rows) = self.rows.remove(&old_key) {
2228 self.rows.insert(new_key, rows);
2229 }
2230 }
2231 }
2232
2233 fn process_insert(&mut self, i: qusql_parse::InsertReplace<'a>) -> Result<(), ()> {
2234 let Some(table_key) = self.parse_qname(&i.table) else {
2235 return Ok(());
2236 };
2237 let col_names: Vec<&'a str> = i.columns.iter().map(|c| c.value).collect();
2238
2239 if let Some(set) = i.set {
2240 let mut row: Vec<(&'a str, SqlValue<'a>)> = Vec::new();
2242 for pair in set.pairs {
2243 if let Ok(val) = self.eval_expr(&pair.value) {
2244 row.push((pair.column.value, val));
2245 }
2246 }
2247 self.rows.entry(table_key).or_default().push(Rc::new(row));
2248 return Ok(());
2249 }
2250
2251 if let Some((_, value_rows)) = i.values {
2252 for row_exprs in value_rows {
2254 let mut row: Vec<(&'a str, SqlValue<'a>)> = Vec::new();
2255 for (col, expr) in col_names.iter().zip(row_exprs.iter()) {
2256 if let Ok(val) = self.eval_expr(expr) {
2257 row.push((col, val));
2258 }
2259 }
2260 self.rows
2261 .entry(table_key.clone())
2262 .or_default()
2263 .push(Rc::new(row));
2264 }
2265 return Ok(());
2266 }
2267
2268 let Some(select_stmt) = i.select else {
2269 return Ok(());
2270 };
2271 let select_exprs: Vec<_> = if let qusql_parse::Statement::Select(s) = &select_stmt {
2275 s.select_exprs.iter().map(|se| se.expr.clone()).collect()
2276 } else {
2277 Vec::new()
2278 };
2279 let source_rows = self.eval_statement_rows(&select_stmt)?;
2280 let mut built_rows = Vec::with_capacity(source_rows.len());
2281 for source_row in source_rows {
2282 let saved_row = self.current_row.replace(source_row);
2283 let mut row: Vec<(&'a str, SqlValue<'a>)> = Vec::new();
2284 for (col, expr) in col_names.iter().zip(select_exprs.iter()) {
2285 if let Ok(val) = self.eval_expr(expr) {
2286 row.push((col, val));
2287 }
2288 }
2289 self.current_row = saved_row;
2290 built_rows.push(Rc::new(row));
2291 }
2292 self.rows.entry(table_key).or_default().extend(built_rows);
2293 Ok(())
2294 }
2295
2296 fn eval_expr(&mut self, expr: &Expression<'a>) -> Result<SqlValue<'a>, ()> {
2301 match expr {
2302 Expression::Null(_) => Ok(SqlValue::Null),
2303 Expression::Bool(b) => Ok(SqlValue::Bool(b.value)),
2304 Expression::Integer(i) => Ok(SqlValue::Integer(i.value as i64)),
2305 Expression::String(s) => Ok(match &s.value {
2306 Cow::Borrowed(b) => SqlValue::SourceText(b),
2307 Cow::Owned(o) => SqlValue::OwnedText(o.clone()),
2308 }),
2309 Expression::Identifier(id) => {
2310 if let [IdentifierPart::Name(name)] = id.parts.as_slice() {
2311 self.bindings
2312 .get(name.value)
2313 .cloned()
2314 .or_else(|| {
2315 self.current_row.as_ref().and_then(|r| {
2316 r.iter()
2317 .find(|(k, _)| *k == name.value)
2318 .map(|(_, v)| v.clone())
2319 })
2320 })
2321 .ok_or(())
2322 } else {
2323 Err(())
2324 }
2325 }
2326 Expression::Exists(e) => Ok(SqlValue::Bool(self.eval_exists(&e.subquery)?)),
2327 Expression::Unary(u) => match &u.op {
2328 qusql_parse::UnaryOperator::Not(_) | qusql_parse::UnaryOperator::LogicalNot(_) => {
2329 Ok(SqlValue::Bool(!self.eval_expr(&u.operand)?.is_truthy()))
2330 }
2331 qusql_parse::UnaryOperator::Minus(_) => match self.eval_expr(&u.operand)? {
2332 SqlValue::Integer(i) => Ok(SqlValue::Integer(-i)),
2333 _ => Err(()),
2334 },
2335 _ => Err(()),
2336 },
2337 Expression::Function(f) => self.eval_function_expr(f),
2338 Expression::AggregateFunction(f) => self.eval_aggregate(f),
2339 Expression::Binary(b) => self.eval_binary_expr(b),
2340 _ => {
2341 self.issues
2342 .err("Unimplemented expression in schema evaluator", expr);
2343 Err(())
2344 }
2345 }
2346 }
2347
2348 fn eval_exists(&mut self, stmt: &qusql_parse::Statement<'a>) -> Result<bool, ()> {
2349 Ok(!self.eval_statement_rows(stmt)?.is_empty())
2350 }
2351
2352 fn resolve_from_rows(&mut self, s: &qusql_parse::Select<'a>) -> Result<Vec<Row<'a>>, ()> {
2359 use qusql_parse::TableReference;
2360 let Some(refs) = s.table_references.as_deref() else {
2361 return Ok(Vec::new());
2362 };
2363 let [TableReference::Table { identifier, .. }] = refs else {
2364 self.issues.err(
2365 "FROM clause with joins or subqueries is not supported in schema evaluator",
2366 &refs[0],
2367 );
2368 return Err(());
2369 };
2370
2371 let table_key: QualifiedIdentifier = match identifier.prefix.as_slice() {
2372 [] => QualifiedIdentifier::Unqualified(identifier.identifier.clone()),
2373 [(schema, _)]
2374 if schema.value.eq_ignore_ascii_case("information_schema")
2375 && identifier.identifier.value.eq_ignore_ascii_case("columns") =>
2376 {
2377 let rows = self
2379 .schemas
2380 .schemas
2381 .iter()
2382 .flat_map(|(key, schema)| {
2383 schema.columns.iter().map(move |col| {
2384 Rc::new(alloc::vec![
2385 ("table_name", SqlValue::SourceText(key.table_name().value)),
2386 ("column_name", SqlValue::SourceText(col.identifier.value)),
2387 ])
2388 })
2389 })
2390 .collect();
2391 return Ok(rows);
2392 }
2393 [(schema, _)] => {
2394 QualifiedIdentifier::Qualified(schema.clone(), identifier.identifier.clone())
2395 }
2396 _ => {
2397 self.issues.err(
2398 "Qualified table name in FROM clause is not supported in schema evaluator",
2399 identifier,
2400 );
2401 return Err(());
2402 }
2403 };
2404
2405 let Some((resolved_key, _)) = lookup_name_key(
2406 &self.schemas.schemas,
2407 &table_key,
2408 self.search_path.as_slice(),
2409 ) else {
2410 self.issues.err(
2411 alloc::format!("Unknown table `{table_key}` referenced in schema evaluator"),
2412 identifier,
2413 );
2414 return Err(());
2415 };
2416 Ok(self.rows.get(resolved_key).cloned().unwrap_or_default())
2417 }
2418
2419 fn eval_statement_rows(
2422 &mut self,
2423 stmt: &qusql_parse::Statement<'a>,
2424 ) -> Result<Vec<Row<'a>>, ()> {
2425 match stmt {
2426 qusql_parse::Statement::Select(s) => self.eval_select_matching_rows(s),
2427 qusql_parse::Statement::CompoundQuery(cq) => self.eval_compound_query_rows(cq),
2428 _ => {
2429 self.issues
2430 .err("Unsupported statement kind in INSERT ... SELECT", stmt);
2431 Err(())
2432 }
2433 }
2434 }
2435
2436 fn eval_compound_query_rows(
2439 &mut self,
2440 cq: &qusql_parse::CompoundQuery<'a>,
2441 ) -> Result<Vec<Row<'a>>, ()> {
2442 use qusql_parse::CompoundOperator;
2443 let mut result = self.eval_statement_rows(&cq.left)?;
2444 for branch in &cq.with {
2445 match branch.operator {
2446 CompoundOperator::Union => {
2447 let branch_rows = self.eval_statement_rows(&branch.statement)?;
2448 result.extend(branch_rows);
2449 }
2450 CompoundOperator::Intersect | CompoundOperator::Except => {
2451 self.issues.err(
2452 "INTERSECT / EXCEPT is not supported in schema evaluator",
2453 &branch.operator_span,
2454 );
2455 return Err(());
2456 }
2457 }
2458 }
2459 Ok(result)
2460 }
2461
2462 fn eval_select_matching_rows(
2464 &mut self,
2465 s: &qusql_parse::Select<'a>,
2466 ) -> Result<Vec<Row<'a>>, ()> {
2467 let source_rows = self.resolve_from_rows(s)?;
2468 let where_expr: Option<Expression<'a>> = s.where_.as_ref().map(|(e, _)| e.clone());
2469 let mut result = Vec::new();
2470 for row in source_rows {
2471 let saved_row = self.current_row.replace(row.clone());
2472 let eval_result = match &where_expr {
2473 Some(expr) => self.eval_expr(expr).map(|v| v.is_truthy()),
2474 None => Ok(true),
2475 };
2476 self.current_row = saved_row;
2477 if eval_result? {
2478 result.push(row);
2479 }
2480 }
2481 Ok(result)
2482 }
2483
2484 fn eval_function_expr(
2485 &mut self,
2486 f: &qusql_parse::FunctionCallExpression<'a>,
2487 ) -> Result<SqlValue<'a>, ()> {
2488 use qusql_parse::Function;
2489 match &f.function {
2490 Function::Other(parts) if !parts.is_empty() => {
2491 let fn_ident = parts.last().unwrap().clone();
2492 let schema_ident = (parts.len() >= 2).then(|| parts[parts.len() - 2].clone());
2493 let key = match schema_ident {
2494 Some(s) => QualifiedIdentifier::Qualified(s, fn_ident.clone()),
2495 None => QualifiedIdentifier::Unqualified(fn_ident.clone()),
2496 };
2497 let func_info =
2498 lookup_name(&self.schemas.functions, &key, self.search_path.as_slice())
2499 .and_then(|func| {
2500 func.body
2501 .as_ref()
2502 .map(|b| (func.params.clone(), b.statements.clone()))
2503 });
2504 let Some((params, statements)) = func_info else {
2505 self.issues.err(
2506 alloc::format!(
2507 "Unknown function or function has no evaluable body: {}",
2508 parts
2509 .iter()
2510 .map(|p| p.value)
2511 .collect::<alloc::vec::Vec<_>>()
2512 .join(".")
2513 ),
2514 f,
2515 );
2516 return Err(());
2517 };
2518 let mut bindings = BTreeMap::new();
2519 for (param, arg) in params.iter().zip(f.args.iter()) {
2520 let Some(name) = ¶m.name else { continue };
2521 if let Ok(value) = self.eval_expr(arg) {
2522 bindings.insert(name.value, value);
2523 }
2524 }
2525 let old_bindings = core::mem::replace(&mut self.bindings, bindings);
2526 let old_return = self.return_value.take();
2527 let _ = self.process_statements(statements);
2528 let ret = self.return_value.take().unwrap_or(SqlValue::Null);
2529 self.return_value = old_return;
2530 self.bindings = old_bindings;
2531 Ok(ret)
2532 }
2533 Function::Coalesce => {
2534 let args: Vec<_> = f.args.clone();
2536 for arg in &args {
2537 let v = self.eval_expr(arg)?;
2538 if v != SqlValue::Null {
2539 return Ok(v);
2540 }
2541 }
2542 Ok(SqlValue::Null)
2543 }
2544 Function::Exists => {
2545 let Some(Expression::Subquery(sq)) = f.args.first() else {
2546 self.issues.err("EXISTS without subquery argument", f);
2547 return Err(());
2548 };
2549 let qusql_parse::Statement::Select(s) = &sq.expression else {
2550 self.issues.err("EXISTS argument is not a SELECT", f);
2551 return Err(());
2552 };
2553 let s = s.clone();
2554 Ok(SqlValue::Bool(
2555 !self.eval_select_matching_rows(&s)?.is_empty(),
2556 ))
2557 }
2558 Function::Nextval => {
2559 let arg = f.args.first().ok_or(())?;
2560 let seq_str = match self.eval_expr(arg)? {
2561 SqlValue::SourceText(s) => alloc::borrow::Cow::Borrowed(s),
2562 SqlValue::OwnedText(s) => alloc::borrow::Cow::Owned(s),
2563 _ => {
2564 self.issues.err("nextval: argument must be a string", f);
2565 return Err(());
2566 }
2567 };
2568 let seq_name = seq_str.as_ref();
2571 let key = if let Some((schema, seq)) = seq_name.split_once('.') {
2572 QualifiedIdentifier::Qualified(
2573 Identifier::new(schema, 0..0),
2574 Identifier::new(seq, 0..0),
2575 )
2576 } else {
2577 QualifiedIdentifier::Unqualified(Identifier::new(seq_name, 0..0))
2578 };
2579 let found = lookup_name(&self.schemas.sequences, &key, self.search_path.as_slice())
2580 .is_some();
2581 if !found {
2582 self.issues
2583 .err(alloc::format!("nextval: unknown sequence `{seq_name}`"), f);
2584 return Err(());
2585 }
2586 Ok(SqlValue::Integer(1))
2587 }
2588 _ => {
2589 self.issues
2590 .err("Unimplemented function in schema evaluator", f);
2591 Err(())
2592 }
2593 }
2594 }
2595
2596 fn eval_aggregate(
2597 &mut self,
2598 f: &qusql_parse::AggregateFunctionCallExpression<'a>,
2599 ) -> Result<SqlValue<'a>, ()> {
2600 use qusql_parse::Function;
2601 match &f.function {
2602 Function::Max => {
2603 let col_expr = f.args.first().ok_or(())?.clone();
2604 let rows = core::mem::take(&mut self.current_table_rows);
2606 let mut max: Option<SqlValue<'a>> = None;
2607 for r in &rows {
2608 let saved = self.current_row.replace(r.clone());
2610 let v = self.eval_expr(&col_expr);
2612 self.current_row = saved;
2613 if let Ok(v) = v
2614 && v != SqlValue::Null
2615 {
2616 max = Some(match max {
2617 None => v,
2618 Some(SqlValue::Integer(m)) => {
2619 if let SqlValue::Integer(n) = &v {
2620 SqlValue::Integer(m.max(*n))
2621 } else {
2622 v
2623 }
2624 }
2625 Some(existing) => existing,
2626 });
2627 }
2628 }
2629 self.current_table_rows = rows;
2630 Ok(max.unwrap_or(SqlValue::Null))
2631 }
2632 Function::Count => {
2633 let rows = core::mem::take(&mut self.current_table_rows);
2634 let is_star = matches!(
2635 f.args.first(),
2636 Some(Expression::Identifier(ie))
2637 if matches!(ie.parts.as_slice(), [IdentifierPart::Star(_)])
2638 );
2639 let count = if f.args.is_empty() || is_star {
2640 rows.len() as i64
2642 } else {
2643 let col_expr = f.args.first().unwrap().clone();
2644 let mut n = 0i64;
2645 for r in &rows {
2646 let saved = self.current_row.replace(r.clone());
2647 let v = self.eval_expr(&col_expr);
2648 self.current_row = saved;
2649 if matches!(v, Ok(v) if v != SqlValue::Null) {
2650 n += 1;
2651 }
2652 }
2653 n
2654 };
2655 self.current_table_rows = rows;
2656 Ok(SqlValue::Integer(count))
2657 }
2658 _ => {
2659 self.issues
2660 .err("Unimplemented aggregate function in schema evaluator", f);
2661 Err(())
2662 }
2663 }
2664 }
2665
2666 fn eval_binary_expr(
2667 &mut self,
2668 b: &qusql_parse::BinaryExpression<'a>,
2669 ) -> Result<SqlValue<'a>, ()> {
2670 use qusql_parse::BinaryOperator;
2671 let lhs = self.eval_expr(&b.lhs)?;
2672 match &b.op {
2674 BinaryOperator::And(_) => {
2675 if !lhs.is_truthy() {
2676 return Ok(SqlValue::Bool(false));
2677 }
2678 return Ok(SqlValue::Bool(self.eval_expr(&b.rhs)?.is_truthy()));
2679 }
2680 BinaryOperator::Or(_) => {
2681 if lhs.is_truthy() {
2682 return Ok(SqlValue::Bool(true));
2683 }
2684 return Ok(SqlValue::Bool(self.eval_expr(&b.rhs)?.is_truthy()));
2685 }
2686 _ => {}
2687 }
2688 let rhs = self.eval_expr(&b.rhs)?;
2689 Ok(match &b.op {
2691 BinaryOperator::Eq(_) => lhs.sql_eq(&rhs).map_or(SqlValue::Null, SqlValue::Bool),
2692 BinaryOperator::Neq(_) => lhs
2693 .sql_eq(&rhs)
2694 .map_or(SqlValue::Null, |v| SqlValue::Bool(!v)),
2695 BinaryOperator::LtEq(_) => lhs.sql_lte(&rhs).map_or(SqlValue::Null, SqlValue::Bool),
2696 BinaryOperator::Lt(_) => rhs
2697 .sql_lte(&lhs)
2698 .map_or(SqlValue::Null, |v| SqlValue::Bool(!v)),
2699 BinaryOperator::GtEq(_) => rhs.sql_lte(&lhs).map_or(SqlValue::Null, SqlValue::Bool),
2700 BinaryOperator::Gt(_) => lhs
2701 .sql_lte(&rhs)
2702 .map_or(SqlValue::Null, |v| SqlValue::Bool(!v)),
2703 _ => {
2704 self.issues
2705 .err("Unimplemented binary operator in schema evaluator", b);
2706 return Err(());
2707 }
2708 })
2709 }
2710
2711 fn eval_condition(&mut self, s: &qusql_parse::Select<'a>) -> Result<bool, ()> {
2716 let expr = s.select_exprs.first().map(|se| se.expr.clone()).ok_or(())?;
2717
2718 let table_rows = self.resolve_from_rows(s)?;
2720 let saved = core::mem::replace(&mut self.current_table_rows, table_rows);
2721 let result = self.eval_expr(&expr);
2722 self.current_table_rows = saved;
2723
2724 Ok(result?.is_truthy())
2725 }
2726
2727 fn process_plpgsql_execute(&mut self, e: qusql_parse::PlpgsqlExecute<'a>) -> Result<(), ()> {
2728 let sql = self.resolve_expr_to_bound_string(&e.command);
2729 let Some(sql) = sql else {
2730 self.issues.err(
2731 "EXECUTE argument could not be resolved to a known SQL string",
2732 &e,
2733 );
2734 return Err(());
2735 };
2736 let span_offset = sql.as_ptr() as usize - self.src.as_ptr() as usize;
2737 let opts = self.options.parse_options.clone().span_offset(span_offset);
2738 let stmts = parse_statements(sql, self.issues, &opts);
2739 let _ = self.process_statements(stmts);
2740 Ok(())
2741 }
2742
2743 fn resolve_expr_to_bound_string(&self, expr: &Expression<'a>) -> Option<&'a str> {
2747 let Expression::Identifier(ident) = expr else {
2748 return None;
2749 };
2750 let [IdentifierPart::Name(name)] = ident.parts.as_slice() else {
2751 return None;
2752 };
2753 self.bindings
2754 .get(name.value)
2755 .and_then(|v| v.as_source_text())
2756 }
2757}
2758
2759const POSTGRESQL_BUILTIN_SQL: &str = "
2768CREATE TABLE spatial_ref_sys (
2769 srid INTEGER NOT NULL,
2770 auth_name VARCHAR(256),
2771 auth_srid INTEGER,
2772 srtext VARCHAR(2048),
2773 proj4text VARCHAR(2048)
2774);
2775CREATE VIEW geometry_columns AS (
2776 SELECT '' AS f_table_catalog, '' AS f_table_schema, '' AS f_table_name,
2777 '' AS f_geometry_column, 0 AS coord_dimension, 0 AS srid, '' AS type
2778);
2779CREATE VIEW geography_columns AS (
2780 SELECT '' AS f_table_catalog, '' AS f_table_schema, '' AS f_table_name,
2781 '' AS f_geography_column, 0 AS coord_dimension, 0 AS srid, '' AS type
2782);
2783";
2784
2785pub fn parse_schemas<'a>(
2786 src: &'a str,
2787 issues: &mut Issues<'a>,
2788 options: &TypeOptions,
2789) -> Schemas<'a> {
2790 let statements = parse_statements(src, issues, &options.parse_options);
2791
2792 let mut schemas = Schemas {
2793 schemas: Default::default(),
2794 schema_names: Default::default(),
2795 procedures: Default::default(),
2796 functions: Default::default(),
2797 indices: Default::default(),
2798 types: Default::default(),
2799 sequences: Default::default(),
2800 };
2801
2802 SchemaCtx::new(&mut schemas, issues, src, options).process_top_level_statements(statements);
2803
2804 let dummy_schemas = Schemas::default();
2805
2806 let mut typer = crate::typer::Typer {
2807 schemas: &dummy_schemas,
2808 issues,
2809 reference_types: Vec::new(),
2810 outer_reference_types: Vec::new(),
2811 arg_types: Default::default(),
2812 options,
2813 with_schemas: Default::default(),
2814 };
2815
2816 for (key, schema) in &mut schemas.schemas {
2818 if schema.columns.iter().all(|v| v.as_.is_none()) {
2819 continue;
2820 }
2821 typer.reference_types.clear();
2822 let mut columns = Vec::new();
2823 for c in &schema.columns {
2824 columns.push((c.identifier.clone(), c.type_.clone()));
2825 }
2826 typer.reference_types.push(crate::typer::ReferenceType {
2827 name: Some(key.table_name().clone()),
2828 span: schema.identifier_span.clone(),
2829 columns,
2830 });
2831 for c in &mut schema.columns {
2832 if let Some(as_) = &c.as_ {
2833 let full_type = crate::type_expression::type_expression(
2834 &mut typer,
2835 as_,
2836 crate::type_expression::ExpressionFlags::default(),
2837 BaseType::Any,
2838 );
2839 c.type_.not_null = full_type.not_null;
2840 }
2841 }
2842 }
2843
2844 let dialect = options.parse_options.get_dialect();
2848 if dialect.is_postgresql() {
2849 let builtin_src: &'a str = POSTGRESQL_BUILTIN_SQL;
2851 let builtin_options = TypeOptions::new().dialect(dialect);
2852 let builtin_stmts = parse_statements(
2853 builtin_src,
2854 &mut Issues::new(builtin_src),
2855 &builtin_options.parse_options,
2856 );
2857 let mut builtin_schemas: Schemas<'a> = Schemas::default();
2858 SchemaCtx::new(
2859 &mut builtin_schemas,
2860 &mut Issues::new(builtin_src),
2861 builtin_src,
2862 &builtin_options,
2863 )
2864 .process_top_level_statements(builtin_stmts);
2865 for (k, v) in builtin_schemas.schemas {
2867 schemas.schemas.entry(k).or_insert(v);
2868 }
2869 }
2870
2871 schemas
2872}