1use crate::{
86 Type, TypeOptions,
87 type_::{BaseType, FullType},
88 type_statement,
89 typer::unqualified_name,
90};
91use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
92use qusql_parse::{
93 AddColumn, AddIndex, AlterColumn, DataType, DropColumn, Expression, Identifier, Issues,
94 ModifyColumn, Span, Spanned, parse_statements,
95};
96
97#[derive(Debug)]
99pub struct Column<'a> {
100 pub identifier: Identifier<'a>,
101 pub type_: FullType<'a>,
103 pub auto_increment: bool,
105 pub default: bool,
106 pub as_: Option<Expression<'a>>,
107 pub generated: bool,
108}
109
110#[derive(Debug)]
112pub struct Schema<'a> {
113 pub identifier_span: Span,
115 pub columns: Vec<Column<'a>>,
117 pub view: bool,
119}
120
121impl<'a> Schema<'a> {
122 pub fn get_column(&self, identifier: &str) -> Option<&Column<'a>> {
123 self.columns
124 .iter()
125 .find(|&column| column.identifier.value == identifier)
126 }
127 pub fn get_column_mut(&mut self, identifier: &str) -> Option<&mut Column<'a>> {
128 self.columns
129 .iter_mut()
130 .find(|column| column.identifier.value == identifier)
131 }
132}
133
134#[derive(Debug)]
136pub struct Procedure {}
137
138#[derive(Debug)]
140pub struct Functions {}
141
142#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
143pub struct IndexKey<'a> {
144 pub table: Option<Identifier<'a>>,
145 pub index: Identifier<'a>,
146}
147
148#[derive(Debug, Default)]
150pub struct Schemas<'a> {
151 pub schemas: BTreeMap<Identifier<'a>, Schema<'a>>,
153 pub procedures: BTreeMap<Identifier<'a>, Procedure>,
155 pub functions: BTreeMap<Identifier<'a>, Functions>,
157 pub indices: BTreeMap<IndexKey<'a>, Span>,
159}
160
161pub(crate) fn parse_column<'a>(
162 data_type: DataType<'a>,
163 identifier: Identifier<'a>,
164 _issues: &mut Issues<'a>,
165 options: Option<&TypeOptions>,
166) -> Column<'a> {
167 let mut not_null = false;
168 let mut unsigned = false;
169 let mut auto_increment = false;
170 let mut default = false;
171 let mut _as = None;
172 let mut generated = false;
173 let mut primary_key = false;
174 let is_sqlite = options
175 .map(|v| v.parse_options.get_dialect().is_sqlite())
176 .unwrap_or_default();
177 for p in data_type.properties {
178 match p {
179 qusql_parse::DataTypeProperty::Signed(_) => unsigned = false,
180 qusql_parse::DataTypeProperty::Unsigned(_) => unsigned = true,
181 qusql_parse::DataTypeProperty::Null(_) => not_null = false,
182 qusql_parse::DataTypeProperty::NotNull(_) => not_null = true,
183 qusql_parse::DataTypeProperty::AutoIncrement(_) => auto_increment = true,
184 qusql_parse::DataTypeProperty::As((_, e)) => _as = Some(e),
185 qusql_parse::DataTypeProperty::Default(_) => default = true,
186 qusql_parse::DataTypeProperty::GeneratedAlways(_) => generated = true,
187 qusql_parse::DataTypeProperty::PrimaryKey(_) => primary_key = true,
188 _ => {}
189 }
190 }
191 let type_ = match data_type.type_ {
192 qusql_parse::Type::TinyInt(v) => {
193 if !unsigned && matches!(v, Some((1, _))) {
194 BaseType::Bool.into()
195 } else if unsigned {
196 Type::U8
197 } else {
198 Type::I8
199 }
200 }
201 qusql_parse::Type::SmallInt(_) => {
202 if unsigned {
203 Type::U16
204 } else {
205 Type::I16
206 }
207 }
208 qusql_parse::Type::MediumInt(_) => {
209 if unsigned {
210 Type::U24
211 } else {
212 Type::I24
213 }
214 }
215 qusql_parse::Type::Int(_) => {
216 if unsigned {
217 Type::U32
218 } else {
219 Type::I32
220 }
221 }
222 qusql_parse::Type::BigInt(_) => {
223 if unsigned {
224 Type::U64
225 } else {
226 Type::I64
227 }
228 }
229 qusql_parse::Type::Char(_) => BaseType::String.into(),
230 qusql_parse::Type::VarChar(_) => BaseType::String.into(),
231 qusql_parse::Type::TinyText(_) => BaseType::String.into(),
232 qusql_parse::Type::MediumText(_) => BaseType::String.into(),
233 qusql_parse::Type::Text(_) => BaseType::String.into(),
234 qusql_parse::Type::LongText(_) => BaseType::String.into(),
235 qusql_parse::Type::Enum(e) => {
236 Type::Enum(Arc::new(e.into_iter().map(|s| s.value).collect()))
237 }
238 qusql_parse::Type::Set(s) => Type::Set(Arc::new(s.into_iter().map(|s| s.value).collect())),
239 qusql_parse::Type::Float(_) => Type::F32,
240 qusql_parse::Type::Double(_) => Type::F64,
241 qusql_parse::Type::DateTime(_) => BaseType::DateTime.into(),
242 qusql_parse::Type::Timestamp(_) => BaseType::TimeStamp.into(),
243 qusql_parse::Type::Time(_) => BaseType::Time.into(),
244 qusql_parse::Type::TinyBlob(_) => BaseType::Bytes.into(),
245 qusql_parse::Type::MediumBlob(_) => BaseType::Bytes.into(),
246 qusql_parse::Type::Date => BaseType::Date.into(),
247 qusql_parse::Type::Blob(_) => BaseType::Bytes.into(),
248 qusql_parse::Type::LongBlob(_) => BaseType::Bytes.into(),
249 qusql_parse::Type::VarBinary(_) => BaseType::Bytes.into(),
250 qusql_parse::Type::Binary(_) => BaseType::Bytes.into(),
251 qusql_parse::Type::Boolean => BaseType::Bool.into(),
252 qusql_parse::Type::Integer(_) => {
253 if is_sqlite && primary_key {
254 auto_increment = true;
255 }
256 BaseType::Integer.into()
257 }
258 qusql_parse::Type::Float8 => BaseType::Float.into(),
259 qusql_parse::Type::Numeric(_) => todo!("Numeric"),
260 qusql_parse::Type::Decimal(_) => todo!("Decimal"),
261 qusql_parse::Type::Timestamptz => BaseType::TimeStamp.into(),
262 qusql_parse::Type::Json => BaseType::String.into(),
263 qusql_parse::Type::Jsonb => BaseType::String.into(),
264 qusql_parse::Type::Bit(_, _) => BaseType::Bytes.into(),
265 qusql_parse::Type::VarBit(_) => BaseType::Bytes.into(),
266 qusql_parse::Type::Bytea => BaseType::Bytes.into(),
267 qusql_parse::Type::Named(_) => BaseType::String.into(), qusql_parse::Type::Inet4 => BaseType::String.into(),
269 qusql_parse::Type::Inet6 => BaseType::String.into(),
270 qusql_parse::Type::InetAddr => BaseType::String.into(),
271 qusql_parse::Type::Cidr => BaseType::String.into(),
272 qusql_parse::Type::Macaddr => BaseType::String.into(),
273 qusql_parse::Type::Macaddr8 => BaseType::String.into(),
274 qusql_parse::Type::Array(_, _) => todo!("Array type not yet implemented"),
275 qusql_parse::Type::Table(_, _) => todo!("Table type not yet implemented"),
276 qusql_parse::Type::Serial
277 | qusql_parse::Type::SmallSerial
278 | qusql_parse::Type::BigSerial => BaseType::Integer.into(),
279 qusql_parse::Type::Money => BaseType::Float.into(),
280 qusql_parse::Type::Timetz(_) => BaseType::Time.into(),
281 qusql_parse::Type::Interval(_) => BaseType::TimeInterval.into(),
282 qusql_parse::Type::TsQuery => BaseType::String.into(),
283 qusql_parse::Type::TsVector => BaseType::String.into(),
284 qusql_parse::Type::Uuid => BaseType::String.into(),
285 qusql_parse::Type::Xml => BaseType::String.into(),
286 qusql_parse::Type::Range(_) => BaseType::Bytes.into(),
287 qusql_parse::Type::MultiRange(_) => BaseType::Bytes.into(),
288 qusql_parse::Type::Point
289 | qusql_parse::Type::Line
290 | qusql_parse::Type::Lseg
291 | qusql_parse::Type::Box
292 | qusql_parse::Type::Path
293 | qusql_parse::Type::Polygon
294 | qusql_parse::Type::Circle => BaseType::Bytes.into(),
295 };
296
297 Column {
298 identifier,
299 type_: FullType {
300 t: type_,
301 not_null,
302 list_hack: false,
303 },
304 auto_increment,
305 as_: _as,
306 default,
307 generated,
308 }
309}
310
311pub fn parse_schemas<'a>(
328 src: &'a str,
329 issues: &mut Issues<'a>,
330 options: &TypeOptions,
331) -> Schemas<'a> {
332 let statements = parse_statements(src, issues, &options.parse_options);
333
334 let mut schemas = Schemas {
335 schemas: Default::default(),
336 procedures: Default::default(),
337 functions: Default::default(),
338 indices: Default::default(),
339 };
340
341 for statement in statements {
342 match statement {
343 qusql_parse::Statement::CreateTable(t) => {
344 let mut replace = false;
345
346 let id = unqualified_name(issues, &t.identifier);
347
348 let mut schema = Schema {
349 view: false,
350 identifier_span: id.span.clone(),
351 columns: Default::default(),
352 };
353
354 for o in t.create_options {
355 match o {
356 qusql_parse::CreateOption::OrReplace(_) => {
357 replace = true;
358 }
359 qusql_parse::CreateOption::Temporary { temporary_span, .. } => {
360 issues.err("Not supported", &temporary_span);
361 }
362 qusql_parse::CreateOption::Materialized(s) => {
363 issues.err("Not supported", &s);
364 }
365 qusql_parse::CreateOption::Concurrently(s) => {
366 issues.err("Not supported", &s);
367 }
368 qusql_parse::CreateOption::Unique(s) => {
369 issues.err("Not supported", &s);
370 }
371 qusql_parse::CreateOption::Algorithm(_, _) => {}
372 qusql_parse::CreateOption::Definer { .. } => {}
373 qusql_parse::CreateOption::SqlSecurityDefiner(_, _) => {}
374 qusql_parse::CreateOption::SqlSecurityUser(_, _) => {}
375 qusql_parse::CreateOption::SqlSecurityInvoker(_, _) => {}
376 }
377 }
378 for d in t.create_definitions {
380 match d {
381 qusql_parse::CreateDefinition::ColumnDefinition {
382 identifier,
383 data_type,
384 } => {
385 let column =
386 parse_column(data_type, identifier.clone(), issues, Some(options));
387 if let Some(oc) = schema.get_column(column.identifier.value) {
388 issues
389 .err("Column already defined", &identifier)
390 .frag("Defined here", &oc.identifier);
391 } else {
392 schema.columns.push(column);
393 }
394 }
395 qusql_parse::CreateDefinition::IndexDefinition { .. } => {}
396 qusql_parse::CreateDefinition::ForeignKeyDefinition { .. } => {}
397 qusql_parse::CreateDefinition::CheckConstraintDefinition { .. } => {}
398 }
399 }
400 match schemas.schemas.entry(id.clone()) {
401 alloc::collections::btree_map::Entry::Occupied(mut e) => {
402 if replace {
403 e.insert(schema);
404 } else if t.if_not_exists.is_none() {
405 issues
406 .err("Table already defined", &t.identifier)
407 .frag("Defined here", &e.get().identifier_span);
408 }
409 }
410 alloc::collections::btree_map::Entry::Vacant(e) => {
411 e.insert(schema);
412 }
413 }
414 }
415 qusql_parse::Statement::CreateView(v) => {
416 let mut replace = false;
417 let mut schema = Schema {
418 view: true,
419 identifier_span: v.name.span(),
420 columns: Default::default(),
421 };
422 for o in v.create_options {
423 match o {
424 qusql_parse::CreateOption::OrReplace(_) => {
425 replace = true;
426 }
427 qusql_parse::CreateOption::Temporary { temporary_span, .. } => {
428 issues.err("Not supported", &temporary_span);
429 }
430 qusql_parse::CreateOption::Materialized(s) => {
431 issues.err("Not supported", &s);
432 }
433 qusql_parse::CreateOption::Concurrently(s) => {
434 issues.err("Not supported", &s);
435 }
436 qusql_parse::CreateOption::Unique(s) => {
437 issues.err("Not supported", &s);
438 }
439 qusql_parse::CreateOption::Algorithm(_, _) => {}
440 qusql_parse::CreateOption::Definer { .. } => {}
441 qusql_parse::CreateOption::SqlSecurityDefiner(_, _) => {}
442 qusql_parse::CreateOption::SqlSecurityUser(_, _) => {}
443 qusql_parse::CreateOption::SqlSecurityInvoker(_, _) => {}
444 }
445 }
446
447 {
448 let mut typer: crate::typer::Typer<'a, '_> = crate::typer::Typer {
449 schemas: &schemas,
450 issues,
451 reference_types: Vec::new(),
452 arg_types: Default::default(),
453 options,
454 with_schemas: Default::default(),
455 };
456
457 let t = type_statement::type_statement(&mut typer, &v.select);
458 let s = if let type_statement::InnerStatementType::Select(s) = t {
459 s
460 } else {
461 issues.err("Not supported", &v.select.span());
462 continue;
463 };
464
465 for column in s.columns {
466 let name = column.name.unwrap();
468
469 schema.columns.push(Column {
470 identifier: name,
471 type_: column.type_,
472 auto_increment: false,
473 default: false,
474 as_: None,
475 generated: false,
476 });
477 }
478 }
479
480 match schemas
481 .schemas
482 .entry(unqualified_name(issues, &v.name).clone())
483 {
484 alloc::collections::btree_map::Entry::Occupied(mut e) => {
485 if replace {
486 e.insert(schema);
487 } else if v.if_not_exists.is_none() {
488 issues
489 .err("View already defined", &v.name)
490 .frag("Defined here", &e.get().identifier_span);
491 }
492 }
493 alloc::collections::btree_map::Entry::Vacant(e) => {
494 e.insert(schema);
495 }
496 }
497 }
498 qusql_parse::Statement::CreateTrigger(_) => {}
499 qusql_parse::Statement::DropTable(t) => {
505 for i in t.tables {
506 match schemas.schemas.entry(unqualified_name(issues, &i).clone()) {
507 alloc::collections::btree_map::Entry::Occupied(e) => {
508 if e.get().view {
509 issues
510 .err("Name defines a view not a table", &i)
511 .frag("View defined here", &e.get().identifier_span);
512 } else {
513 e.remove();
514 }
515 }
516 alloc::collections::btree_map::Entry::Vacant(_) => {
517 if t.if_exists.is_none() {
518 issues.err("A table with this name does not exist to drop", &i);
519 }
520 }
521 }
522 }
523 }
524 qusql_parse::Statement::DropFunction(f) => {
525 for (func_name, _args) in &f.functions {
526 match schemas
527 .functions
528 .entry(unqualified_name(issues, func_name).clone())
529 {
530 alloc::collections::btree_map::Entry::Occupied(e) => {
531 e.remove();
532 }
533 alloc::collections::btree_map::Entry::Vacant(_) => {
534 if f.if_exists.is_none() {
535 issues.err(
536 "A function with this name does not exist to drop",
537 func_name,
538 );
539 }
540 }
541 }
542 }
543 }
544 qusql_parse::Statement::DropProcedure(p) => {
545 match schemas
546 .procedures
547 .entry(unqualified_name(issues, &p.procedure).clone())
548 {
549 alloc::collections::btree_map::Entry::Occupied(e) => {
550 e.remove();
551 }
552 alloc::collections::btree_map::Entry::Vacant(_) => {
553 if p.if_exists.is_none() {
554 issues.err(
555 "A procedure with this name does not exist to drop",
556 &p.procedure,
557 );
558 }
559 }
560 }
561 }
562 qusql_parse::Statement::DropDatabase(_) => {}
564 qusql_parse::Statement::DropServer(_) => {}
565 qusql_parse::Statement::DropTrigger(_) => {}
566 qusql_parse::Statement::DropView(v) => {
567 for i in v.views {
568 match schemas.schemas.entry(unqualified_name(issues, &i).clone()) {
569 alloc::collections::btree_map::Entry::Occupied(e) => {
570 if !e.get().view {
571 issues
572 .err("Name defines a table not a view", &i)
573 .frag("Table defined here", &e.get().identifier_span);
574 } else {
575 e.remove();
576 }
577 }
578 alloc::collections::btree_map::Entry::Vacant(_) => {
579 if v.if_exists.is_none() {
580 issues.err("A view with this name does not exist to drop", &i);
581 }
582 }
583 }
584 }
585 }
586 qusql_parse::Statement::Set(_) => {}
587 qusql_parse::Statement::AlterTable(a) => {
588 let e = match schemas
589 .schemas
590 .entry(unqualified_name(issues, &a.table).clone())
591 {
592 alloc::collections::btree_map::Entry::Occupied(e) => {
593 let e = e.into_mut();
594 if e.view {
595 issues.err("Cannot alter view", &a.table);
596 continue;
597 }
598 e
599 }
600 alloc::collections::btree_map::Entry::Vacant(_) => {
601 if a.if_exists.is_none() {
602 issues.err("Table not found", &a.table);
603 }
604 continue;
605 }
606 };
607 for s in a.alter_specifications {
608 match s {
609 qusql_parse::AlterSpecification::AddIndex(AddIndex {
610 if_not_exists,
611 name,
612 cols,
613 ..
614 }) => {
615 for col in &cols {
616 if let qusql_parse::IndexColExpr::Column(name) = &col.expr
618 && e.get_column(name.value).is_none()
619 {
620 issues
621 .err("No such column in table", col)
622 .frag("Table defined here", &a.table);
623 }
624 }
625
626 if let Some(name) = &name {
627 let ident = if options.parse_options.get_dialect().is_postgresql() {
628 IndexKey {
629 table: None,
630 index: name.clone(),
631 }
632 } else {
633 IndexKey {
634 table: Some(unqualified_name(issues, &a.table).clone()),
635 index: name.clone(),
636 }
637 };
638
639 if let Some(old) = schemas.indices.insert(ident, name.span())
640 && if_not_exists.is_none()
641 {
642 issues
643 .err(
644 "Multiple indeces with the same identifier",
645 &name.span(),
646 )
647 .frag("Already defined here", &old);
648 }
649 }
650 }
651 qusql_parse::AlterSpecification::AddForeignKey { .. } => {}
652 qusql_parse::AlterSpecification::Modify(ModifyColumn {
653 if_exists,
654 col,
655 definition,
656 ..
657 }) => {
658 let c = match e.get_column_mut(col.value) {
659 Some(v) => v,
660 None => {
661 if if_exists.is_none() {
662 issues
663 .err("No such column in table", &col)
664 .frag("Table defined here", &e.identifier_span);
665 }
666 continue;
667 }
668 };
669 *c = parse_column(
670 definition,
671 c.identifier.clone(),
672 issues,
673 Some(options),
674 );
675 }
676 qusql_parse::AlterSpecification::AddColumn(AddColumn {
677 identifier,
678 data_type,
679 ..
680 }) => {
681 e.columns.push(parse_column(
682 data_type,
683 identifier,
684 issues,
685 Some(options),
686 ));
687 }
688 qusql_parse::AlterSpecification::OwnerTo { .. } => {}
689 qusql_parse::AlterSpecification::DropColumn(DropColumn {
690 column, ..
691 }) => {
692 let cnt = e.columns.len();
693 e.columns.retain(|c| c.identifier != column);
694 if cnt == e.columns.len() {
695 issues
696 .err("No such column in table", &column)
697 .frag("Table defined here", &e.identifier_span);
698 }
699 }
700 qusql_parse::AlterSpecification::AlterColumn(AlterColumn {
701 column,
702 alter_column_action,
703 ..
704 }) => {
705 let c = match e.get_column_mut(column.value) {
706 Some(v) => v,
707 None => {
708 issues
709 .err("No such column in table", &column)
710 .frag("Table defined here", &e.identifier_span);
711 continue;
712 }
713 };
714 match alter_column_action {
715 qusql_parse::AlterColumnAction::SetDefault { .. } => (),
716 qusql_parse::AlterColumnAction::DropDefault { .. } => (),
717 qusql_parse::AlterColumnAction::Type { type_, .. } => {
718 *c = parse_column(type_, column, issues, Some(options))
719 }
720 qusql_parse::AlterColumnAction::SetNotNull { .. } => {
721 c.type_.not_null = true
722 }
723 qusql_parse::AlterColumnAction::DropNotNull { .. } => {
724 c.type_.not_null = false
725 }
726 a @ qusql_parse::AlterColumnAction::AddGenerated { .. } => {
727 issues.err("not implemented", &a);
728 }
729 }
730 }
731 s @ qusql_parse::AlterSpecification::Lock { .. } => {
732 issues.err(
733 alloc::format!("Unsupported statement {s:?} in schema definition"),
734 &s,
735 );
736 }
737 s @ qusql_parse::AlterSpecification::DropIndex { .. } => {
738 issues.err(
739 alloc::format!("Unsupported statement {s:?} in schema definition"),
740 &s,
741 );
742 }
743 s @ qusql_parse::AlterSpecification::DropForeignKey { .. } => {
744 issues.err(
745 alloc::format!("Unsupported statement {s:?} in schema definition"),
746 &s,
747 );
748 }
749 s @ qusql_parse::AlterSpecification::DropPrimaryKey { .. } => {
750 issues.err(
751 alloc::format!("Unsupported statement {s:?} in schema definition"),
752 &s,
753 );
754 }
755 s @ qusql_parse::AlterSpecification::RenameColumn { .. } => {
756 issues.err(
757 alloc::format!("Unsupported statement {s:?} in schema definition"),
758 &s,
759 );
760 }
761 s @ qusql_parse::AlterSpecification::RenameIndex { .. } => {
762 issues.err(
763 alloc::format!("Unsupported statement {s:?} in schema definition"),
764 &s,
765 );
766 }
767 s @ qusql_parse::AlterSpecification::RenameConstraint { .. } => {
768 issues.err(
769 alloc::format!("Unsupported statement {s:?} in schema definition"),
770 &s,
771 );
772 }
773 s @ qusql_parse::AlterSpecification::RenameTo { .. } => {
774 issues.err(
775 alloc::format!("Unsupported statement {s:?} in schema definition"),
776 &s,
777 );
778 }
779 s @ qusql_parse::AlterSpecification::Algorithm { .. } => {
780 issues.err(
781 alloc::format!("Unsupported statement {s:?} in schema definition"),
782 &s,
783 );
784 }
785 s @ qusql_parse::AlterSpecification::AutoIncrement { .. } => {
786 issues.err(
787 alloc::format!("Unsupported statement {s:?} in schema definition"),
788 &s,
789 );
790 }
791 s @ qusql_parse::AlterSpecification::Change { .. } => {
792 issues.err(
793 alloc::format!("Unsupported statement {s:?} in schema definition"),
794 &s,
795 );
796 }
797 s @ qusql_parse::AlterSpecification::ReplicaIdentity(_) => {
798 issues.err("Not supported", &s);
799 }
800 s @ qusql_parse::AlterSpecification::ValidateConstraint(_) => {
801 issues.err("Not supported", &s);
802 }
803 s @ qusql_parse::AlterSpecification::AddTableConstraint(_) => {
804 issues.err("Not supported", &s);
805 }
806 s @ qusql_parse::AlterSpecification::DisableTrigger(_) => {
807 issues.err("Not supported", &s);
808 }
809 s @ qusql_parse::AlterSpecification::EnableTrigger(_) => {
810 issues.err("Not supported", &s);
811 }
812 s @ qusql_parse::AlterSpecification::DisableRule(_) => {
813 issues.err("Not supported", &s);
814 }
815 s @ qusql_parse::AlterSpecification::EnableRule(_) => {
816 issues.err("Not supported", &s);
817 }
818 s @ qusql_parse::AlterSpecification::DisableRowLevelSecurity(_) => {
819 issues.err("Not supported", &s);
820 }
821 s @ qusql_parse::AlterSpecification::EnableRowLevelSecurity(_) => {
822 issues.err("Not supported", &s);
823 }
824 s @ qusql_parse::AlterSpecification::ForceRowLevelSecurity(_) => {
825 issues.err("Not supported", &s);
826 }
827 s @ qusql_parse::AlterSpecification::NoForceRowLevelSecurity(_) => {
828 issues.err("Not supported", &s);
829 }
830 }
831 }
832 }
833 qusql_parse::Statement::Do(_) => {
834 }
836 qusql_parse::Statement::CreateIndex(ci) => {
843 let t = unqualified_name(issues, &ci.table_name);
844
845 if let Some(table) = schemas.schemas.get(t) {
846 for col in &ci.column_names {
847 if let qusql_parse::IndexColExpr::Column(name) = &col.expr
849 && table.get_column(name.value).is_none()
850 {
851 issues
852 .err("No such column in table", col)
853 .frag("Table defined here", &table.identifier_span);
854 }
855 }
856 } else {
858 issues.err("No such table", &ci.table_name);
859 }
860
861 let index_name = match &ci.index_name {
863 Some(name) => name.clone(),
864 None => continue,
865 };
866
867 let ident = if options.parse_options.get_dialect().is_postgresql() {
868 IndexKey {
869 table: None,
870 index: index_name.clone(),
871 }
872 } else {
873 IndexKey {
874 table: Some(t.clone()),
875 index: index_name.clone(),
876 }
877 };
878
879 if let Some(old) = schemas.indices.insert(ident, ci.span())
880 && ci.if_not_exists.is_none()
881 {
882 issues
883 .err("Multiple indeces with the same identifier", &ci)
884 .frag("Already defined here", &old);
885 }
886 }
887 qusql_parse::Statement::DropIndex(ci) => {
888 let key = IndexKey {
889 table: ci.on.as_ref().map(|(_, t)| t.identifier.clone()),
890 index: ci.index_name.clone(),
891 };
892 if schemas.indices.remove(&key).is_none() && ci.if_exists.is_none() {
893 issues.err("No such index", &ci);
894 }
895 }
896 qusql_parse::Statement::Commit(_) => (),
897 qusql_parse::Statement::Begin(_) => (),
898 qusql_parse::Statement::CreateFunction(_) => (),
899 qusql_parse::Statement::CreateProcedure(_) => (),
900 qusql_parse::Statement::Call(_) => (),
901 qusql_parse::Statement::DeclareVariable(_) => (),
902 qusql_parse::Statement::DeclareCursorMariaDb(_) => (),
903 qusql_parse::Statement::DeclareHandler(_) => (),
904 qusql_parse::Statement::OpenCursor(_) => (),
905 qusql_parse::Statement::CloseCursor(_) => (),
906 qusql_parse::Statement::FetchCursor(_) => (),
907 qusql_parse::Statement::Leave(_) => (),
908 qusql_parse::Statement::Iterate(_) => (),
909 qusql_parse::Statement::Loop(_) => (),
910 qusql_parse::Statement::While(_) => (),
911 qusql_parse::Statement::Repeat(_) => (),
912 s => {
913 issues.err(
914 alloc::format!("Unsupported statement {s:?} in schema definition"),
915 &s,
916 );
917 }
918 }
919 }
920
921 let dummy_schemas = Schemas::default();
922
923 let mut typer = crate::typer::Typer {
924 schemas: &dummy_schemas,
925 issues,
926 reference_types: Vec::new(),
927 arg_types: Default::default(),
928 options,
929 with_schemas: Default::default(),
930 };
931
932 for (name, schema) in &mut schemas.schemas {
934 if schema.columns.iter().all(|v| v.as_.is_none()) {
935 continue;
936 }
937 typer.reference_types.clear();
938 let mut columns = Vec::new();
939 for c in &schema.columns {
940 columns.push((c.identifier.clone(), c.type_.clone()));
941 }
942 typer.reference_types.push(crate::typer::ReferenceType {
943 name: Some(name.clone()),
944 span: schema.identifier_span.clone(),
945 columns,
946 });
947 for c in &mut schema.columns {
948 if let Some(as_) = &c.as_ {
949 let full_type = crate::type_expression::type_expression(
950 &mut typer,
951 as_,
952 crate::type_expression::ExpressionFlags::default(),
953 BaseType::Any,
954 );
955 c.type_.not_null = full_type.not_null;
956 }
957 }
958 }
959 schemas
960}