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::{DataType, Expression, Identifier, Issues, Span, Spanned, parse_statements};
93
94#[derive(Debug)]
96pub struct Column<'a> {
97 pub identifier: Identifier<'a>,
98 pub type_: FullType<'a>,
100 pub auto_increment: bool,
102 pub default: bool,
103 pub as_: Option<alloc::boxed::Box<Expression<'a>>>,
104 pub generated: bool,
105}
106
107#[derive(Debug)]
109pub struct Schema<'a> {
110 pub identifier_span: Span,
112 pub columns: Vec<Column<'a>>,
114 pub view: bool,
116}
117
118impl<'a> Schema<'a> {
119 pub fn get_column(&self, identifier: &str) -> Option<&Column<'a>> {
120 self.columns
121 .iter()
122 .find(|&column| column.identifier.value == identifier)
123 }
124 pub fn get_column_mut(&mut self, identifier: &str) -> Option<&mut Column<'a>> {
125 self.columns
126 .iter_mut()
127 .find(|column| column.identifier.value == identifier)
128 }
129}
130
131#[derive(Debug)]
133pub struct Procedure {}
134
135#[derive(Debug)]
137pub struct Functions {}
138
139#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
140pub struct IndexKey<'a> {
141 pub table: Option<Identifier<'a>>,
142 pub index: Identifier<'a>,
143}
144
145#[derive(Debug, Default)]
147pub struct Schemas<'a> {
148 pub schemas: BTreeMap<Identifier<'a>, Schema<'a>>,
150 pub procedures: BTreeMap<Identifier<'a>, Procedure>,
152 pub functions: BTreeMap<Identifier<'a>, Functions>,
154 pub indices: BTreeMap<IndexKey<'a>, Span>,
156}
157
158pub(crate) fn parse_column<'a>(
159 data_type: DataType<'a>,
160 identifier: Identifier<'a>,
161 _issues: &mut Issues<'a>,
162 options: Option<&TypeOptions>,
163) -> Column<'a> {
164 let mut not_null = false;
165 let mut unsigned = false;
166 let mut auto_increment = false;
167 let mut default = false;
168 let mut _as = None;
169 let mut generated = false;
170 let mut primary_key = false;
171 let is_sqlite = options
172 .map(|v| v.parse_options.get_dialect().is_sqlite())
173 .unwrap_or_default();
174 for p in data_type.properties {
175 match p {
176 qusql_parse::DataTypeProperty::Signed(_) => unsigned = false,
177 qusql_parse::DataTypeProperty::Unsigned(_) => unsigned = true,
178 qusql_parse::DataTypeProperty::Null(_) => not_null = false,
179 qusql_parse::DataTypeProperty::NotNull(_) => not_null = true,
180 qusql_parse::DataTypeProperty::AutoIncrement(_) => auto_increment = true,
181 qusql_parse::DataTypeProperty::As((_, e)) => _as = Some(e),
182 qusql_parse::DataTypeProperty::Default(_) => default = true,
183 qusql_parse::DataTypeProperty::GeneratedAlways(_) => generated = true,
184 qusql_parse::DataTypeProperty::PrimaryKey(_) => primary_key = true,
185 _ => {}
186 }
187 }
188 let type_ = match data_type.type_ {
189 qusql_parse::Type::TinyInt(v) => {
190 if !unsigned && matches!(v, Some((1, _))) {
191 BaseType::Bool.into()
192 } else if unsigned {
193 Type::U8
194 } else {
195 Type::I8
196 }
197 }
198 qusql_parse::Type::SmallInt(_) => {
199 if unsigned {
200 Type::U16
201 } else {
202 Type::I16
203 }
204 }
205 qusql_parse::Type::Int(_) => {
206 if unsigned {
207 Type::U32
208 } else {
209 Type::I32
210 }
211 }
212 qusql_parse::Type::BigInt(_) => {
213 if unsigned {
214 Type::U64
215 } else {
216 Type::I64
217 }
218 }
219 qusql_parse::Type::Char(_) => BaseType::String.into(),
220 qusql_parse::Type::VarChar(_) => BaseType::String.into(),
221 qusql_parse::Type::TinyText(_) => BaseType::String.into(),
222 qusql_parse::Type::MediumText(_) => BaseType::String.into(),
223 qusql_parse::Type::Text(_) => BaseType::String.into(),
224 qusql_parse::Type::LongText(_) => BaseType::String.into(),
225 qusql_parse::Type::Enum(e) => {
226 Type::Enum(Arc::new(e.into_iter().map(|s| s.value).collect()))
227 }
228 qusql_parse::Type::Set(s) => Type::Set(Arc::new(s.into_iter().map(|s| s.value).collect())),
229 qusql_parse::Type::Float(_) => Type::F32,
230 qusql_parse::Type::Double(_) => Type::F64,
231 qusql_parse::Type::DateTime(_) => BaseType::DateTime.into(),
232 qusql_parse::Type::Timestamp(_) => BaseType::TimeStamp.into(),
233 qusql_parse::Type::Time(_) => BaseType::Time.into(),
234 qusql_parse::Type::TinyBlob(_) => BaseType::Bytes.into(),
235 qusql_parse::Type::MediumBlob(_) => BaseType::Bytes.into(),
236 qusql_parse::Type::Date => BaseType::Date.into(),
237 qusql_parse::Type::Blob(_) => BaseType::Bytes.into(),
238 qusql_parse::Type::LongBlob(_) => BaseType::Bytes.into(),
239 qusql_parse::Type::VarBinary(_) => BaseType::Bytes.into(),
240 qusql_parse::Type::Binary(_) => BaseType::Bytes.into(),
241 qusql_parse::Type::Boolean => BaseType::Bool.into(),
242 qusql_parse::Type::Integer(_) => {
243 if is_sqlite && primary_key {
244 auto_increment = true;
245 }
246 BaseType::Integer.into()
247 }
248 qusql_parse::Type::Float8 => BaseType::Float.into(),
249 qusql_parse::Type::Numeric(_, _, _) => todo!("Numeric"),
250 qusql_parse::Type::Timestamptz => BaseType::TimeStamp.into(),
251 qusql_parse::Type::Json => BaseType::String.into(),
252 qusql_parse::Type::Bit(_, _) => BaseType::Bytes.into(),
253 qusql_parse::Type::Bytea => BaseType::Bytes.into(),
254 qusql_parse::Type::Named(_) => BaseType::String.into(), qusql_parse::Type::Inet4 => BaseType::String.into(),
256 qusql_parse::Type::Inet6 => BaseType::String.into(),
257 };
258
259 Column {
260 identifier,
261 type_: FullType {
262 t: type_,
263 not_null,
264 list_hack: false,
265 },
266 auto_increment,
267 as_: _as,
268 default,
269 generated,
270 }
271}
272
273pub fn parse_schemas<'a>(
290 src: &'a str,
291 issues: &mut Issues<'a>,
292 options: &TypeOptions,
293) -> Schemas<'a> {
294 let statements = parse_statements(src, issues, &options.parse_options);
295
296 let mut schemas = Schemas {
297 schemas: Default::default(),
298 procedures: Default::default(),
299 functions: Default::default(),
300 indices: Default::default(),
301 };
302
303 for statement in statements {
304 match statement {
305 qusql_parse::Statement::CreateTable(t) => {
306 let mut replace = false;
307
308 let id = unqualified_name(issues, &t.identifier);
309
310 let mut schema = Schema {
311 view: false,
312 identifier_span: id.span.clone(),
313 columns: Default::default(),
314 };
315
316 for o in t.create_options {
317 match o {
318 qusql_parse::CreateOption::OrReplace(_) => {
319 replace = true;
320 }
321 qusql_parse::CreateOption::Temporary(s) => {
322 issues.err("Not supported", &s);
323 }
324 qusql_parse::CreateOption::Unique(s) => {
325 issues.err("Not supported", &s);
326 }
327 qusql_parse::CreateOption::Algorithm(_, _) => {}
328 qusql_parse::CreateOption::Definer { .. } => {}
329 qusql_parse::CreateOption::SqlSecurityDefiner(_, _) => {}
330 qusql_parse::CreateOption::SqlSecurityUser(_, _) => {}
331 }
332 }
333 for d in t.create_definitions {
335 match d {
336 qusql_parse::CreateDefinition::ColumnDefinition {
337 identifier,
338 data_type,
339 } => {
340 let column =
341 parse_column(data_type, identifier.clone(), issues, Some(options));
342 if let Some(oc) = schema.get_column(column.identifier.value) {
343 issues
344 .err("Column already defined", &identifier)
345 .frag("Defined here", &oc.identifier);
346 } else {
347 schema.columns.push(column);
348 }
349 }
350 qusql_parse::CreateDefinition::ConstraintDefinition { .. } => {}
351 }
352 }
353 match schemas.schemas.entry(id.clone()) {
354 alloc::collections::btree_map::Entry::Occupied(mut e) => {
355 if replace {
356 e.insert(schema);
357 } else if t.if_not_exists.is_none() {
358 issues
359 .err("Table already defined", &t.identifier)
360 .frag("Defined here", &e.get().identifier_span);
361 }
362 }
363 alloc::collections::btree_map::Entry::Vacant(e) => {
364 e.insert(schema);
365 }
366 }
367 }
368 qusql_parse::Statement::CreateView(v) => {
369 let mut replace = false;
370 let mut schema = Schema {
371 view: true,
372 identifier_span: v.name.span(),
373 columns: Default::default(),
374 };
375 for o in v.create_options {
376 match o {
377 qusql_parse::CreateOption::OrReplace(_) => {
378 replace = true;
379 }
380 qusql_parse::CreateOption::Temporary(s) => {
381 issues.err("Not supported", &s);
382 }
383 qusql_parse::CreateOption::Unique(s) => {
384 issues.err("Not supported", &s);
385 }
386 qusql_parse::CreateOption::Algorithm(_, _) => {}
387 qusql_parse::CreateOption::Definer { .. } => {}
388 qusql_parse::CreateOption::SqlSecurityDefiner(_, _) => {}
389 qusql_parse::CreateOption::SqlSecurityUser(_, _) => {}
390 }
391 }
392
393 {
394 let mut typer: crate::typer::Typer<'a, '_> = crate::typer::Typer {
395 schemas: &schemas,
396 issues,
397 reference_types: Vec::new(),
398 arg_types: Default::default(),
399 options,
400 with_schemas: Default::default(),
401 };
402
403 let t = type_statement::type_statement(&mut typer, &v.select);
404 let s = if let type_statement::InnerStatementType::Select(s) = t {
405 s
406 } else {
407 issues.err("Not supported", &v.select.span());
408 continue;
409 };
410
411 for column in s.columns {
412 let name = column.name.unwrap();
414
415 schema.columns.push(Column {
416 identifier: name,
417 type_: column.type_,
418 auto_increment: false,
419 default: false,
420 as_: None,
421 generated: false,
422 });
423 }
424 }
425
426 match schemas
427 .schemas
428 .entry(unqualified_name(issues, &v.name).clone())
429 {
430 alloc::collections::btree_map::Entry::Occupied(mut e) => {
431 if replace {
432 e.insert(schema);
433 } else if v.if_not_exists.is_none() {
434 issues
435 .err("View already defined", &v.name)
436 .frag("Defined here", &e.get().identifier_span);
437 }
438 }
439 alloc::collections::btree_map::Entry::Vacant(e) => {
440 e.insert(schema);
441 }
442 }
443 }
444 qusql_parse::Statement::CreateTrigger(_) => {}
445 qusql_parse::Statement::DropTable(t) => {
451 for i in t.tables {
452 match schemas.schemas.entry(unqualified_name(issues, &i).clone()) {
453 alloc::collections::btree_map::Entry::Occupied(e) => {
454 if e.get().view {
455 issues
456 .err("Name defines a view not a table", &i)
457 .frag("View defined here", &e.get().identifier_span);
458 } else {
459 e.remove();
460 }
461 }
462 alloc::collections::btree_map::Entry::Vacant(_) => {
463 if t.if_exists.is_none() {
464 issues.err("A table with this name does not exist to drop", &i);
465 }
466 }
467 }
468 }
469 }
470 qusql_parse::Statement::DropFunction(f) => {
471 match schemas
472 .functions
473 .entry(unqualified_name(issues, &f.function).clone())
474 {
475 alloc::collections::btree_map::Entry::Occupied(e) => {
476 e.remove();
477 }
478 alloc::collections::btree_map::Entry::Vacant(_) => {
479 if f.if_exists.is_none() {
480 issues.err(
481 "A function with this name does not exist to drop",
482 &f.function,
483 );
484 }
485 }
486 }
487 }
488 qusql_parse::Statement::DropProcedure(p) => {
489 match schemas
490 .procedures
491 .entry(unqualified_name(issues, &p.procedure).clone())
492 {
493 alloc::collections::btree_map::Entry::Occupied(e) => {
494 e.remove();
495 }
496 alloc::collections::btree_map::Entry::Vacant(_) => {
497 if p.if_exists.is_none() {
498 issues.err(
499 "A procedure with this name does not exist to drop",
500 &p.procedure,
501 );
502 }
503 }
504 }
505 }
506 qusql_parse::Statement::DropDatabase(_) => {}
508 qusql_parse::Statement::DropServer(_) => {}
509 qusql_parse::Statement::DropTrigger(_) => {}
510 qusql_parse::Statement::DropView(v) => {
511 for i in v.views {
512 match schemas.schemas.entry(unqualified_name(issues, &i).clone()) {
513 alloc::collections::btree_map::Entry::Occupied(e) => {
514 if !e.get().view {
515 issues
516 .err("Name defines a table not a view", &i)
517 .frag("Table defined here", &e.get().identifier_span);
518 } else {
519 e.remove();
520 }
521 }
522 alloc::collections::btree_map::Entry::Vacant(_) => {
523 if v.if_exists.is_none() {
524 issues.err("A view with this name does not exist to drop", &i);
525 }
526 }
527 }
528 }
529 }
530 qusql_parse::Statement::Set(_) => {}
531 qusql_parse::Statement::AlterTable(a) => {
532 let e = match schemas
533 .schemas
534 .entry(unqualified_name(issues, &a.table).clone())
535 {
536 alloc::collections::btree_map::Entry::Occupied(e) => {
537 let e = e.into_mut();
538 if e.view {
539 issues.err("Cannot alter view", &a.table);
540 continue;
541 }
542 e
543 }
544 alloc::collections::btree_map::Entry::Vacant(_) => {
545 if a.if_exists.is_none() {
546 issues.err("Table not found", &a.table);
547 }
548 continue;
549 }
550 };
551 for s in a.alter_specifications {
552 match s {
553 qusql_parse::AlterSpecification::AddIndex {
554 if_not_exists,
555 name,
556 cols,
557 ..
558 } => {
559 for col in &cols {
560 if e.get_column(&col.name).is_none() {
561 issues
562 .err("No such column in table", col)
563 .frag("Table defined here", &a.table);
564 }
565 }
566
567 if let Some(name) = &name {
568 let ident = if options.parse_options.get_dialect().is_postgresql() {
569 IndexKey {
570 table: None,
571 index: name.clone(),
572 }
573 } else {
574 IndexKey {
575 table: Some(unqualified_name(issues, &a.table).clone()),
576 index: name.clone(),
577 }
578 };
579
580 if let Some(old) = schemas.indices.insert(ident, name.span())
581 && if_not_exists.is_none()
582 {
583 issues
584 .err(
585 "Multiple indeces with the same identifier",
586 &name.span(),
587 )
588 .frag("Already defined here", &old);
589 }
590 }
591 }
592 qusql_parse::AlterSpecification::AddForeignKey { .. } => {}
593 qusql_parse::AlterSpecification::Modify {
594 if_exists,
595 col,
596 definition,
597 ..
598 } => {
599 let c = match e.get_column_mut(col.value) {
600 Some(v) => v,
601 None => {
602 if if_exists.is_none() {
603 issues
604 .err("No such column in table", &col)
605 .frag("Table defined here", &e.identifier_span);
606 }
607 continue;
608 }
609 };
610 *c = parse_column(
611 definition,
612 c.identifier.clone(),
613 issues,
614 Some(options),
615 );
616 }
617 qusql_parse::AlterSpecification::AddColumn {
618 identifier,
619 data_type,
620 ..
621 } => {
622 e.columns.push(parse_column(
623 data_type,
624 identifier,
625 issues,
626 Some(options),
627 ));
628 }
629 qusql_parse::AlterSpecification::OwnerTo { .. } => {}
630 qusql_parse::AlterSpecification::DropColumn { column, .. } => {
631 let cnt = e.columns.len();
632 e.columns.retain(|c| c.identifier != column);
633 if cnt == e.columns.len() {
634 issues
635 .err("No such column in table", &column)
636 .frag("Table defined here", &e.identifier_span);
637 }
638 }
639 qusql_parse::AlterSpecification::AlterColumn {
640 column,
641 alter_column_action,
642 ..
643 } => {
644 let c = match e.get_column_mut(column.value) {
645 Some(v) => v,
646 None => {
647 issues
648 .err("No such column in table", &column)
649 .frag("Table defined here", &e.identifier_span);
650 continue;
651 }
652 };
653 match alter_column_action {
654 qusql_parse::AlterColumnAction::SetDefault { .. } => (),
655 qusql_parse::AlterColumnAction::DropDefault { .. } => (),
656 qusql_parse::AlterColumnAction::Type { type_, .. } => {
657 *c = parse_column(type_, column, issues, Some(options))
658 }
659 qusql_parse::AlterColumnAction::SetNotNull { .. } => {
660 c.type_.not_null = true
661 }
662 qusql_parse::AlterColumnAction::DropNotNull { .. } => {
663 c.type_.not_null = false
664 }
665 }
666 }
667 }
668 }
669 }
670 qusql_parse::Statement::Do(_) => {
671 }
673 qusql_parse::Statement::CreateIndex(ci) => {
680 let t = unqualified_name(issues, &ci.table_name);
681
682 if let Some(table) = schemas.schemas.get(t) {
683 for col in &ci.column_names {
684 if table.get_column(col).is_none() {
685 issues
686 .err("No such column in table", col)
687 .frag("Table defined here", &table.identifier_span);
688 }
689 }
690 } else {
692 issues.err("No such table", &ci.table_name);
693 }
694
695 let ident = if options.parse_options.get_dialect().is_postgresql() {
696 IndexKey {
697 table: None,
698 index: ci.index_name.clone(),
699 }
700 } else {
701 IndexKey {
702 table: Some(t.clone()),
703 index: ci.index_name.clone(),
704 }
705 };
706
707 if let Some(old) = schemas.indices.insert(ident, ci.span())
708 && ci.if_not_exists.is_none()
709 {
710 issues
711 .err("Multiple indeces with the same identifier", &ci)
712 .frag("Already defined here", &old);
713 }
714 }
715 qusql_parse::Statement::DropIndex(ci) => {
716 let key = IndexKey {
717 table: ci.on.as_ref().map(|(_, t)| t.identifier.clone()),
718 index: ci.index_name.clone(),
719 };
720 if schemas.indices.remove(&key).is_none() && ci.if_exists.is_none() {
721 issues.err("No such index", &ci);
722 }
723 }
724 qusql_parse::Statement::Commit(_) => (),
725 qusql_parse::Statement::Begin(_) => (),
726 qusql_parse::Statement::CreateFunction(_) => (),
727 s => {
728 issues.err(
729 alloc::format!("Unsupported statement {s:?} in schema definition"),
730 &s,
731 );
732 }
733 }
734 }
735
736 let dummy_schemas = Schemas::default();
737
738 let mut typer = crate::typer::Typer {
739 schemas: &dummy_schemas,
740 issues,
741 reference_types: Vec::new(),
742 arg_types: Default::default(),
743 options,
744 with_schemas: Default::default(),
745 };
746
747 for (name, schema) in &mut schemas.schemas {
749 if schema.columns.iter().all(|v| v.as_.is_none()) {
750 continue;
751 }
752 typer.reference_types.clear();
753 let mut columns = Vec::new();
754 for c in &schema.columns {
755 columns.push((c.identifier.clone(), c.type_.clone()));
756 }
757 typer.reference_types.push(crate::typer::ReferenceType {
758 name: Some(name.clone()),
759 span: schema.identifier_span.clone(),
760 columns,
761 });
762 for c in &mut schema.columns {
763 if let Some(as_) = &c.as_ {
764 let full_type = crate::type_expression::type_expression(
765 &mut typer,
766 as_,
767 crate::type_expression::ExpressionFlags::default(),
768 BaseType::Any,
769 );
770 c.type_.not_null = full_type.not_null;
771 }
772 }
773 }
774 schemas
775}