1#![cfg_attr(not(test), no_std)]
13#![forbid(unsafe_code)]
14
15extern crate alloc;
91
92use alloc::vec::Vec;
93pub use qusql_parse::{ByteToChar, Fragment, Issue, Issues, Level};
94use qusql_parse::{ParseOptions, parse_statement};
95use schema::Schemas;
96
97mod type_;
98mod type_binary_expression;
99mod type_call;
100mod type_delete;
101mod type_expression;
102mod type_function;
103mod type_insert_replace;
104mod type_lock;
105mod type_reference;
106mod type_select;
107mod type_set;
108mod type_statement;
109mod type_truncate;
110mod type_update;
111mod typer;
112
113pub mod schema;
114pub use type_::{BaseType, FullType, Type};
115pub use type_insert_replace::AutoIncrementId;
116pub use type_select::SelectTypeColumn;
117use typer::Typer;
118
119pub use qusql_parse::{SQLArguments, SQLDialect};
120
121#[derive(Debug, Default, Clone)]
123pub struct TypeOptions {
124 parse_options: ParseOptions,
125 warn_unnamed_column_in_select: bool,
126 warn_duplicate_column_in_select: bool,
127}
128
129impl TypeOptions {
130 pub fn new() -> Self {
132 Default::default()
133 }
134
135 pub fn dialect(self, dialect: SQLDialect) -> Self {
137 Self {
138 parse_options: self.parse_options.dialect(dialect),
139 ..self
140 }
141 }
142
143 pub fn arguments(self, arguments: SQLArguments) -> Self {
145 Self {
146 parse_options: self.parse_options.arguments(arguments),
147 ..self
148 }
149 }
150
151 pub fn warn_unquoted_identifiers(self, warn_unquoted_identifiers: bool) -> Self {
153 Self {
154 parse_options: self
155 .parse_options
156 .warn_unquoted_identifiers(warn_unquoted_identifiers),
157 ..self
158 }
159 }
160
161 pub fn warn_none_capital_keywords(self, warn_none_capital_keywords: bool) -> Self {
163 Self {
164 parse_options: self
165 .parse_options
166 .warn_none_capital_keywords(warn_none_capital_keywords),
167 ..self
168 }
169 }
170
171 pub fn warn_unnamed_column_in_select(self, warn_unnamed_column_in_select: bool) -> Self {
173 Self {
174 warn_unnamed_column_in_select,
175 ..self
176 }
177 }
178
179 pub fn warn_duplicate_column_in_select(self, warn_duplicate_column_in_select: bool) -> Self {
181 Self {
182 warn_duplicate_column_in_select,
183 ..self
184 }
185 }
186
187 pub fn list_hack(self, list_hack: bool) -> Self {
189 Self {
190 parse_options: self.parse_options.list_hack(list_hack),
191 ..self
192 }
193 }
194}
195
196#[derive(Debug, Clone, Hash, PartialEq, Eq)]
198pub enum ArgumentKey<'a> {
199 Index(usize),
201 Identifier(&'a str),
203}
204
205#[derive(Debug, Clone)]
207pub enum StatementType<'a> {
208 Select {
210 columns: Vec<SelectTypeColumn<'a>>,
212 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
214 },
215 Delete {
217 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
219 returning: Option<Vec<SelectTypeColumn<'a>>>,
221 },
222 Insert {
224 yield_autoincrement: AutoIncrementId,
226 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
228 returning: Option<Vec<SelectTypeColumn<'a>>>,
230 },
231 Update {
233 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
235 returning: Option<Vec<SelectTypeColumn<'a>>>,
237 },
238 Replace {
240 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
242 returning: Option<Vec<SelectTypeColumn<'a>>>,
244 },
245 Truncate,
247 Call {
249 arguments: Vec<(ArgumentKey<'a>, FullType<'a>)>,
251 },
252 Transaction,
254 Set,
256 Lock,
258 Invalid,
260}
261
262pub fn type_statement<'a>(
264 schemas: &'a Schemas<'a>,
265 statement: &'a str,
266 issues: &mut Issues<'a>,
267 options: &TypeOptions,
268) -> StatementType<'a> {
269 if let Some(stmt) = parse_statement(statement, issues, &options.parse_options) {
270 let mut typer = Typer {
271 schemas,
272 issues,
273 reference_types: Vec::new(),
274 outer_reference_types: Vec::new(),
275 arg_types: Default::default(),
276 options,
277 with_schemas: Default::default(),
278 };
279 let t = type_statement::type_statement(&mut typer, &stmt);
280 let arguments = typer.arg_types;
281 match t {
282 type_statement::InnerStatementType::Select(s) => StatementType::Select {
283 columns: s.columns,
284 arguments,
285 },
286 type_statement::InnerStatementType::Delete { returning } => StatementType::Delete {
287 arguments,
288 returning: returning.map(|r| r.columns),
289 },
290 type_statement::InnerStatementType::Insert {
291 auto_increment_id,
292 returning,
293 } => StatementType::Insert {
294 yield_autoincrement: auto_increment_id,
295 arguments,
296 returning: returning.map(|r| r.columns),
297 },
298 type_statement::InnerStatementType::Update { returning } => StatementType::Update {
299 arguments,
300 returning: returning.map(|r| r.columns),
301 },
302 type_statement::InnerStatementType::Replace { returning } => StatementType::Replace {
303 arguments,
304 returning: returning.map(|r| r.columns),
305 },
306 type_statement::InnerStatementType::Truncate => StatementType::Truncate,
307 type_statement::InnerStatementType::Call => StatementType::Call { arguments },
308 type_statement::InnerStatementType::Transaction => StatementType::Transaction,
309 type_statement::InnerStatementType::Set => StatementType::Set,
310 type_statement::InnerStatementType::Lock => StatementType::Lock,
311 type_statement::InnerStatementType::Invalid => StatementType::Invalid,
312 }
313 } else {
314 StatementType::Invalid
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use alloc::vec::Vec;
321 use codespan_reporting::{
322 diagnostic::{Diagnostic, Label},
323 files::SimpleFiles,
324 term::{
325 self,
326 termcolor::{ColorChoice, StandardStream},
327 },
328 };
329 use qusql_parse::{Identifier, Issue, Issues, Level, SQLArguments, SQLDialect};
330
331 use crate::{
332 ArgumentKey, AutoIncrementId, BaseType, FullType, SelectTypeColumn, StatementType, Type,
333 TypeOptions, schema::parse_schemas, type_statement,
334 };
335
336 struct N<'a>(Option<&'a str>);
337 impl<'a> alloc::fmt::Display for N<'a> {
338 fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
339 if let Some(v) = self.0 {
340 v.fmt(f)
341 } else {
342 f.write_str("None")
343 }
344 }
345 }
346
347 struct N2<'a>(Option<Identifier<'a>>);
348 impl<'a> alloc::fmt::Display for N2<'a> {
349 fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
350 if let Some(v) = &self.0 {
351 v.fmt(f)
352 } else {
353 f.write_str("None")
354 }
355 }
356 }
357
358 fn check_no_errors(name: &str, src: &str, issues: &[Issue], errors: &mut usize) {
359 let mut files = SimpleFiles::new();
360 let file_id = files.add(name, &src);
361 let writer = StandardStream::stderr(ColorChoice::Always);
362 let config = codespan_reporting::term::Config::default();
363 for issue in issues {
364 let mut labels = vec![Label::primary(file_id, issue.span.clone())];
365 for fragment in &issue.fragments {
366 labels.push(
367 Label::secondary(file_id, fragment.span.clone())
368 .with_message(fragment.message.to_string()),
369 );
370 }
371 let d = match issue.level {
372 Level::Error => {
373 *errors += 1;
374 Diagnostic::error()
375 }
376 Level::Warning => Diagnostic::warning(),
377 };
378 let d = d
379 .with_message(issue.message.to_string())
380 .with_labels(labels);
381 term::emit_to_write_style(&mut writer.lock(), &config, &files, &d).unwrap();
382 }
383 }
384
385 fn str_to_type(t: &str) -> FullType<'static> {
386 let (t, not_null) = if let Some(t) = t.strip_suffix('!') {
387 (t, true)
388 } else {
389 (t, false)
390 };
391 let (t, list_hack) = if let Some(v) = t.strip_suffix("[]") {
392 (v, true)
393 } else {
394 (t, false)
395 };
396 let t = match t {
397 "b" => BaseType::Bool.into(),
398 "u8" => Type::U8,
399 "u16" => Type::U16,
400 "u32" => Type::U32,
401 "u64" => Type::U64,
402 "i8" => Type::I8,
403 "i16" => Type::I16,
404 "i32" => Type::I32,
405 "i64" => Type::I64,
406 "f32" => Type::F32,
407 "f64" => Type::F64,
408 "i" => BaseType::Integer.into(),
409 "f" => BaseType::Float.into(),
410 "str" => BaseType::String.into(),
411 "bytes" => BaseType::Bytes.into(),
412 "dt" => BaseType::DateTime.into(),
413 "date" => BaseType::Date.into(),
414 "ts" => BaseType::TimeStamp.into(),
415 "time" => BaseType::Time.into(),
416 "json" => Type::JSON,
417 "any" => BaseType::Any.into(),
418 _ => panic!("Unknown type {t}"),
419 };
420 let mut t = FullType::new(t, not_null);
421 if list_hack {
422 t.list_hack = true;
423 }
424 t
425 }
426
427 fn check_arguments(
428 name: &str,
429 got: &[(ArgumentKey<'_>, FullType<'_>)],
430 expected: &str,
431 errors: &mut usize,
432 ) {
433 if expected.is_empty() {
434 for (cnt, value) in got.iter().enumerate() {
435 println!("{name}: Unexpected argument {cnt} type {value:?}");
436 *errors += 1;
437 }
438 return;
439 }
440 let mut got2 = Vec::new();
441 let inv = FullType::invalid();
442 for (k, v) in got {
443 match k {
444 ArgumentKey::Index(i) => {
445 while got2.len() <= *i {
446 got2.push(&inv);
447 }
448 got2[*i] = v;
449 }
450 ArgumentKey::Identifier(k) => {
451 println!("{name}: Got named argument {k}");
452 *errors += 1;
453 }
454 }
455 }
456 let mut cnt = 0;
457 for (i, t) in expected.split(',').enumerate() {
458 let t = t.trim();
459 let t = str_to_type(t);
460 if let Some(v) = got2.get(i) {
461 if *v != &t {
462 println!("{name}: Expected type {t} for argument {i} got {v}");
463 *errors += 1;
464 }
465 } else {
466 println!("{name}: Expected type {t} for argument {i} got None");
467 *errors += 1;
468 }
469 cnt += 1;
470 }
471 while cnt < got.len() {
472 println!("{}: Unexpected argument {} type {:?}", name, cnt, got[cnt]);
473 cnt += 1;
474 *errors += 1;
475 }
476 }
477
478 fn check_columns(
479 name: &str,
480 src: &str,
481 got: &[SelectTypeColumn<'_>],
482 expected: &str,
483 errors: &mut usize,
484 ) {
485 let mut cnt = 0;
486 for (i, t) in expected.split(',').enumerate() {
487 let t = t.trim();
488 let (cname, t) = t.split_once(":").unwrap();
489 let t = str_to_type(t);
490 let cname = if cname.is_empty() { None } else { Some(cname) };
491 if let Some(v) = got.get(i) {
492 if v.name.as_deref() != cname || v.type_ != t {
493 let mut files = SimpleFiles::new();
494 let file_id = files.add(name, &src);
495 let writer = StandardStream::stderr(ColorChoice::Always);
496 let config = codespan_reporting::term::Config::default();
497 let d = Diagnostic::error()
498 .with_message(format!(
499 "{}: Expected column {} with name {} of type {} got {} of type {}",
500 name,
501 i,
502 N(cname),
503 t,
504 N2(v.name.clone()),
505 v.type_
506 ))
507 .with_label(Label::primary(file_id, v.span.clone()));
508
509 term::emit_to_write_style(&mut writer.lock(), &config, &files, &d).unwrap();
510
511 *errors += 1;
512 }
513 } else {
514 let mut files = SimpleFiles::new();
515 let file_id = files.add(name, &src);
516 let writer = StandardStream::stderr(ColorChoice::Always);
517 let config = codespan_reporting::term::Config::default();
518 let d = Diagnostic::error()
519 .with_message(format!(
520 "{}: Expected column {} with name {} of type {} got None",
521 name,
522 i,
523 N(cname),
524 t
525 ))
526 .with_label(Label::primary(file_id, 0..src.len()));
527 term::emit_to_write_style(&mut writer.lock(), &config, &files, &d).unwrap();
528 *errors += 1;
529 }
530 cnt += 1;
531 }
532 while cnt < got.len() {
533 println!(
534 "{}: Unexpected column {} with name {} of type {}",
535 name,
536 cnt,
537 N2(got[cnt].name.clone()),
538 got[cnt].type_
539 );
540 cnt += 1;
541 *errors += 1;
542 }
543 }
544
545 #[test]
546 fn mariadb() {
547 let schema_src = "
548
549 DROP TABLE IF EXISTS `t1`;
550 CREATE TABLE `t1` (
551 `id` int(11) NOT NULL,
552 `cbool` tinyint(1) NOT NULL,
553 `cu8` tinyint UNSIGNED NOT NULL,
554 `cu16` smallint UNSIGNED NOT NULL,
555 `cu32` int UNSIGNED NOT NULL,
556 `cu64` bigint UNSIGNED NOT NULL,
557 `ci8` tinyint,
558 `ci16` smallint,
559 `ci32` int,
560 `ci64` bigint,
561 `cbin` binary(16),
562 `ctext` varchar(100) NOT NULL,
563 `cbytes` blob,
564 `cf32` float,
565 `cf64` double,
566 `cu8_plus_one` tinyint UNSIGNED GENERATED ALWAYS AS (
567 `cu8` + 1
568 ) STORED,
569 `status` varchar(10) GENERATED ALWAYS AS (case when `cu8` <> 0 and `cu16` = 0 then 'a' when
570 `cbool` then 'b' when `ci32` = 42 then 'd' when `cu64` = 43 then 'x' when
571 `ci64` = 12 then 'y' else 'z' end) VIRTUAL
572 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
573
574 ALTER TABLE `t1`
575 MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
576
577 DROP INDEX IF EXISTS `hat` ON `t1`;
578
579 CREATE INDEX `hat2` ON `t1` (`id`, `cf64`);
580
581 CREATE TABLE `t2` (
582 `id` int(11) NOT NULL AUTO_INCREMENT,
583 `t1_id` int(11) NOT NULL);
584
585 CREATE TABLE `t3` (
586 `id` int(11) NOT NULL AUTO_INCREMENT,
587 `text` TEXT);
588
589 CREATE TABLE `t4` (
590 `id` int(11) NOT NULL AUTO_INCREMENT,
591 `dt` datetime NOT NULL);
592
593 CREATE TABLE `t5` (
594 `id` int(11) NOT NULL AUTO_INCREMENT,
595 `a` int NOT NULL,
596 `b` int,
597 `c` int NOT NULL DEFAULT 42);
598
599 CREATE TABLE `t6` (
600 `id` int(11) NOT NULL AUTO_INCREMENT,
601 `d` date NOT NULL,
602 `dt` datetime NOT NULL,
603 `t` time NOT NULL);
604
605 CREATE PROCEDURE `p1`(IN `a` int, IN `b` varchar(100))
606 BEGIN
607 END;
608 ";
609
610 let options = TypeOptions::new().dialect(SQLDialect::MariaDB);
611 let mut issues = Issues::new(schema_src);
612 let schema = parse_schemas(schema_src, &mut issues, &options);
613 let mut errors = 0;
614 check_no_errors("schema", schema_src, issues.get(), &mut errors);
615
616 let options = TypeOptions::new()
617 .dialect(SQLDialect::MariaDB)
618 .arguments(SQLArguments::QuestionMark);
619
620 {
621 let name = "q1";
622 let src =
623 "SELECT `id`, `cbool`, `cu8`, `cu8_plus_one`, `cu16`, `cu32`, `cu64`, `ci8`, `ci16`, `ci32`, `ci64`,
624 `ctext`, `cbytes`, `cf32`, `cf64` FROM `t1` WHERE ci8 IS NOT NULL
625 AND `cbool`=? AND `cu8`=? AND `cu16`=? AND `cu32`=? AND `cu64`=?
626 AND `ci8`=? AND `ci16`=? AND `ci32`=? AND `ci64`=?
627 AND `ctext`=? AND `cbytes`=? AND `cf32`=? AND `cf64`=?";
628
629 let mut issues: Issues<'_> = Issues::new(src);
630 let q = type_statement(&schema, src, &mut issues, &options);
631 check_no_errors(name, src, issues.get(), &mut errors);
632 if let StatementType::Select { arguments, columns } = q {
633 check_arguments(
634 name,
635 &arguments,
636 "b,i,i,i,i,i,i,i,i,str,bytes,f,f",
637 &mut errors,
638 );
639 check_columns(
640 name,
641 src,
642 &columns,
643 "id:i32!,cbool:b!,cu8:u8!,cu8_plus_one:u8!,cu16:u16!,cu32:u32!,cu64:u64!,
644 ci8:i8!,ci16:i16!,ci32:i32!,ci64:i64!,ctext:str!,cbytes:bytes!,cf32:f32!,cf64:f64!",
645 &mut errors,
646 );
647 } else {
648 println!("{name} should be select");
649 errors += 1;
650 }
651 }
652
653 {
654 let name = "q1.1";
655 let src =
656 "SELECT `id`, `cbool`, `cu8`, `cu16`, `cu32`, `cu64`, `ci8`, `ci16`, `ci32`, `ci64`,
657 `ctext`, `cbytes`, `cf32`, `cf64`, `cbin` FROM `t1` WHERE ci8 IS NOT NULL";
658
659 let mut issues: Issues<'_> = Issues::new(src);
660 let q = type_statement(&schema, src, &mut issues, &options);
661 check_no_errors(name, src, issues.get(), &mut errors);
662 if let StatementType::Select { arguments, columns } = q {
663 check_arguments(name, &arguments, "", &mut errors);
664 check_columns(
665 name,
666 src,
667 &columns,
668 "id:i32!,cbool:b!,cu8:u8!,cu16:u16!,cu32:u32!,cu64:u64!,
669 ci8:i8!,ci16:i16,ci32:i32,ci64:i64,ctext:str!,cbytes:bytes,cf32:f32,cf64:f64,cbin:bytes",
670 &mut errors,
671 );
672 } else {
673 println!("{name} should be select");
674 errors += 1;
675 }
676 }
677
678 {
679 let name = "q2";
680 let src =
681 "INSERT INTO `t1` (`cbool`, `cu8`, `cu16`, `cu32`, `cu64`, `ci8`, `ci16`, `ci32`, `ci64`,
682 `ctext`, `cbytes`, `cf32`, `cf64`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
683
684 let mut issues: Issues<'_> = Issues::new(src);
685 let q = type_statement(&schema, src, &mut issues, &options);
686 check_no_errors(name, src, issues.get(), &mut errors);
687 if let StatementType::Insert {
688 arguments,
689 yield_autoincrement,
690 returning,
691 } = q
692 {
693 check_arguments(
694 name,
695 &arguments,
696 "b!,u8!,u16!,u32!,u64!,i8,i16,i32,i64,str!,bytes,f32,f64",
697 &mut errors,
698 );
699 if yield_autoincrement != AutoIncrementId::Yes {
700 println!("{name} should yield autoincrement");
701 errors += 1;
702 }
703 if returning.is_some() {
704 println!("{name} should not return columns");
705 errors += 1;
706 }
707 } else {
708 println!("{name} should be insert");
709 errors += 1;
710 }
711 }
712
713 {
714 let name = "q3";
715 let src =
716 "DELETE `t1` FROM `t1`, `t2` WHERE `t1`.`id` = `t2`.`t1_id` AND `t2`.`id` = ?";
717 let mut issues: Issues<'_> = Issues::new(src);
718 let q = type_statement(&schema, src, &mut issues, &options);
719 check_no_errors(name, src, issues.get(), &mut errors);
720 if let StatementType::Delete { arguments, .. } = q {
721 check_arguments(name, &arguments, "i", &mut errors);
722 } else {
723 println!("{name} should be delete");
724 errors += 1;
725 }
726 }
727
728 {
729 let name = "q4";
730 let src = "INSERT INTO `t2` (`t1_id`) VALUES (?) ON DUPLICATE KEY UPDATE `t1_id`=?";
731 let mut issues: Issues<'_> = Issues::new(src);
732 let q = type_statement(&schema, src, &mut issues, &options);
733 check_no_errors(name, src, issues.get(), &mut errors);
734 if let StatementType::Insert {
735 arguments,
736 yield_autoincrement,
737 returning,
738 } = q
739 {
740 check_arguments(name, &arguments, "i32!,i32!", &mut errors);
741 if yield_autoincrement != AutoIncrementId::Optional {
742 println!("{name} should yield optional auto increment");
743 errors += 1;
744 }
745 if returning.is_some() {
746 println!("{name} should not return columns");
747 errors += 1;
748 }
749 } else {
750 println!("{name} should be insert");
751 errors += 1;
752 }
753 }
754
755 {
756 let name = "q5";
757 let src = "INSERT IGNORE INTO `t2` SET `t1_id`=?";
758 let mut issues: Issues<'_> = Issues::new(src);
759 let q = type_statement(&schema, src, &mut issues, &options);
760 check_no_errors(name, src, issues.get(), &mut errors);
761 if let StatementType::Insert {
762 arguments,
763 yield_autoincrement,
764 returning,
765 } = q
766 {
767 check_arguments(name, &arguments, "i32!", &mut errors);
768 if yield_autoincrement != AutoIncrementId::Optional {
769 println!("{name} should yield optional auto increment");
770 errors += 1;
771 }
772 if returning.is_some() {
773 println!("{name} should not return columns");
774 errors += 1;
775 }
776 } else {
777 println!("{name} should be insert");
778 errors += 1;
779 }
780 }
781
782 {
783 let name = "q6";
784 let src = "SELECT IF(`ci32` IS NULL, `cbool`, ?) AS `cc` FROM `t1`";
785 let mut issues: Issues<'_> = Issues::new(src);
786 let q = type_statement(&schema, src, &mut issues, &options);
787 check_no_errors(name, src, issues.get(), &mut errors);
788 if let StatementType::Select { arguments, columns } = q {
789 check_arguments(name, &arguments, "b", &mut errors);
790 check_columns(name, src, &columns, "cc:b", &mut errors);
791 } else {
792 println!("{name} should be select");
793 errors += 1;
794 }
795 }
796
797 {
798 let name = "q7";
799 let src = "SELECT FROM_UNIXTIME(CAST(UNIX_TIMESTAMP() AS DOUBLE)) AS `cc` FROM `t1` WHERE `id`=?";
800 let mut issues: Issues<'_> = Issues::new(src);
801 let q = type_statement(&schema, src, &mut issues, &options);
802 check_no_errors(name, src, issues.get(), &mut errors);
803 if let StatementType::Select { arguments, columns } = q {
804 check_arguments(name, &arguments, "i", &mut errors);
805 check_columns(name, src, &columns, "cc:dt!", &mut errors);
806 } else {
807 println!("{name} should be select");
808 errors += 1;
809 }
810 }
811
812 {
813 let name = "q8";
814 let src = "REPLACE INTO `t2` SET `id` = ?, `t1_id`=?";
815 let mut issues: Issues<'_> = Issues::new(src);
816 let q = type_statement(&schema, src, &mut issues, &options);
817 check_no_errors(name, src, issues.get(), &mut errors);
818 if let StatementType::Replace {
819 arguments,
820 returning,
821 } = q
822 {
823 check_arguments(name, &arguments, "i32!,i32!", &mut errors);
824 if returning.is_some() {
825 println!("{name} should not return columns");
826 errors += 1;
827 }
828 } else {
829 println!("{name} should be replace");
830 errors += 1;
831 }
832 }
833
834 {
835 let name = "q9";
836 let src = "INSERT INTO `t2` (`t1_id`) VALUES (32) ON DUPLICATE KEY UPDATE `t1_id` = `t1_id` + VALUES(`t1_id`)";
837 let mut issues: Issues<'_> = Issues::new(src);
838 let q = type_statement(&schema, src, &mut issues, &options);
839 check_no_errors(name, src, issues.get(), &mut errors);
840 if let StatementType::Insert { arguments, .. } = q {
841 check_arguments(name, &arguments, "", &mut errors);
842 } else {
843 println!("{name} should be insert");
844 errors += 1;
845 }
846 }
847
848 {
849 let name = "q10";
850 let src =
851 "SELECT SUBSTRING_INDEX(`text`, '/', 5) AS `k` FROM `t3` WHERE `text` LIKE '%T%'";
852 let mut issues: Issues<'_> = Issues::new(src);
853 let q = type_statement(&schema, src, &mut issues, &options);
854 check_no_errors(name, src, issues.get(), &mut errors);
855 if let StatementType::Select { arguments, columns } = q {
856 check_arguments(name, &arguments, "", &mut errors);
857 check_columns(name, src, &columns, "k:str!", &mut errors);
858 } else {
859 println!("{name} should be select");
860 errors += 1;
861 }
862 }
863
864 {
865 let name = "q11";
866 let src = "SELECT * FROM `t1`, `t2` LEFT JOIN `t3` ON `t3`.`id` = `t1`.`id`";
867 let mut issues: Issues<'_> = Issues::new(src);
868 type_statement(&schema, src, &mut issues, &options);
869 if !issues.get().iter().any(|i| i.level == Level::Error) {
870 println!("{name} should be an error");
871 errors += 1;
872 }
873 }
874
875 {
876 let name = "q11b";
877 let src = "SELECT 1 FROM `t3` WHERE `text` LIKE '%T%' ESCAPE 'ab'";
878 let mut issues: Issues<'_> = Issues::new(src);
879 type_statement(&schema, src, &mut issues, &options);
880 if !issues.get().iter().any(|i| i.level == Level::Error) {
881 println!("{name} should be an error (ESCAPE must be one character)");
882 errors += 1;
883 }
884 }
885
886 {
887 let name = "q12";
888 let src = "SELECT JSON_REPLACE('{ \"A\": 1, \"B\": [2, 3]}', '$.B[1]', 4, '$.C[3]', 3) AS `k` FROM `t3`";
889 let mut issues: Issues<'_> = Issues::new(src);
890 let q = type_statement(&schema, src, &mut issues, &options);
891 check_no_errors(name, src, issues.get(), &mut errors);
892 if let StatementType::Select { arguments, columns } = q {
893 check_arguments(name, &arguments, "", &mut errors);
894 check_columns(name, src, &columns, "k:json", &mut errors);
895 } else {
896 println!("{name} should be select");
897 errors += 1;
898 }
899 }
900
901 {
902 let options = options.clone().list_hack(true);
903 let name = "q13";
904 let src = "SELECT `id` FROM `t1` WHERE `id` IN (_LIST_)";
905 let mut issues: Issues<'_> = Issues::new(src);
906 let q = type_statement(&schema, src, &mut issues, &options);
907 check_no_errors(name, src, issues.get(), &mut errors);
908 if let StatementType::Select { arguments, columns } = q {
909 check_arguments(name, &arguments, "i[]", &mut errors);
910 check_columns(name, src, &columns, "id:i32!", &mut errors);
911 } else {
912 println!("{name} should be select");
913 errors += 1;
914 }
915 }
916
917 {
918 let name = "q14";
919 let src = "SELECT CAST(NULL AS CHAR) AS `id`";
920 let mut issues: Issues<'_> = Issues::new(src);
921 let q = type_statement(&schema, src, &mut issues, &options);
922 check_no_errors(name, src, issues.get(), &mut errors);
923 if let StatementType::Select { arguments, columns } = q {
924 check_arguments(name, &arguments, "", &mut errors);
925 check_columns(name, src, &columns, "id:str", &mut errors);
926 } else {
927 println!("{name} should be select");
928 errors += 1;
929 }
930 }
931
932 {
933 let name = "q15";
934 let src =
935 "INSERT INTO `t1` (`cbool`, `cu8`, `cu16`, `cu32`, `cu64`, `ci8`, `ci16`, `ci32`, `ci64`,
936 `ctext`, `cbytes`, `cf32`, `cf64`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
937 RETURNING `id`, `cbool`, `cu8`, `ctext`, `cf64`";
938 let mut issues: Issues<'_> = Issues::new(src);
939 let q = type_statement(&schema, src, &mut issues, &options);
940 check_no_errors(name, src, issues.get(), &mut errors);
941 if let StatementType::Insert {
942 arguments,
943 yield_autoincrement,
944 returning,
945 } = q
946 {
947 check_arguments(
948 name,
949 &arguments,
950 "b!,u8!,u16!,u32!,u64!,i8,i16,i32,i64,str!,bytes,f32,f64",
951 &mut errors,
952 );
953 if yield_autoincrement != AutoIncrementId::Yes {
954 println!("{name} should yield autoincrement");
955 errors += 1;
956 }
957 if let Some(returning) = returning {
958 check_columns(
959 name,
960 src,
961 &returning,
962 "id:i32!,cbool:b!,cu8:u8!,ctext:str!,cf64:f64",
963 &mut errors,
964 );
965 } else {
966 println!("{name} should return columns");
967 errors += 1;
968 }
969 } else {
970 println!("{name} should be insert");
971 errors += 1;
972 }
973 }
974
975 {
976 let name = "q16";
977 let src = "REPLACE INTO `t2` SET `id` = ?, `t1_id`=? RETURNING `id`";
978 let mut issues: Issues<'_> = Issues::new(src);
979 let q = type_statement(&schema, src, &mut issues, &options);
980 check_no_errors(name, src, issues.get(), &mut errors);
981 if let StatementType::Replace {
982 arguments,
983 returning,
984 } = q
985 {
986 check_arguments(name, &arguments, "i32!,i32!", &mut errors);
987 if let Some(returning) = returning {
988 check_columns(name, src, &returning, "id:i32!", &mut errors);
989 } else {
990 println!("{name} should return columns");
991 errors += 1;
992 }
993 } else {
994 println!("{name} should be replace");
995 errors += 1;
996 }
997 }
998
999 {
1000 let name = "q17";
1001 let src = "SELECT dt, UNIX_TIMESTAMP(dt) AS t FROM t4";
1002 let mut issues: Issues<'_> = Issues::new(src);
1003 let q = type_statement(&schema, src, &mut issues, &options);
1004 check_no_errors(name, src, issues.get(), &mut errors);
1005 if let StatementType::Select { arguments, columns } = q {
1006 check_arguments(name, &arguments, "", &mut errors);
1007 check_columns(name, src, &columns, "dt:dt!,t:i64!", &mut errors);
1008 } else {
1009 println!("{name} should be select");
1010 errors += 1;
1011 }
1012 }
1013
1014 {
1015 let name = "q17";
1016 let src = "SELECT CONCAT(?, \"hat\") AS c";
1017 let mut issues: Issues<'_> = Issues::new(src);
1018 let q = type_statement(&schema, src, &mut issues, &options);
1019 check_no_errors(name, src, issues.get(), &mut errors);
1020 if let StatementType::Select { arguments, columns } = q {
1021 check_arguments(name, &arguments, "any", &mut errors);
1022 check_columns(name, src, &columns, "c:str", &mut errors);
1023 } else {
1024 println!("{name} should be selsect");
1025 errors += 1;
1026 }
1027 }
1028
1029 {
1030 let name = "q18";
1031 let src = "SELECT CAST(\"::0\" AS INET6) AS `id`";
1032 let mut issues: Issues<'_> = Issues::new(src);
1033 let q = type_statement(&schema, src, &mut issues, &options);
1034 check_no_errors(name, src, issues.get(), &mut errors);
1035 if let StatementType::Select { arguments, columns } = q {
1036 check_arguments(name, &arguments, "", &mut errors);
1037 check_columns(name, src, &columns, "id:str!", &mut errors);
1038 } else {
1039 println!("{name} should be select");
1040 errors += 1;
1041 }
1042 }
1043
1044 {
1045 let name: &str = "q18";
1046 let src = "SELECT SUBSTRING(`cbytes`, 1, 5) AS `k` FROM `t1`";
1047 let mut issues: Issues<'_> = Issues::new(src);
1048 let q = type_statement(&schema, src, &mut issues, &options);
1049 check_no_errors(name, src, issues.get(), &mut errors);
1050 if let StatementType::Select { arguments, columns } = q {
1051 check_arguments(name, &arguments, "", &mut errors);
1052 check_columns(name, src, &columns, "k:bytes", &mut errors);
1053 } else {
1054 println!("{name} should be select");
1055 errors += 1;
1056 }
1057 }
1058
1059 {
1060 let name = "q19";
1061 let src = "SELECT SUBSTRING(`ctext`, 1, 5) AS `k` FROM `t1`";
1062 let mut issues: Issues<'_> = Issues::new(src);
1063 let q = type_statement(&schema, src, &mut issues, &options);
1064 check_no_errors(name, src, issues.get(), &mut errors);
1065 if let StatementType::Select { arguments, columns } = q {
1066 check_arguments(name, &arguments, "", &mut errors);
1067 check_columns(name, src, &columns, "k:str!", &mut errors);
1068 } else {
1069 println!("{name} should be select");
1070 errors += 1;
1071 }
1072 }
1073
1074 {
1075 let name = "q19";
1076 let src = "SELECT SUBSTRING(`ctext`, 1, 5) AS `k` FROM `t1`";
1077 let mut issues: Issues<'_> = Issues::new(src);
1078 let q = type_statement(&schema, src, &mut issues, &options);
1079 check_no_errors(name, src, issues.get(), &mut errors);
1080 if let StatementType::Select { arguments, columns } = q {
1081 check_arguments(name, &arguments, "", &mut errors);
1082 check_columns(name, src, &columns, "k:str!", &mut errors);
1083 } else {
1084 println!("{name} should be select");
1085 errors += 1;
1086 }
1087 }
1088
1089 {
1090 let name = "q20";
1091 let src = "SELECT JSON_QUERY('{ \"A\": 1, \"B\": [2, 3]}', '$.B[1]') AS `k` FROM `t3`";
1092 let mut issues: Issues<'_> = Issues::new(src);
1093 let q = type_statement(&schema, src, &mut issues, &options);
1094 check_no_errors(name, src, issues.get(), &mut errors);
1095 if let StatementType::Select { arguments, columns } = q {
1096 check_arguments(name, &arguments, "", &mut errors);
1097 check_columns(name, src, &columns, "k:json", &mut errors);
1098 } else {
1099 println!("{name} should be select");
1100 errors += 1;
1101 }
1102 }
1103
1104 {
1105 let name = "q21";
1106 let src = "SELECT JSON_REMOVE('{ \"A\": 1, \"B\": [2, 3]}', '$.B[1]', '$.C[3]') AS `k` FROM `t3`";
1107 let mut issues: Issues<'_> = Issues::new(src);
1108 let q = type_statement(&schema, src, &mut issues, &options);
1109 check_no_errors(name, src, issues.get(), &mut errors);
1110 if let StatementType::Select { arguments, columns } = q {
1111 check_arguments(name, &arguments, "", &mut errors);
1112 check_columns(name, src, &columns, "k:json", &mut errors);
1113 } else {
1114 println!("{name} should be select");
1115 errors += 1;
1116 }
1117 }
1118
1119 {
1120 let name = "q22";
1121 let src = "SELECT JSON_OVERLAPS('false', 'false') AS `k` FROM `t3`";
1122 let mut issues: Issues<'_> = Issues::new(src);
1123 let q = type_statement(&schema, src, &mut issues, &options);
1124 check_no_errors(name, src, issues.get(), &mut errors);
1125 if let StatementType::Select { arguments, columns } = q {
1126 check_arguments(name, &arguments, "", &mut errors);
1127 check_columns(name, src, &columns, "k:b!", &mut errors);
1128 } else {
1129 println!("{name} should be select");
1130 errors += 1;
1131 }
1132 }
1133
1134 {
1135 let name = "q23";
1136 let src = "SELECT JSON_OVERLAPS('false', NULL) AS `k` FROM `t3`";
1137 let mut issues: Issues<'_> = Issues::new(src);
1138 let q = type_statement(&schema, src, &mut issues, &options);
1139 check_no_errors(name, src, issues.get(), &mut errors);
1140 if let StatementType::Select { arguments, columns } = q {
1141 check_arguments(name, &arguments, "", &mut errors);
1142 check_columns(name, src, &columns, "k:b", &mut errors);
1143 } else {
1144 println!("{name} should be select");
1145 errors += 1;
1146 }
1147 }
1148
1149 {
1150 let name = "q24";
1151 let src = "SELECT JSON_CONTAINS('{\"A\": 0, \"B\": [\"x\", \"y\"]}', '\"x\"', '$.B') AS `k` FROM `t3`";
1152 let mut issues: Issues<'_> = Issues::new(src);
1153 let q = type_statement(&schema, src, &mut issues, &options);
1154 check_no_errors(name, src, issues.get(), &mut errors);
1155 if let StatementType::Select { arguments, columns } = q {
1156 check_arguments(name, &arguments, "", &mut errors);
1157 check_columns(name, src, &columns, "k:b!", &mut errors);
1158 } else {
1159 println!("{name} should be select");
1160 errors += 1;
1161 }
1162 }
1163
1164 {
1165 let name = "q25";
1166 let src = "SELECT JSON_CONTAINS('{\"A\": 0, \"B\": [\"x\", \"y\"]}', NULL, '$.A') AS `k` FROM `t3`";
1167 let mut issues: Issues<'_> = Issues::new(src);
1168 let q = type_statement(&schema, src, &mut issues, &options);
1169 check_no_errors(name, src, issues.get(), &mut errors);
1170 if let StatementType::Select { arguments, columns } = q {
1171 check_arguments(name, &arguments, "", &mut errors);
1172 check_columns(name, src, &columns, "k:b", &mut errors);
1173 } else {
1174 println!("{name} should be select");
1175 errors += 1;
1176 }
1177 }
1178
1179 {
1180 let name = "q26";
1181 let src = "SELECT `id` FROM `t1` FORCE INDEX (`hat`)";
1182 let mut issues: Issues<'_> = Issues::new(src);
1183 type_statement(&schema, src, &mut issues, &options);
1184 if issues.is_ok() {
1185 println!("{name} should fail");
1186 errors += 1;
1187 }
1188 }
1189
1190 {
1191 let name = "q27";
1192 let src = "SELECT `id` FROM `t1` USE INDEX (`hat2`)";
1193 let mut issues: Issues<'_> = Issues::new(src);
1194 let q = type_statement(&schema, src, &mut issues, &options);
1195 check_no_errors(name, src, issues.get(), &mut errors);
1196 if let StatementType::Select { arguments, columns } = q {
1197 check_arguments(name, &arguments, "", &mut errors);
1198 check_columns(name, src, &columns, "id:i32!", &mut errors);
1199 } else {
1200 println!("{name} should be select");
1201 errors += 1;
1202 }
1203 }
1204
1205 {
1206 let name = "q28";
1207 let src = "INSERT INTO t5 (`a`) VALUES (44)";
1208 check_no_errors(name, src, issues.get(), &mut errors);
1209 }
1210
1211 {
1212 let name = "q29";
1213 let src = "INSERT INTO t5 (`a`, `b`, `c`) VALUES (?, ?)";
1214 let mut issues: Issues<'_> = Issues::new(src);
1215 type_statement(&schema, src, &mut issues, &options);
1216 if issues.is_ok() {
1217 println!("{name} should fail");
1218 errors += 1;
1219 }
1220 }
1221
1222 {
1223 let name = "q30";
1224 let src = "INSERT INTO t5 (`a`, `b`, `c`) VALUES (?, ?, ?)";
1225 check_no_errors(name, src, issues.get(), &mut errors);
1226 }
1227
1228 {
1229 let name = "q31";
1230 let src = "INSERT INTO t5 (`a`, `b`, `c`) VALUES (?, ?, ?, ?)";
1231 let mut issues: Issues<'_> = Issues::new(src);
1232 type_statement(&schema, src, &mut issues, &options);
1233 if issues.is_ok() {
1234 println!("{name} should fail");
1235 errors += 1;
1236 }
1237 }
1238
1239 {
1240 let name = "q32";
1241 let src = "INSERT INTO t5 (`b`, `c`) VALUES (44, 45)";
1242 let mut issues: Issues<'_> = Issues::new(src);
1243 type_statement(&schema, src, &mut issues, &options);
1244 if issues.is_ok() {
1245 println!("{name} should fail");
1246 errors += 1;
1247 }
1248 }
1249
1250 {
1251 let mut t = |expr: &str, t: &str| {
1252 let name = format!("q33 {expr}");
1253 let src = format!("SELECT {expr} AS q FROM t6");
1254 let mut issues: Issues<'_> = Issues::new(&src);
1255 let q = type_statement(&schema, &src, &mut issues, &options);
1256 check_no_errors(&name, &src, issues.get(), &mut errors);
1257 if let StatementType::Select { columns, .. } = q {
1258 check_columns(&name, &src, &columns, &format!("q:{t}"), &mut errors);
1259 } else {
1260 println!("{name} should be select");
1261 errors += 1;
1262 }
1263 };
1264 t("ADD_MONTHS(`d`, 2)", "date!");
1265 t("DATE_ADD(`d`, INTERVAL 31 DAY)", "date");
1266 t("ADDDATE(`d`, 31)", "date");
1267 t("CONVERT_TZ(`dt`, '+00:00','+10:00')", "dt!");
1270 t("CURDATE()", "date!");
1272 t("CURDATE() - INTERVAL 5 DAY", "date!");
1273 t("CURTIME()", "time!");
1275 t("CURTIME()", "time!");
1276 t("DATE('2013-07-18 12:21:32')", "date!");
1277 t("`dt` + INTERVAL 1 SECOND", "dt!");
1278 t("INTERVAL 1 DAY + `d`", "date!");
1279 t("DATE_ADD(`dt`, INTERVAL 1 SECOND)", "dt");
1280 t("DATE_ADD(`dt`, INTERVAL '1:1' MINUTE_SECOND)", "dt");
1281 t("DATE_FORMAT(`dt`, '%D %y %a %d %m %b %j')", "str!");
1282 t("DATE_SUB(`d`, INTERVAL 31 DAY)", "date");
1283 t("DATE_SUB(`dt`, INTERVAL '1 1:1:1' DAY_SECOND)", "dt");
1284 t("DATEDIFF(`dt`, `d`)", "i!");
1285 t("DAYNAME(`d`)", "str!");
1286 t("DAYOFMONTH(`d`)", "i!");
1287 t("DAYOFWEEK(`d`)", "i!");
1288 t("DAYOFYEAR(`d`)", "i!");
1289 t("EXTRACT(DAY_MINUTE FROM `dt`)", "i!");
1290 t("FROM_DAYS(730669)", "date!");
1291 t("FROM_UNIXTIME(1196440219.0)", "dt!");
1292 t(
1294 "FROM_UNIXTIME(1196440219.0, '%Y %D %M %h:%i:%s %x')",
1295 "str!",
1296 );
1297 t("HOUR(`t`)", "i!");
1298 t("LAST_DAY('2004-01-01 01:01:01')", "date!");
1299 t("MAKEDATE(2011,31)", "date");
1300 t("MAKETIME(13,57,33)", "time");
1301 t("MICROSECOND(`dt`)", "i!");
1302 t("MINUTE(`dt`)", "i!");
1303 t("MONTH(`d`)", "i!");
1304 t("MONTHNAME(`dt`)", "str!");
1305 t("NOW()", "dt!");
1306 t("PERIOD_ADD(200801,2)", "i!");
1308 t("PERIOD_DIFF(200802,200703)", "i!");
1309 t("QUARTER(`dt`)", "i!");
1310 t("SEC_TO_TIME(12414)", "time!");
1312 t("SECOND(`dt`)", "i!");
1313 t(
1314 "STR_TO_DATE('Wednesday23423, June 2, 2014', '%W, %M %e, %Y')",
1315 "dt!",
1316 );
1317 t("SYSDATE()", "dt!");
1320 t("TIME('2013-07-18 12:21:32')", "time!");
1321 t("TIME_FORMAT(`t`, '%H %k %h %I %l')", "str!");
1322 t("TIME_TO_SEC(`t`)", "f!");
1323 t(
1324 "TIMEDIFF('2000:01:01 00:00:00', '2000:01:01 00:00:00.000001')",
1325 "time!",
1326 );
1327 t("TIMESTAMP('2003-12-31')", "dt!");
1328 t("TIMESTAMP('2003-12-31 12:00:00','6:30:00')", "dt!");
1329 t("TIMESTAMPADD(MINUTE,1,`d`)", "dt!");
1330 t("TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01')", "i!");
1331 t("TO_DAYS(`d`)", "i!");
1332 t("TO_SECONDS(`dt`)", "i!");
1333 t("UNIX_TIMESTAMP(`dt`)", "i64!");
1334 t("UNIX_TIMESTAMP()", "i64!");
1335 t("UTC_DATE()", "date!");
1337 t("UTC_TIME()", "time!");
1339 t("UTC_TIMESTAMP()", "dt!");
1341 t("WEEK(`d`)", "i!");
1342 t("WEEK(`d`, 3)", "i!");
1343 t("WEEKDAY(`d`)", "i!");
1344 t("YEAR(`d`)", "i!");
1345 t("YEARWEEK(`d`)", "i!");
1346 t("YEARWEEK(`d`, 3)", "i!");
1347 }
1348 {
1349 let name = "q34";
1350 let src = "TRUNCATE TABLE `t1`";
1351 let mut issues: Issues<'_> = Issues::new(src);
1352 let q = type_statement(&schema, src, &mut issues, &options);
1353 check_no_errors(name, src, issues.get(), &mut errors);
1354 if !matches!(q, StatementType::Truncate) {
1355 println!("{name} should be truncate");
1356 errors += 1;
1357 }
1358 }
1359
1360 {
1361 let name = "q35";
1362 let src = "TRUNCATE TABLE `unknown_table`";
1363 let mut issues: Issues<'_> = Issues::new(src);
1364 type_statement(&schema, src, &mut issues, &options);
1365 if issues.is_ok() {
1366 println!("{name} should fail");
1367 errors += 1;
1368 }
1369 }
1370
1371 {
1372 let name = "q36";
1373 let src = "CALL p1(42, 'hello')";
1374 let mut issues: Issues<'_> = Issues::new(src);
1375 let q = type_statement(&schema, src, &mut issues, &options);
1376 check_no_errors(name, src, issues.get(), &mut errors);
1377 if !matches!(q, StatementType::Call { .. }) {
1378 println!("{name} should be call");
1379 errors += 1;
1380 }
1381 }
1382
1383 {
1384 let name = "q37";
1385 let src = "CALL p1(42)";
1386 let mut issues: Issues<'_> = Issues::new(src);
1387 type_statement(&schema, src, &mut issues, &options);
1388 if issues.is_ok() {
1389 println!("{name} should fail (wrong arg count)");
1390 errors += 1;
1391 }
1392 }
1393
1394 {
1395 let name = "q38";
1396 let src = "CALL unknown_proc(1)";
1397 let mut issues: Issues<'_> = Issues::new(src);
1398 type_statement(&schema, src, &mut issues, &options);
1399 if issues.is_ok() {
1400 println!("{name} should fail (unknown procedure)");
1401 errors += 1;
1402 }
1403 }
1404
1405 {
1406 let name = "q39";
1407 let src = "BEGIN";
1408 let mut issues: Issues<'_> = Issues::new(src);
1409 let q = type_statement(&schema, src, &mut issues, &options);
1410 check_no_errors(name, src, issues.get(), &mut errors);
1411 if !matches!(q, StatementType::Transaction) {
1412 println!("{name} should be transaction");
1413 errors += 1;
1414 }
1415 }
1416
1417 {
1418 let name = "q40";
1419 let src = "COMMIT";
1420 let mut issues: Issues<'_> = Issues::new(src);
1421 let q = type_statement(&schema, src, &mut issues, &options);
1422 check_no_errors(name, src, issues.get(), &mut errors);
1423 if !matches!(q, StatementType::Transaction) {
1424 println!("{name} should be transaction");
1425 errors += 1;
1426 }
1427 }
1428
1429 {
1430 let name = "q41";
1431 let src = "SET @var = 42";
1432 let mut issues: Issues<'_> = Issues::new(src);
1433 let q = type_statement(&schema, src, &mut issues, &options);
1434 check_no_errors(name, src, issues.get(), &mut errors);
1435 if !matches!(q, StatementType::Set) {
1436 println!("{name} should be set");
1437 errors += 1;
1438 }
1439 }
1440
1441 {
1442 let name = "q42";
1443 let src = "SET @@session.time_zone = 'UTC'";
1444 let mut issues: Issues<'_> = Issues::new(src);
1445 let q = type_statement(&schema, src, &mut issues, &options);
1446 check_no_errors(name, src, issues.get(), &mut errors);
1447 if !matches!(q, StatementType::Set) {
1448 println!("{name} should be set");
1449 errors += 1;
1450 }
1451 }
1452
1453 {
1454 let name = "q42b";
1455 let src = "SET @@time_zone = '+00:00'";
1456 let mut issues: Issues<'_> = Issues::new(src);
1457 let q = type_statement(&schema, src, &mut issues, &options);
1458 check_no_errors(name, src, issues.get(), &mut errors);
1459 if !matches!(q, StatementType::Set) {
1460 println!("{name} should be set");
1461 errors += 1;
1462 }
1463 }
1464
1465 {
1466 let name = "q43";
1467 let src = "LOCK TABLES `t1` READ";
1468 let mut issues: Issues<'_> = Issues::new(src);
1469 let q = type_statement(&schema, src, &mut issues, &options);
1470 check_no_errors(name, src, issues.get(), &mut errors);
1471 if !matches!(q, StatementType::Lock) {
1472 println!("{name} should be lock");
1473 errors += 1;
1474 }
1475 }
1476
1477 {
1478 let name = "q44";
1479 let src = "LOCK TABLES `unknown_table` WRITE";
1480 let mut issues: Issues<'_> = Issues::new(src);
1481 type_statement(&schema, src, &mut issues, &options);
1482 if issues.is_ok() {
1483 println!("{name} should fail (unknown table)");
1484 errors += 1;
1485 }
1486 }
1487
1488 {
1489 let name = "q45";
1490 let src = "UNLOCK TABLES";
1491 let mut issues: Issues<'_> = Issues::new(src);
1492 let q = type_statement(&schema, src, &mut issues, &options);
1493 check_no_errors(name, src, issues.get(), &mut errors);
1494 if !matches!(q, StatementType::Lock) {
1495 println!("{name} should be lock");
1496 errors += 1;
1497 }
1498 }
1499
1500 if errors != 0 {
1501 panic!("{errors} errors in test");
1502 }
1503 }
1504
1505 #[test]
1506 fn postgresql() {
1507 let schema_src = "
1508 BEGIN;
1509
1510 DO $$ BEGIN
1511 CREATE TYPE my_enum AS ENUM (
1512 'V1',
1513 'V2',
1514 'V3'
1515 );
1516 EXCEPTION
1517 WHEN duplicate_object THEN null;
1518 END $$;
1519
1520 CREATE TABLE IF NOT EXISTS t1 (
1521 id bigint NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
1522 path text NOT NULL UNIQUE,
1523 v my_enum NOT NULL,
1524 time timestamptz NOT NULL DEFAULT now(),
1525 props jsonb NOT NULL DEFAULT '{}',
1526 old_id bigint,
1527 CONSTRAINT t1__old
1528 FOREIGN KEY(old_id)
1529 REFERENCES t1(id)
1530 ON DELETE SET NULL
1531 );
1532
1533 CREATE TABLE IF NOT EXISTS t2 (
1534 id bigint NOT NULL PRIMARY KEY
1535 );
1536
1537 DROP INDEX IF EXISTS t2_index;
1538
1539 CREATE INDEX t2_index2 ON t2 (id);
1540
1541 CREATE TABLE IF NOT EXISTS t3 (
1542 a bigint NOT NULL,
1543 b bigint NOT NULL
1544 );
1545
1546 CREATE UNIQUE INDEX t3u ON t3(a,b);
1547
1548 COMMIT;
1549 ";
1550
1551 let options = TypeOptions::new().dialect(SQLDialect::PostgreSQL);
1552 let mut issues = Issues::new(schema_src);
1553 let schema = parse_schemas(schema_src, &mut issues, &options);
1554 let mut errors = 0;
1555 check_no_errors("schema", schema_src, issues.get(), &mut errors);
1556
1557 let options = TypeOptions::new()
1558 .dialect(SQLDialect::PostgreSQL)
1559 .arguments(SQLArguments::Dollar);
1560
1561 {
1562 let name = "q1";
1563 let src = "INSERT INTO t2 (id) SELECT id FROM t1 WHERE path=$1 ON CONFLICT (id) DO NOTHING RETURNING id";
1564 let mut issues = Issues::new(src);
1565 let q = type_statement(&schema, src, &mut issues, &options);
1566 check_no_errors(name, src, issues.get(), &mut errors);
1567 if let StatementType::Insert {
1568 arguments,
1569 returning,
1570 ..
1571 } = q
1572 {
1573 check_arguments(name, &arguments, "str", &mut errors);
1574 check_columns(
1575 name,
1576 src,
1577 &returning.expect("Returning"),
1578 "id:i64!",
1579 &mut errors,
1580 );
1581 } else {
1582 println!("{name} should be select");
1583 errors += 1;
1584 }
1585 }
1586
1587 {
1588 let name = "q2";
1589 let src = "WITH hat AS (DELETE FROM t1 WHERE old_id=42 RETURNING id) INSERT INTO t2 (id) SELECT id FROM hat";
1590 let mut issues = Issues::new(src);
1591 let q = type_statement(&schema, src, &mut issues, &options);
1592 check_no_errors(name, src, issues.get(), &mut errors);
1593
1594 if let StatementType::Insert { arguments, .. } = q {
1595 check_arguments(name, &arguments, "", &mut errors);
1596 } else {
1597 println!("{name} should be select {q:?}");
1598 errors += 1;
1599 }
1600 }
1601
1602 {
1603 let name = "q3";
1604 let src = "INSERT INTO t1 (path) VALUES ('HI')";
1605 let mut issues: Issues<'_> = Issues::new(src);
1606 type_statement(&schema, src, &mut issues, &options);
1607 if issues.is_ok() {
1608 println!("{name} should fail");
1609 errors += 1;
1610 }
1611 }
1612
1613 {
1614 let name = "q4";
1615 let src = "INSERT INTO t1 (path, v) VALUES ('HI', 'V1')";
1616 let mut issues: Issues<'_> = Issues::new(src);
1617 let q = type_statement(&schema, src, &mut issues, &options);
1618 check_no_errors(name, src, issues.get(), &mut errors);
1619
1620 if let StatementType::Insert { arguments, .. } = q {
1621 check_arguments(name, &arguments, "", &mut errors);
1622 } else {
1623 println!("{name} should be insert {q:?}");
1624 errors += 1;
1625 }
1626 }
1627
1628 {
1629 let name = "q5";
1630 let src = "UPDATE t1 SET path='HI' RETURNING id";
1631 let mut issues: Issues<'_> = Issues::new(src);
1632 let q = type_statement(&schema, src, &mut issues, &options);
1633 if let StatementType::Update {
1634 arguments,
1635 returning,
1636 ..
1637 } = q
1638 {
1639 check_arguments(name, &arguments, "", &mut errors);
1640 if returning.is_none() {
1641 println!("{name} should have returning");
1642 errors += 1;
1643 }
1644 } else {
1645 println!("{name} should be update {q:?}");
1646 errors += 1;
1647 }
1648 }
1649
1650 {
1651 let name = "q6";
1652 let src = "INSERT INTO t3 (a,b) VALUES (1, 1) ON CONFLICT (a,b) DO UPDATE SET a=t3.a, b=EXCLUDED.b WHERE t3.a != EXCLUDED.a";
1653 let mut issues = Issues::new(src);
1654 let q = type_statement(&schema, src, &mut issues, &options);
1655 check_no_errors(name, src, issues.get(), &mut errors);
1656
1657 if let StatementType::Insert { arguments, .. } = q {
1658 check_arguments(name, &arguments, "", &mut errors);
1659 } else {
1660 println!("{name} should be insert {q:?}");
1661 errors += 1;
1662 }
1663 }
1664
1665 {
1666 let name = "q7";
1667 let src = "INSERT INTO t3 (a,b) VALUES (1, 1) ON CONFLICT (a,c) cake DO UPDATE SET a=2";
1668 let mut issues = Issues::new(src);
1669 type_statement(&schema, src, &mut issues, &options);
1670 if issues.is_ok() {
1671 println!("{name} should fail");
1672 errors += 1;
1673 }
1674 }
1675
1676 {
1677 let name = "q8";
1678 let src = "INSERT INTO t3 (a,b) VALUES (1, 1) ON CONFLICT (a,b) t3u DO UPDATE SET a=2 WHERE b=2";
1679 let mut issues = Issues::new(src);
1680 type_statement(&schema, src, &mut issues, &options);
1681 if issues.is_ok() {
1682 println!("{name} should fail");
1683 errors += 1;
1684 }
1685 }
1686
1687 {
1688 let name = "q9";
1689 let src = "SELECT left(path, -4) AS k FROM t1";
1690 let mut issues = Issues::new(src);
1691 let q = type_statement(&schema, src, &mut issues, &options);
1692
1693 check_no_errors(name, src, issues.get(), &mut errors);
1694 if let StatementType::Select { arguments, columns } = q {
1695 check_arguments(name, &arguments, "", &mut errors);
1696 check_columns(name, src, &columns, "k:str!", &mut errors);
1697 } else {
1698 println!("{name} should be select");
1699 errors += 1;
1700 }
1701 }
1702
1703 {
1704 let name = "json_extract";
1705 let src = "SELECT props -> 'a' AS k FROM t1";
1706 let mut issues = Issues::new(src);
1707 let q = type_statement(&schema, src, &mut issues, &options);
1708 check_no_errors(name, src, issues.get(), &mut errors);
1709 if let StatementType::Select { arguments, columns } = q {
1710 check_arguments(name, &arguments, "", &mut errors);
1711 check_columns(name, src, &columns, "k:json", &mut errors);
1714 } else {
1715 println!("{name} should be select");
1716 errors += 1;
1717 }
1718 }
1719
1720 {
1721 let name = "json_extract_text";
1722 let src = "SELECT props ->> 'a' AS k FROM t1";
1723 let mut issues = Issues::new(src);
1724 let q = type_statement(&schema, src, &mut issues, &options);
1725 check_no_errors(name, src, issues.get(), &mut errors);
1726 if let StatementType::Select { arguments, columns } = q {
1727 check_arguments(name, &arguments, "", &mut errors);
1728 check_columns(name, src, &columns, "k:str", &mut errors);
1731 } else {
1732 println!("{name} should be select");
1733 errors += 1;
1734 }
1735 }
1736
1737 {
1738 let name = "json_get_path";
1739 let src = "SELECT props #> '{a,b}' AS k FROM t1";
1740 let mut issues = Issues::new(src);
1741 let q = type_statement(&schema, src, &mut issues, &options);
1742 check_no_errors(name, src, issues.get(), &mut errors);
1743 if let StatementType::Select { arguments, columns } = q {
1744 check_arguments(name, &arguments, "", &mut errors);
1745 check_columns(name, src, &columns, "k:json", &mut errors);
1748 } else {
1749 println!("{name} should be select");
1750 errors += 1;
1751 }
1752 }
1753
1754 {
1755 let name = "json_get_path_text";
1756 let src = "SELECT props #>> '{a,b}' AS k FROM t1";
1757 let mut issues = Issues::new(src);
1758 let q = type_statement(&schema, src, &mut issues, &options);
1759 check_no_errors(name, src, issues.get(), &mut errors);
1760 if let StatementType::Select { arguments, columns } = q {
1761 check_arguments(name, &arguments, "", &mut errors);
1762 check_columns(name, src, &columns, "k:str", &mut errors);
1765 } else {
1766 println!("{name} should be select");
1767 errors += 1;
1768 }
1769 }
1770
1771 {
1772 let name = "pg_set1";
1773 let src = "SET search_path = 'myschema'";
1774 let mut issues = Issues::new(src);
1775 let q = type_statement(&schema, src, &mut issues, &options);
1776 check_no_errors(name, src, issues.get(), &mut errors);
1777 if !matches!(q, StatementType::Set) {
1778 println!("{name} should be set");
1779 errors += 1;
1780 }
1781 }
1782
1783 {
1784 let name = "pg_set2";
1785 let src = "SET @user_var = 42";
1786 let mut issues = Issues::new(src);
1787 type_statement(&schema, src, &mut issues, &options);
1788 if issues.is_ok() {
1789 println!("{name} should fail: @var not valid in PostgreSQL");
1790 errors += 1;
1791 }
1792 }
1793
1794 {
1795 let name = "pg_set3";
1796 let src = "SET @@session.time_zone = 'UTC'";
1797 let mut issues = Issues::new(src);
1798 type_statement(&schema, src, &mut issues, &options);
1799 if issues.is_ok() {
1800 println!("{name} should fail: @@var not valid in PostgreSQL");
1801 errors += 1;
1802 }
1803 }
1804
1805 if errors != 0 {
1806 panic!("{errors} errors in test");
1807 }
1808 }
1809
1810 #[test]
1811 fn sqlite() {
1812 let schema_src = "
1813 CREATE TABLE IF NOT EXISTS `t1` (
1814 `id` INTEGER NOT NULL PRIMARY KEY,
1815 `sid` TEXT NOT NULL) STRICT;
1816 CREATE UNIQUE INDEX IF NOT EXISTS `t1_sid` ON `t1` (`sid`);
1817 ";
1818
1819 let options = TypeOptions::new().dialect(SQLDialect::Sqlite);
1820 let mut issues = Issues::new(schema_src);
1821 let schema = parse_schemas(schema_src, &mut issues, &options);
1822 let mut errors = 0;
1823 check_no_errors("schema", schema_src, issues.get(), &mut errors);
1824
1825 let options = TypeOptions::new()
1826 .dialect(SQLDialect::Sqlite)
1827 .arguments(SQLArguments::QuestionMark);
1828
1829 {
1830 let name = "q1";
1831 let src = "INSERT INTO `t1` (`sid`) VALUES (?)";
1832 let mut issues = Issues::new(src);
1833 let q = type_statement(&schema, src, &mut issues, &options);
1834 check_no_errors(name, src, issues.get(), &mut errors);
1835 if !matches!(q, StatementType::Insert { .. }) {
1836 println!("{name} should be select");
1837 errors += 1;
1838 }
1839 }
1840
1841 if errors != 0 {
1842 panic!("{errors} errors in test");
1843 }
1844 }
1845
1846 #[test]
1848 fn postgres_schema_support() {
1849 let schema_src = "
1850 CREATE SCHEMA myschema;
1851
1852 CREATE TABLE myschema.items (
1853 id bigint NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
1854 name text NOT NULL
1855 );
1856
1857 -- Unqualified name lands in 'public' schema
1858 CREATE TABLE products (
1859 id bigint NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
1860 label text NOT NULL
1861 );
1862 ";
1863
1864 let opts = TypeOptions::new()
1865 .dialect(SQLDialect::PostgreSQL)
1866 .arguments(SQLArguments::Dollar);
1867 let mut issues = Issues::new(schema_src);
1868 let schema = parse_schemas(schema_src, &mut issues, &opts);
1869 let mut errors = 0;
1870 check_no_errors("schema", schema_src, issues.get(), &mut errors);
1871
1872 {
1874 let src = "SELECT id, name FROM myschema.items WHERE id = $1";
1875 let mut issues = Issues::new(src);
1876 let q = type_statement(&schema, src, &mut issues, &opts);
1877 check_no_errors("qualified SELECT", src, issues.get(), &mut errors);
1878 if let StatementType::Select { columns, arguments } = q {
1879 assert_eq!(columns.len(), 2, "expected 2 columns");
1880 assert_eq!(arguments.len(), 1, "expected 1 argument");
1881 } else {
1882 errors += 1;
1883 println!("qualified SELECT: expected Select");
1884 }
1885 }
1886
1887 {
1889 let src = "SELECT id, label FROM products WHERE id = $1";
1890 let mut issues = Issues::new(src);
1891 let q = type_statement(&schema, src, &mut issues, &opts);
1892 check_no_errors("unqualified SELECT", src, issues.get(), &mut errors);
1893 if let StatementType::Select { columns, .. } = q {
1894 assert_eq!(columns.len(), 2, "expected 2 columns");
1895 } else {
1896 errors += 1;
1897 println!("unqualified SELECT: expected Select");
1898 }
1899 }
1900
1901 {
1903 let src = "INSERT INTO myschema.items (name) VALUES ($1)";
1904 let mut issues = Issues::new(src);
1905 let q = type_statement(&schema, src, &mut issues, &opts);
1906 check_no_errors("qualified INSERT", src, issues.get(), &mut errors);
1907 assert!(matches!(q, StatementType::Insert { .. }), "expected Insert");
1908 }
1909
1910 {
1912 let schema_src2 = "
1913 CREATE SCHEMA tmp;
1914 CREATE TABLE tmp.t (id bigint NOT NULL);
1915 DROP SCHEMA tmp;
1916 ";
1917 let mut issues2 = Issues::new(schema_src2);
1918 let s2 = parse_schemas(schema_src2, &mut issues2, &opts);
1919 check_no_errors("drop schema", schema_src2, issues2.get(), &mut errors);
1920 let src = "SELECT id FROM tmp.t";
1922 let mut issues2 = Issues::new(src);
1923 let q = type_statement(&s2, src, &mut issues2, &opts);
1924 assert!(
1925 !issues2.is_ok(),
1926 "expected error: tmp.t should not exist after DROP SCHEMA"
1927 );
1928 let _ = q;
1929 }
1930
1931 if errors != 0 {
1932 panic!("{errors} errors in postgres_schema_support test");
1933 }
1934 }
1935
1936 #[test]
1938 fn mysql_create_schema_fails() {
1939 let opts_maria = TypeOptions::new()
1940 .dialect(SQLDialect::MariaDB)
1941 .arguments(SQLArguments::QuestionMark);
1942
1943 {
1945 let src = "CREATE SCHEMA myschema;";
1946 let mut issues = Issues::new(src);
1947 let _ = parse_schemas(src, &mut issues, &opts_maria);
1948 assert!(
1949 !issues.is_ok(),
1950 "CREATE SCHEMA should produce an error in MySQL"
1951 );
1952 }
1953
1954 {
1956 let src = "CREATE TABLE myschema.items (id int NOT NULL);";
1957 let mut issues = Issues::new(src);
1958 let _ = parse_schemas(src, &mut issues, &opts_maria);
1959 assert!(
1960 !issues.is_ok(),
1961 "schema.table in CREATE TABLE should fail in MySQL"
1962 );
1963 }
1964
1965 {
1967 let tbl_src = "CREATE TABLE items (id int NOT NULL);";
1968 let mut issues = Issues::new(tbl_src);
1969 let schema = parse_schemas(tbl_src, &mut issues, &opts_maria);
1970 assert!(issues.is_ok());
1971
1972 let src = "SELECT id FROM myschema.items";
1973 let mut issues = Issues::new(src);
1974 let _ = type_statement(&schema, src, &mut issues, &opts_maria);
1975 assert!(!issues.is_ok(), "schema.table in FROM should fail in MySQL");
1976 }
1977 }
1978
1979 #[test]
1983 fn postgresql_range_types() {
1984 let schema_src = "
1985 CREATE TABLE reservation (
1986 id bigint NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
1987 during tsrange NOT NULL,
1988 ids int4multirange NOT NULL
1989 );
1990 ";
1991
1992 let opts = TypeOptions::new()
1993 .dialect(SQLDialect::PostgreSQL)
1994 .arguments(SQLArguments::Dollar);
1995 let mut issues = Issues::new(schema_src);
1996 let schema = parse_schemas(schema_src, &mut issues, &opts);
1997 let mut errors = 0;
1998 check_no_errors("schema", schema_src, issues.get(), &mut errors);
1999
2000 let mut check_expr = |src: &'static str, expected: Option<Type<'static>>| {
2003 let mut issues = Issues::new(src);
2004 let q = type_statement(&schema, src, &mut issues, &opts);
2005 let Some(expected) = expected else {
2006 if issues.is_ok() {
2007 println!("{src}: expected a type error");
2008 errors += 1;
2009 }
2010 return;
2011 };
2012 check_no_errors(src, src, issues.get(), &mut errors);
2013 if let StatementType::Select { columns, .. } = q {
2014 match columns.first() {
2015 Some(c) if c.type_.t == expected => {}
2016 Some(c) => {
2017 println!("{src}: expected type {expected} got {}", c.type_.t);
2018 errors += 1;
2019 }
2020 None => {
2021 println!("{src}: expected a column, got none");
2022 errors += 1;
2023 }
2024 }
2025 } else {
2026 println!("{src}: expected Select");
2027 errors += 1;
2028 }
2029 };
2030 check_expr(
2032 "SELECT int4range(1, 10)",
2033 Some(Type::Range(Box::new(Type::I32))),
2034 );
2035 check_expr(
2036 "SELECT int4range(1, 10, '[]')",
2037 Some(Type::Range(Box::new(Type::I32))),
2038 );
2039 check_expr(
2040 "SELECT int8range(1, 10)",
2041 Some(Type::Range(Box::new(Type::I64))),
2042 );
2043 check_expr(
2044 "SELECT numrange(1.1, 2.2)",
2045 Some(Type::Range(Box::new(BaseType::Float.into()))),
2046 );
2047 check_expr(
2048 "SELECT daterange('2010-01-01'::date, '2010-01-02'::date)",
2049 Some(Type::Range(Box::new(BaseType::Date.into()))),
2050 );
2051 check_expr(
2052 "SELECT tstzrange(now(), now())",
2053 Some(Type::Range(Box::new(BaseType::TimeStamp.into()))),
2054 );
2055
2056 check_expr(
2058 "SELECT int4multirange(int4range(1, 2), int4range(3, 4))",
2059 Some(Type::MultiRange(Box::new(Type::I32))),
2060 );
2061 check_expr(
2062 "SELECT int4multirange()",
2063 Some(Type::MultiRange(Box::new(Type::I32))),
2064 );
2065 check_expr(
2066 "SELECT multirange(int4range(1, 2))",
2067 Some(Type::MultiRange(Box::new(Type::I32))),
2068 );
2069
2070 check_expr(
2072 "SELECT int4range(10, 20) @> 3",
2073 Some(Type::Base(BaseType::Bool)),
2074 );
2075 check_expr(
2076 "SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0)",
2077 Some(Type::Base(BaseType::Bool)),
2078 );
2079 check_expr(
2080 "SELECT int8range(1, 10) << int8range(100, 110)",
2081 Some(Type::Base(BaseType::Bool)),
2082 );
2083 check_expr(
2084 "SELECT int8range(50, 60) >> int8range(20, 30)",
2085 Some(Type::Base(BaseType::Bool)),
2086 );
2087 check_expr(
2088 "SELECT int8range(1, 20) &< int8range(18, 20)",
2089 Some(Type::Base(BaseType::Bool)),
2090 );
2091 check_expr(
2092 "SELECT int8range(7, 20) &> int8range(5, 10)",
2093 Some(Type::Base(BaseType::Bool)),
2094 );
2095 check_expr(
2096 "SELECT numrange(1.1, 2.2) -|- numrange(2.2, 3.3)",
2097 Some(Type::Base(BaseType::Bool)),
2098 );
2099 check_expr(
2100 "SELECT numrange(5.0, 15.0) + numrange(10.0, 20.0)",
2101 Some(Type::Range(Box::new(BaseType::Float.into()))),
2102 );
2103 check_expr(
2104 "SELECT int8range(5, 15) * int8range(10, 20)",
2105 Some(Type::Range(Box::new(Type::I64))),
2106 );
2107 check_expr(
2108 "SELECT int8range(5, 15) - int8range(10, 20)",
2109 Some(Type::Range(Box::new(Type::I64))),
2110 );
2111 check_expr("SELECT 1 << 2", Some(Type::Base(BaseType::Integer)));
2113 check_expr("SELECT 1 & 2", Some(Type::Base(BaseType::Integer)));
2114
2115 check_expr("SELECT upper(int8range(15, 25))", Some(Type::I64));
2117 check_expr(
2118 "SELECT lower(numrange(1.1, 2.2))",
2119 Some(Type::Base(BaseType::Float)),
2120 );
2121 check_expr(
2122 "SELECT isempty(numrange(1.0, 5.0))",
2123 Some(Type::Base(BaseType::Bool)),
2124 );
2125 check_expr(
2126 "SELECT lower_inc(numrange(1.1, 2.2))",
2127 Some(Type::Base(BaseType::Bool)),
2128 );
2129 check_expr(
2130 "SELECT upper_inc(numrange(1.1, 2.2))",
2131 Some(Type::Base(BaseType::Bool)),
2132 );
2133 check_expr(
2134 "SELECT lower_inf('(,)'::daterange)",
2135 Some(Type::Base(BaseType::Bool)),
2136 );
2137 check_expr(
2138 "SELECT upper_inf('(,)'::daterange)",
2139 Some(Type::Base(BaseType::Bool)),
2140 );
2141 check_expr(
2142 "SELECT range_merge(int4range(1, 2), int4range(3, 4))",
2143 Some(Type::Range(Box::new(Type::I32))),
2144 );
2145 check_expr(
2146 "SELECT range_merge('{[1,2), [3,4)}'::int4multirange)",
2147 Some(Type::Range(Box::new(Type::I32))),
2148 );
2149 check_expr(
2150 "SELECT unnest('{[1,2), [3,4)}'::int4multirange)",
2151 Some(Type::Range(Box::new(Type::I32))),
2152 );
2153
2154 check_expr(
2157 "SELECT lower(during) FROM reservation",
2158 Some(Type::Base(BaseType::DateTime)),
2159 );
2160 check_expr(
2161 "SELECT r FROM unnest('{[1,2), [3,4)}'::int4multirange) AS u(r)",
2162 Some(Type::Range(Box::new(Type::I32))),
2163 );
2164
2165 check_expr("SELECT int4range(1, 2) + numrange(1.0, 2.0)", None);
2167 check_expr("SELECT int4range(1, 2) + '{[1,2)}'::int4multirange", None);
2168
2169 if errors != 0 {
2170 panic!("{errors} errors in postgresql_range_types test");
2171 }
2172 }
2173
2174 #[test]
2178 fn postgresql_row_in_unnest_subquery() {
2179 let schema_src = "
2180 CREATE TABLE t1 (
2181 id bigint NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
2182 a bigint NOT NULL,
2183 b bigint NOT NULL
2184 );
2185 ";
2186
2187 let opts = TypeOptions::new()
2188 .dialect(SQLDialect::PostgreSQL)
2189 .arguments(SQLArguments::Dollar);
2190 let mut issues = Issues::new(schema_src);
2191 let schema = parse_schemas(schema_src, &mut issues, &opts);
2192 let mut errors = 0;
2193 check_no_errors("schema", schema_src, issues.get(), &mut errors);
2194
2195 let src = "SELECT id, a, b FROM t1
2196 WHERE (a, b) IN (
2197 SELECT * FROM UNNEST($1::bigint[], $2::bigint[])
2198 )";
2199 let mut issues = Issues::new(src);
2200 let q = type_statement(&schema, src, &mut issues, &opts);
2201 check_no_errors("query", src, issues.get(), &mut errors);
2202 if let StatementType::Select { columns, arguments } = q {
2203 if columns.len() != 3 {
2204 println!("query: expected 3 columns, got {}", columns.len());
2205 errors += 1;
2206 }
2207 if arguments.len() != 2 {
2208 println!("query: expected 2 arguments, got {}", arguments.len());
2209 errors += 1;
2210 }
2211 } else {
2212 println!("query: expected Select");
2213 errors += 1;
2214 }
2215
2216 if errors != 0 {
2217 panic!("{errors} errors in postgresql_row_in_unnest_subquery test");
2218 }
2219 }
2220
2221 #[test]
2225 fn postgresql_eq_any_uncast_arg() {
2226 let schema_src = "
2227 CREATE TABLE t1 (
2228 a_id bigint NOT NULL,
2229 b_id bigint NOT NULL
2230 );
2231 CREATE TABLE t2 (
2232 id bigint NOT NULL PRIMARY KEY,
2233 x bigint NOT NULL
2234 );
2235 ";
2236
2237 let opts = TypeOptions::new()
2238 .dialect(SQLDialect::PostgreSQL)
2239 .arguments(SQLArguments::Dollar);
2240 let mut issues = Issues::new(schema_src);
2241 let schema = parse_schemas(schema_src, &mut issues, &opts);
2242 let mut errors = 0;
2243 check_no_errors("schema", schema_src, issues.get(), &mut errors);
2244
2245 let src = "SELECT a_id, b_id, x FROM t1, t2 WHERE a_id = ANY($1) AND b_id = t2.id";
2246 let mut issues = Issues::new(src);
2247 let q = type_statement(&schema, src, &mut issues, &opts);
2248 check_no_errors("query", src, issues.get(), &mut errors);
2249 if let StatementType::Select { columns, arguments } = q {
2250 if columns.len() != 3 {
2251 println!("query: expected 3 columns, got {}", columns.len());
2252 errors += 1;
2253 }
2254 match arguments.first() {
2255 Some((_, t)) if t.t == Type::Array(Box::new(BaseType::Integer.into())) => {}
2256 Some((_, t)) => {
2257 println!("query: expected argument 0 of type integer[] got {}", t.t);
2258 errors += 1;
2259 }
2260 None => {
2261 println!("query: expected an argument, got none");
2262 errors += 1;
2263 }
2264 }
2265 if arguments.len() != 1 {
2266 println!("query: expected 1 argument, got {}", arguments.len());
2267 errors += 1;
2268 }
2269 } else {
2270 println!("query: expected Select");
2271 errors += 1;
2272 }
2273
2274 if errors != 0 {
2275 panic!("{errors} errors in postgresql_eq_any_uncast_arg test");
2276 }
2277 }
2278
2279 #[test]
2282 fn postgresql_insert_unnest_bytea_array() {
2283 let schema_src = "
2284 CREATE TABLE t1 (
2285 a bigint NOT NULL,
2286 b bigint NOT NULL,
2287 payload bytea NOT NULL,
2288 PRIMARY KEY (a, b)
2289 );
2290 ";
2291
2292 let opts = TypeOptions::new()
2293 .dialect(SQLDialect::PostgreSQL)
2294 .arguments(SQLArguments::Dollar);
2295 let mut issues = Issues::new(schema_src);
2296 let schema = parse_schemas(schema_src, &mut issues, &opts);
2297 let mut errors = 0;
2298 check_no_errors("schema", schema_src, issues.get(), &mut errors);
2299
2300 let src = "INSERT INTO t1 (a, b, payload)
2301 SELECT * FROM UNNEST($1::bigint[], $2::bigint[], $3::bytea[])
2302 ON CONFLICT DO NOTHING
2303 RETURNING a, b";
2304 let mut issues = Issues::new(src);
2305 let q = type_statement(&schema, src, &mut issues, &opts);
2306 check_no_errors("query", src, issues.get(), &mut errors);
2307 if let StatementType::Insert {
2308 arguments,
2309 returning,
2310 ..
2311 } = q
2312 {
2313 match returning {
2314 Some(columns) if columns.len() == 2 => {}
2315 Some(columns) => {
2316 println!("query: expected 2 returning columns, got {}", columns.len());
2317 errors += 1;
2318 }
2319 None => {
2320 println!("query: expected returning columns, got none");
2321 errors += 1;
2322 }
2323 }
2324 if arguments.len() != 3 {
2325 println!("query: expected 3 arguments, got {}", arguments.len());
2326 errors += 1;
2327 }
2328 } else {
2329 println!("query: expected Insert");
2330 errors += 1;
2331 }
2332
2333 if errors != 0 {
2334 panic!("{errors} errors in postgresql_insert_unnest_bytea_array test");
2335 }
2336 }
2337
2338 #[test]
2341 fn postgresql_multirange_update_returning_unnest() {
2342 let schema_src = "
2343 CREATE TABLE partial_files (
2344 id uuid NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),
2345 uploaded_bytes int8multirange NOT NULL DEFAULT '{}',
2346 last_modified timestamptz NOT NULL DEFAULT now()
2347 );
2348 ";
2349
2350 let opts = TypeOptions::new()
2351 .dialect(SQLDialect::PostgreSQL)
2352 .arguments(SQLArguments::Dollar);
2353 let mut issues = Issues::new(schema_src);
2354 let schema = parse_schemas(schema_src, &mut issues, &opts);
2355 let mut errors = 0;
2356 check_no_errors("schema", schema_src, issues.get(), &mut errors);
2357
2358 let src = "
2359 WITH the_update AS (
2360 UPDATE partial_files
2361 SET last_modified = now(),
2362 uploaded_bytes = uploaded_bytes + multirange(int8range($2, $3, '[)'))
2363 WHERE id = $1
2364 RETURNING uploaded_bytes
2365 )
2366 SELECT unnest(uploaded_bytes) AS \"range\"
2367 FROM the_update
2368 ";
2369 let mut issues = Issues::new(src);
2370 let q = type_statement(&schema, src, &mut issues, &opts);
2371 check_no_errors("query", src, issues.get(), &mut errors);
2372 if let StatementType::Select { columns, arguments } = q {
2373 match columns.first() {
2374 Some(c) if c.type_.t == Type::Range(Box::new(Type::I64)) => {}
2375 Some(c) => {
2376 println!(
2377 "query: expected column 0 of type range(i64) got {}",
2378 c.type_.t
2379 );
2380 errors += 1;
2381 }
2382 None => {
2383 println!("query: expected a column, got none");
2384 errors += 1;
2385 }
2386 }
2387 if arguments.len() != 3 {
2388 println!("query: expected 3 arguments, got {}", arguments.len());
2389 errors += 1;
2390 }
2391 } else {
2392 println!("query: expected Select");
2393 errors += 1;
2394 }
2395
2396 if errors != 0 {
2397 panic!("{errors} errors in postgresql_multirange_update_returning_unnest test");
2398 }
2399 }
2400
2401 #[test]
2402 fn postgresql_alter_sequence() {
2403 let schema_src = "
2404 CREATE SEQUENCE tsng_datasets2_id_seq;
2405 ALTER SEQUENCE tsng_datasets2_id_seq OWNER TO compute;
2406 ALTER SEQUENCE tsng_datasets2_id_seq RENAME TO datasets_id_seq;
2407 ALTER SEQUENCE IF EXISTS datasets_id_seq
2408 INCREMENT BY 2
2409 MINVALUE 1
2410 MAXVALUE 1000
2411 START WITH 1
2412 RESTART WITH 42
2413 CACHE 10
2414 CYCLE
2415 OWNED BY none;
2416 ";
2417
2418 let opts = TypeOptions::new()
2419 .dialect(SQLDialect::PostgreSQL)
2420 .arguments(SQLArguments::Dollar);
2421 let mut issues = Issues::new(schema_src);
2422 let schemas = parse_schemas(schema_src, &mut issues, &opts);
2423 let mut errors = 0;
2424 check_no_errors("schema", schema_src, issues.get(), &mut errors);
2425
2426 use crate::schema::{QualifiedIdentifier, lookup_name};
2429 let search_path: &[&str] = &["public"];
2430 let new_key = QualifiedIdentifier::Qualified(
2431 Identifier::new("public", 0..0),
2432 Identifier::new("datasets_id_seq", 0..0),
2433 );
2434 if lookup_name(&schemas.sequences, &new_key, search_path).is_none() {
2435 println!("rename: datasets_id_seq missing from sequence registry");
2436 errors += 1;
2437 }
2438 let old_key = QualifiedIdentifier::Qualified(
2439 Identifier::new("public", 0..0),
2440 Identifier::new("tsng_datasets2_id_seq", 0..0),
2441 );
2442 if lookup_name(&schemas.sequences, &old_key, search_path).is_some() {
2443 println!("rename: old sequence name still in registry");
2444 errors += 1;
2445 }
2446
2447 {
2449 let src = "ALTER SEQUENCE unknown_seq OWNER TO compute;";
2450 let mut issues = Issues::new(src);
2451 let _schemas = parse_schemas(src, &mut issues, &opts);
2452 if issues.get().is_empty() {
2453 println!("unknown seq: expected an error, got none");
2454 errors += 1;
2455 }
2456 }
2457 {
2459 let src = "ALTER SEQUENCE IF EXISTS unknown_seq OWNER TO compute;";
2460 let mut issues = Issues::new(src);
2461 let _schemas = parse_schemas(src, &mut issues, &opts);
2462 check_no_errors("if exists", src, issues.get(), &mut errors);
2463 }
2464 {
2466 let src = "
2467 CREATE SEQUENCE s1 RESTART;
2468 CREATE SEQUENCE s2 RESTART WITH 42;
2469 CREATE SEQUENCE s3 RESTART 42;
2470 ALTER SEQUENCE s1 RESTART;
2471 ALTER SEQUENCE s2 RESTART WITH 42;
2472 ALTER SEQUENCE s3 RESTART 42;
2473 ";
2474 let mut issues = Issues::new(src);
2475 let _schemas = parse_schemas(src, &mut issues, &opts);
2476 check_no_errors("restart forms", src, issues.get(), &mut errors);
2477 }
2478 for src in [
2480 "ALTER SEQUENCE s RESTART WITH;",
2481 "CREATE SEQUENCE s RESTART WITH;",
2482 ] {
2483 let mut issues = Issues::new(src);
2484 let _schemas = parse_schemas(src, &mut issues, &opts);
2485 if issues.get().is_empty() {
2486 println!("restart with: expected an error for '{src}', got none");
2487 errors += 1;
2488 }
2489 }
2490 {
2493 let src = "
2494 CREATE SCHEMA app;
2495 CREATE SEQUENCE app.s;
2496 SET search_path TO app;
2497 ALTER SEQUENCE s RENAME TO t;
2498 ";
2499 let mut issues = Issues::new(src);
2500 let schemas = parse_schemas(src, &mut issues, &opts);
2501 check_no_errors("search path rename", src, issues.get(), &mut errors);
2502 let in_app = |name: &str| {
2503 lookup_name(
2504 &schemas.sequences,
2505 &QualifiedIdentifier::Unqualified(Identifier::new(name, 0..0)),
2506 ["app"].as_slice(),
2507 )
2508 .is_some()
2509 };
2510 if !in_app("t") {
2511 println!("search path rename: app.t missing from sequence registry");
2512 errors += 1;
2513 }
2514 if in_app("s") {
2515 println!("search path rename: app.s still in sequence registry");
2516 errors += 1;
2517 }
2518 }
2519 {
2522 let src = "
2523 CREATE SCHEMA app;
2524 CREATE SEQUENCE app.s;
2525 ALTER SEQUENCE s RENAME TO t;
2526 ";
2527 let mut issues = Issues::new(src);
2528 let _schemas = parse_schemas(src, &mut issues, &opts);
2529 if issues.get().is_empty() {
2530 println!("search path miss: expected an error, got none");
2531 errors += 1;
2532 }
2533 }
2534
2535 if errors != 0 {
2536 panic!("{errors} errors in postgresql_alter_sequence test");
2537 }
2538 }
2539}