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