1use nom::character::complete::{digit1, multispace0, multispace1};
2use std::fmt;
3use std::str;
4use std::str::FromStr;
5
6use column::{Column, ColumnConstraint, ColumnSpecification};
7use common::{
8 column_identifier_no_alias, parse_comment, sql_identifier, statement_terminator,
9 table_reference, type_identifier, ws_sep_comma, Literal, Real, SqlType, TableKey,
10};
11use compound_select::{compound_selection, CompoundSelectStatement};
12use create_table_options::table_options;
13use keywords::escape_if_keyword;
14use nom::branch::alt;
15use nom::bytes::complete::{tag, tag_no_case, take_until};
16use nom::combinator::{map, opt};
17use nom::multi::{many0, many1};
18use nom::sequence::{delimited, preceded, terminated, tuple};
19use nom::IResult;
20use order::{order_type, OrderType};
21use select::{nested_selection, SelectStatement};
22use table::Table;
23
24#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
25pub struct CreateTableStatement {
26 pub table: Table,
27 pub fields: Vec<ColumnSpecification>,
28 pub keys: Option<Vec<TableKey>>,
29}
30
31impl fmt::Display for CreateTableStatement {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 write!(f, "CREATE TABLE {} ", escape_if_keyword(&self.table.name))?;
34 write!(f, "(")?;
35 write!(
36 f,
37 "{}",
38 self.fields
39 .iter()
40 .map(|field| format!("{}", field))
41 .collect::<Vec<_>>()
42 .join(", ")
43 )?;
44 if let Some(ref keys) = self.keys {
45 write!(
46 f,
47 ", {}",
48 keys.iter()
49 .map(|key| format!("{}", key))
50 .collect::<Vec<_>>()
51 .join(", ")
52 )?;
53 }
54 write!(f, ")")
55 }
56}
57
58#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
59pub enum SelectSpecification {
60 Compound(CompoundSelectStatement),
61 Simple(SelectStatement),
62}
63
64impl fmt::Display for SelectSpecification {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 match *self {
67 SelectSpecification::Compound(ref csq) => write!(f, "{}", csq),
68 SelectSpecification::Simple(ref sq) => write!(f, "{}", sq),
69 }
70 }
71}
72
73#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
74pub struct CreateViewStatement {
75 pub name: String,
76 pub fields: Vec<Column>,
77 pub definition: Box<SelectSpecification>,
78}
79
80impl fmt::Display for CreateViewStatement {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 write!(f, "CREATE VIEW {} ", escape_if_keyword(&self.name))?;
83 if !self.fields.is_empty() {
84 write!(f, "(")?;
85 write!(
86 f,
87 "{}",
88 self.fields
89 .iter()
90 .map(|field| format!("{}", field))
91 .collect::<Vec<_>>()
92 .join(", ")
93 )?;
94 write!(f, ") ")?;
95 }
96 write!(f, "AS ")?;
97 write!(f, "{}", self.definition)
98 }
99}
100
101pub fn index_col_name(i: &[u8]) -> IResult<&[u8], (Column, Option<u16>, Option<OrderType>)> {
103 let (remaining_input, (column, len_u8, order)) = tuple((
104 terminated(column_identifier_no_alias, multispace0),
105 opt(delimited(tag("("), digit1, tag(")"))),
106 opt(order_type),
107 ))(i)?;
108 let len = len_u8.map(|l| u16::from_str(str::from_utf8(l).unwrap()).unwrap());
109
110 Ok((remaining_input, (column, len, order)))
111}
112
113pub fn index_col_list(i: &[u8]) -> IResult<&[u8], Vec<Column>> {
115 many0(map(
116 terminated(index_col_name, opt(ws_sep_comma)),
117 |e| e.0,
119 ))(i)
120}
121
122pub fn key_specification(i: &[u8]) -> IResult<&[u8], TableKey> {
124 alt((full_text_key, primary_key, unique, key_or_index))(i)
125}
126
127fn full_text_key(i: &[u8]) -> IResult<&[u8], TableKey> {
128 let (remaining_input, (_, _, _, _, name, _, columns)) = tuple((
129 tag_no_case("fulltext"),
130 multispace1,
131 alt((tag_no_case("key"), tag_no_case("index"))),
132 multispace1,
133 opt(sql_identifier),
134 multispace0,
135 delimited(
136 tag("("),
137 delimited(multispace0, index_col_list, multispace0),
138 tag(")"),
139 ),
140 ))(i)?;
141
142 match name {
143 Some(name) => {
144 let n = String::from_utf8(name.to_vec()).unwrap();
145 Ok((remaining_input, TableKey::FulltextKey(Some(n), columns)))
146 }
147 None => Ok((remaining_input, TableKey::FulltextKey(None, columns))),
148 }
149}
150
151fn primary_key(i: &[u8]) -> IResult<&[u8], TableKey> {
152 let (remaining_input, (_, _, columns, _)) = tuple((
153 tag_no_case("primary key"),
154 multispace0,
155 delimited(
156 tag("("),
157 delimited(multispace0, index_col_list, multispace0),
158 tag(")"),
159 ),
160 opt(map(
161 preceded(multispace1, tag_no_case("auto_increment")),
162 |_| (),
163 )),
164 ))(i)?;
165
166 Ok((remaining_input, TableKey::PrimaryKey(columns)))
167}
168
169fn unique(i: &[u8]) -> IResult<&[u8], TableKey> {
170 let (remaining_input, (_, _, _, name, _, columns)) = tuple((
172 tag_no_case("unique"),
173 opt(preceded(
174 multispace1,
175 alt((tag_no_case("key"), tag_no_case("index"))),
176 )),
177 multispace0,
178 opt(sql_identifier),
179 multispace0,
180 delimited(
181 tag("("),
182 delimited(multispace0, index_col_list, multispace0),
183 tag(")"),
184 ),
185 ))(i)?;
186
187 match name {
188 Some(name) => {
189 let n = String::from_utf8(name.to_vec()).unwrap();
190 Ok((remaining_input, TableKey::UniqueKey(Some(n), columns)))
191 }
192 None => Ok((remaining_input, TableKey::UniqueKey(None, columns))),
193 }
194}
195
196fn key_or_index(i: &[u8]) -> IResult<&[u8], TableKey> {
197 let (remaining_input, (_, _, name, _, columns)) = tuple((
198 alt((tag_no_case("key"), tag_no_case("index"))),
199 multispace0,
200 sql_identifier,
201 multispace0,
202 delimited(
203 tag("("),
204 delimited(multispace0, index_col_list, multispace0),
205 tag(")"),
206 ),
207 ))(i)?;
208
209 let n = String::from_utf8(name.to_vec()).unwrap();
210 Ok((remaining_input, TableKey::Key(n, columns)))
211}
212
213pub fn key_specification_list(i: &[u8]) -> IResult<&[u8], Vec<TableKey>> {
215 many1(terminated(key_specification, opt(ws_sep_comma)))(i)
216}
217
218fn field_specification(i: &[u8]) -> IResult<&[u8], ColumnSpecification> {
219 let (remaining_input, (column, field_type, constraints, comment, _)) = tuple((
220 column_identifier_no_alias,
221 opt(delimited(multispace1, type_identifier, multispace0)),
222 many0(column_constraint),
223 opt(parse_comment),
224 opt(ws_sep_comma),
225 ))(i)?;
226
227 let sql_type = match field_type {
228 None => SqlType::Text,
229 Some(ref t) => t.clone(),
230 };
231 Ok((
232 remaining_input,
233 ColumnSpecification {
234 column,
235 sql_type,
236 constraints: constraints.into_iter().filter_map(|m| m).collect(),
237 comment,
238 },
239 ))
240}
241
242pub fn field_specification_list(i: &[u8]) -> IResult<&[u8], Vec<ColumnSpecification>> {
244 many1(field_specification)(i)
245}
246
247pub fn column_constraint(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
249 let not_null = map(
250 delimited(multispace0, tag_no_case("not null"), multispace0),
251 |_| Some(ColumnConstraint::NotNull),
252 );
253 let null = map(
254 delimited(multispace0, tag_no_case("null"), multispace0),
255 |_| None,
256 );
257 let auto_increment = map(
258 delimited(multispace0, tag_no_case("auto_increment"), multispace0),
259 |_| Some(ColumnConstraint::AutoIncrement),
260 );
261 let primary_key = map(
262 delimited(multispace0, tag_no_case("primary key"), multispace0),
263 |_| Some(ColumnConstraint::PrimaryKey),
264 );
265 let unique = map(
266 delimited(multispace0, tag_no_case("unique"), multispace0),
267 |_| Some(ColumnConstraint::Unique),
268 );
269 let character_set = map(
270 preceded(
271 delimited(multispace0, tag_no_case("character set"), multispace1),
272 sql_identifier,
273 ),
274 |cs| {
275 let char_set = str::from_utf8(cs).unwrap().to_owned();
276 Some(ColumnConstraint::CharacterSet(char_set))
277 },
278 );
279 let collate = map(
280 preceded(
281 delimited(multispace0, tag_no_case("collate"), multispace1),
282 sql_identifier,
283 ),
284 |c| {
285 let collation = str::from_utf8(c).unwrap().to_owned();
286 Some(ColumnConstraint::Collation(collation))
287 },
288 );
289
290 alt((
291 not_null,
292 null,
293 auto_increment,
294 default,
295 primary_key,
296 unique,
297 character_set,
298 collate,
299 ))(i)
300}
301
302fn fixed_point(i: &[u8]) -> IResult<&[u8], Literal> {
303 let (remaining_input, (i, _, f)) = tuple((digit1, tag("."), digit1))(i)?;
304
305 Ok((
306 remaining_input,
307 Literal::FixedPoint(Real {
308 integral: i32::from_str(str::from_utf8(i).unwrap()).unwrap(),
309 fractional: i32::from_str(str::from_utf8(f).unwrap()).unwrap(),
310 }),
311 ))
312}
313
314fn default(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
315 let (remaining_input, (_, _, _, def, _)) = tuple((
316 multispace0,
317 tag_no_case("default"),
318 multispace1,
319 alt((
320 map(
321 delimited(tag("'"), take_until("'"), tag("'")),
322 |s: &[u8]| Literal::String(String::from_utf8(s.to_vec()).unwrap()),
323 ),
324 fixed_point,
325 map(digit1, |d| {
326 let d_i64 = i64::from_str(str::from_utf8(d).unwrap()).unwrap();
327 Literal::Integer(d_i64)
328 }),
329 map(tag("''"), |_| Literal::String(String::from(""))),
330 map(tag_no_case("null"), |_| Literal::Null),
331 map(tag_no_case("current_timestamp"), |_| {
332 Literal::CurrentTimestamp
333 }),
334 )),
335 multispace0,
336 ))(i)?;
337
338 Ok((remaining_input, Some(ColumnConstraint::DefaultValue(def))))
339}
340
341pub fn creation(i: &[u8]) -> IResult<&[u8], CreateTableStatement> {
344 let (remaining_input, (_, _, _, _, table, _, _, _, fields_list, _, keys_list, _, _, _, _, _)) =
345 tuple((
346 tag_no_case("create"),
347 multispace1,
348 tag_no_case("table"),
349 multispace1,
350 table_reference,
351 multispace0,
352 tag("("),
353 multispace0,
354 field_specification_list,
355 multispace0,
356 opt(key_specification_list),
357 multispace0,
358 tag(")"),
359 multispace0,
360 table_options,
361 statement_terminator,
362 ))(i)?;
363
364 assert!(table.alias.is_none());
366 let fields = fields_list
368 .into_iter()
369 .map(|field| {
370 let column = Column {
371 table: Some(table.name.clone()),
372 ..field.column
373 };
374
375 ColumnSpecification { column, ..field }
376 })
377 .collect();
378
379 let keys = keys_list.and_then(|ks| {
381 Some(
382 ks.into_iter()
383 .map(|key| {
384 let attach_names = |columns: Vec<Column>| {
385 columns
386 .into_iter()
387 .map(|column| Column {
388 table: Some(table.name.clone()),
389 ..column
390 })
391 .collect()
392 };
393
394 match key {
395 TableKey::PrimaryKey(columns) => {
396 TableKey::PrimaryKey(attach_names(columns))
397 }
398 TableKey::UniqueKey(name, columns) => {
399 TableKey::UniqueKey(name, attach_names(columns))
400 }
401 TableKey::FulltextKey(name, columns) => {
402 TableKey::FulltextKey(name, attach_names(columns))
403 }
404 TableKey::Key(name, columns) => TableKey::Key(name, attach_names(columns)),
405 }
406 })
407 .collect(),
408 )
409 });
410
411 Ok((
412 remaining_input,
413 CreateTableStatement {
414 table,
415 fields,
416 keys,
417 },
418 ))
419}
420
421pub fn view_creation(i: &[u8]) -> IResult<&[u8], CreateViewStatement> {
423 let (remaining_input, (_, _, _, _, name_slice, _, _, _, def, _)) = tuple((
424 tag_no_case("create"),
425 multispace1,
426 tag_no_case("view"),
427 multispace1,
428 sql_identifier,
429 multispace1,
430 tag_no_case("as"),
431 multispace1,
432 alt((
433 map(compound_selection, |s| SelectSpecification::Compound(s)),
434 map(nested_selection, |s| SelectSpecification::Simple(s)),
435 )),
436 statement_terminator,
437 ))(i)?;
438
439 let name = String::from_utf8(name_slice.to_vec()).unwrap();
440 let fields = vec![]; let definition = Box::new(def);
442
443 Ok((
444 remaining_input,
445 CreateViewStatement {
446 name,
447 fields,
448 definition,
449 },
450 ))
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456 use column::Column;
457 use table::Table;
458
459 #[test]
460 fn sql_types() {
461 let type0 = "bigint(20)";
462 let type1 = "varchar(255) binary";
463 let type2 = "bigint(20) unsigned";
464 let type3 = "bigint(20) signed";
465
466 let res = type_identifier(type0.as_bytes());
467 assert_eq!(res.unwrap().1, SqlType::Bigint(20));
468 let res = type_identifier(type1.as_bytes());
469 assert_eq!(res.unwrap().1, SqlType::Varchar(255));
470 let res = type_identifier(type2.as_bytes());
471 assert_eq!(res.unwrap().1, SqlType::UnsignedBigint(20));
472 let res = type_identifier(type3.as_bytes());
473 assert_eq!(res.unwrap().1, SqlType::Bigint(20));
474 let res = type_identifier(type2.as_bytes());
475 assert_eq!(res.unwrap().1, SqlType::UnsignedBigint(20));
476 }
477
478 #[test]
479 fn field_spec() {
480 let qstring = "id bigint(20), name varchar(255),";
483
484 let res = field_specification_list(qstring.as_bytes());
485 assert_eq!(
486 res.unwrap().1,
487 vec![
488 ColumnSpecification::new(Column::from("id"), SqlType::Bigint(20)),
489 ColumnSpecification::new(Column::from("name"), SqlType::Varchar(255)),
490 ]
491 );
492 }
493
494 #[test]
495 fn simple_create() {
496 let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255));";
497
498 let res = creation(qstring.as_bytes());
499 assert_eq!(
500 res.unwrap().1,
501 CreateTableStatement {
502 table: Table::from("users"),
503 fields: vec![
504 ColumnSpecification::new(Column::from("users.id"), SqlType::Bigint(20)),
505 ColumnSpecification::new(Column::from("users.name"), SqlType::Varchar(255)),
506 ColumnSpecification::new(Column::from("users.email"), SqlType::Varchar(255)),
507 ],
508 ..Default::default()
509 }
510 );
511 }
512
513 #[test]
514 fn create_without_space_after_tablename() {
515 let qstring = "CREATE TABLE t(x integer);";
516 let res = creation(qstring.as_bytes());
517 assert_eq!(
518 res.unwrap().1,
519 CreateTableStatement {
520 table: Table::from("t"),
521 fields: vec![ColumnSpecification::new(
522 Column::from("t.x"),
523 SqlType::Int(32)
524 ),],
525 ..Default::default()
526 }
527 );
528 }
529
530 #[test]
531 fn mediawiki_create() {
532 let qstring = "CREATE TABLE user_newtalk ( user_id int(5) NOT NULL default '0', user_ip \
533 varchar(40) NOT NULL default '') TYPE=MyISAM;";
534 let res = creation(qstring.as_bytes());
535 assert_eq!(
536 res.unwrap().1,
537 CreateTableStatement {
538 table: Table::from("user_newtalk"),
539 fields: vec![
540 ColumnSpecification::with_constraints(
541 Column::from("user_newtalk.user_id"),
542 SqlType::Int(5),
543 vec![
544 ColumnConstraint::NotNull,
545 ColumnConstraint::DefaultValue(Literal::String(String::from("0"))),
546 ],
547 ),
548 ColumnSpecification::with_constraints(
549 Column::from("user_newtalk.user_ip"),
550 SqlType::Varchar(40),
551 vec![
552 ColumnConstraint::NotNull,
553 ColumnConstraint::DefaultValue(Literal::String(String::from(""))),
554 ],
555 ),
556 ],
557 ..Default::default()
558 }
559 );
560 }
561
562 #[test]
563 fn mediawiki_create2() {
564 let qstring = "CREATE TABLE `user` (
565 user_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
566 user_name varchar(255) binary NOT NULL default '',
567 user_real_name varchar(255) binary NOT NULL default '',
568 user_password tinyblob NOT NULL,
569 user_newpassword tinyblob NOT NULL,
570 user_newpass_time binary(14),
571 user_email tinytext NOT NULL,
572 user_touched binary(14) NOT NULL default '',
573 user_token binary(32) NOT NULL default '',
574 user_email_authenticated binary(14),
575 user_email_token binary(32),
576 user_email_token_expires binary(14),
577 user_registration binary(14),
578 user_editcount int,
579 user_password_expires varbinary(14) DEFAULT NULL
580 ) ENGINE=, DEFAULT CHARSET=utf8";
581 creation(qstring.as_bytes()).unwrap();
582 }
583
584 #[test]
585 fn mediawiki_create3() {
586 let qstring = "CREATE TABLE `interwiki` (
587 iw_prefix varchar(32) NOT NULL,
588 iw_url blob NOT NULL,
589 iw_api blob NOT NULL,
590 iw_wikiid varchar(64) NOT NULL,
591 iw_local bool NOT NULL,
592 iw_trans tinyint NOT NULL default 0
593 ) ENGINE=, DEFAULT CHARSET=utf8";
594 creation(qstring.as_bytes()).unwrap();
595 }
596
597 #[test]
598 fn mediawiki_externallinks() {
599 let qstring = "CREATE TABLE `externallinks` (
600 `el_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
601 `el_from` int(8) unsigned NOT NULL DEFAULT '0',
602 `el_from_namespace` int(11) NOT NULL DEFAULT '0',
603 `el_to` blob NOT NULL,
604 `el_index` blob NOT NULL,
605 `el_index_60` varbinary(60) NOT NULL,
606 PRIMARY KEY (`el_id`),
607 KEY `el_from` (`el_from`,`el_to`(40)),
608 KEY `el_to` (`el_to`(60),`el_from`),
609 KEY `el_index` (`el_index`(60)),
610 KEY `el_backlinks_to` (`el_from_namespace`,`el_to`(60),`el_from`),
611 KEY `el_index_60` (`el_index_60`,`el_id`),
612 KEY `el_from_index_60` (`el_from`,`el_index_60`,`el_id`)
613 )";
614 creation(qstring.as_bytes()).unwrap();
615 }
616
617 #[test]
618 fn keys() {
619 let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
621 PRIMARY KEY (id));";
622
623 let res = creation(qstring.as_bytes());
624 assert_eq!(
625 res.unwrap().1,
626 CreateTableStatement {
627 table: Table::from("users"),
628 fields: vec![
629 ColumnSpecification::new(Column::from("users.id"), SqlType::Bigint(20)),
630 ColumnSpecification::new(Column::from("users.name"), SqlType::Varchar(255)),
631 ColumnSpecification::new(Column::from("users.email"), SqlType::Varchar(255)),
632 ],
633 keys: Some(vec![TableKey::PrimaryKey(vec![Column::from("users.id")])]),
634 ..Default::default()
635 }
636 );
637
638 let qstring = "CREATE TABLE users (id bigint(20), name varchar(255), email varchar(255), \
640 UNIQUE KEY id_k (id));";
641
642 let res = creation(qstring.as_bytes());
643 assert_eq!(
644 res.unwrap().1,
645 CreateTableStatement {
646 table: Table::from("users"),
647 fields: vec![
648 ColumnSpecification::new(Column::from("users.id"), SqlType::Bigint(20)),
649 ColumnSpecification::new(Column::from("users.name"), SqlType::Varchar(255)),
650 ColumnSpecification::new(Column::from("users.email"), SqlType::Varchar(255)),
651 ],
652 keys: Some(vec![TableKey::UniqueKey(
653 Some(String::from("id_k")),
654 vec![Column::from("users.id")],
655 ),]),
656 ..Default::default()
657 }
658 );
659 }
660
661 #[test]
662 fn django_create() {
663 let qstring = "CREATE TABLE `django_admin_log` (
664 `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
665 `action_time` datetime NOT NULL,
666 `user_id` integer NOT NULL,
667 `content_type_id` integer,
668 `object_id` longtext,
669 `object_repr` varchar(200) NOT NULL,
670 `action_flag` smallint UNSIGNED NOT NULL,
671 `change_message` longtext NOT NULL);";
672 let res = creation(qstring.as_bytes());
673 assert_eq!(
674 res.unwrap().1,
675 CreateTableStatement {
676 table: Table::from("django_admin_log"),
677 fields: vec![
678 ColumnSpecification::with_constraints(
679 Column::from("django_admin_log.id"),
680 SqlType::Int(32),
681 vec![
682 ColumnConstraint::AutoIncrement,
683 ColumnConstraint::NotNull,
684 ColumnConstraint::PrimaryKey,
685 ],
686 ),
687 ColumnSpecification::with_constraints(
688 Column::from("django_admin_log.action_time"),
689 SqlType::DateTime(0),
690 vec![ColumnConstraint::NotNull],
691 ),
692 ColumnSpecification::with_constraints(
693 Column::from("django_admin_log.user_id"),
694 SqlType::Int(32),
695 vec![ColumnConstraint::NotNull],
696 ),
697 ColumnSpecification::new(
698 Column::from("django_admin_log.content_type_id"),
699 SqlType::Int(32),
700 ),
701 ColumnSpecification::new(
702 Column::from("django_admin_log.object_id"),
703 SqlType::Longtext,
704 ),
705 ColumnSpecification::with_constraints(
706 Column::from("django_admin_log.object_repr"),
707 SqlType::Varchar(200),
708 vec![ColumnConstraint::NotNull],
709 ),
710 ColumnSpecification::with_constraints(
711 Column::from("django_admin_log.action_flag"),
712 SqlType::UnsignedInt(32),
713 vec![ColumnConstraint::NotNull],
714 ),
715 ColumnSpecification::with_constraints(
716 Column::from("django_admin_log.change_message"),
717 SqlType::Longtext,
718 vec![ColumnConstraint::NotNull],
719 ),
720 ],
721 ..Default::default()
722 }
723 );
724
725 let qstring = "CREATE TABLE `auth_group` (
726 `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
727 `name` varchar(80) NOT NULL UNIQUE)";
728 let res = creation(qstring.as_bytes());
729 assert_eq!(
730 res.unwrap().1,
731 CreateTableStatement {
732 table: Table::from("auth_group"),
733 fields: vec![
734 ColumnSpecification::with_constraints(
735 Column::from("auth_group.id"),
736 SqlType::Int(32),
737 vec![
738 ColumnConstraint::AutoIncrement,
739 ColumnConstraint::NotNull,
740 ColumnConstraint::PrimaryKey,
741 ],
742 ),
743 ColumnSpecification::with_constraints(
744 Column::from("auth_group.name"),
745 SqlType::Varchar(80),
746 vec![ColumnConstraint::NotNull, ColumnConstraint::Unique],
747 ),
748 ],
749 ..Default::default()
750 }
751 );
752 }
753
754 #[test]
755 fn format_create() {
756 let qstring = "CREATE TABLE `auth_group` (
757 `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
758 `name` varchar(80) NOT NULL UNIQUE)";
759 let expected = "CREATE TABLE auth_group (\
761 id INT(32) AUTO_INCREMENT NOT NULL PRIMARY KEY, \
762 name VARCHAR(80) NOT NULL UNIQUE)";
763 let res = creation(qstring.as_bytes());
764 assert_eq!(format!("{}", res.unwrap().1), expected);
765 }
766
767 #[test]
768 fn simple_create_view() {
769 use common::{FieldDefinitionExpression, Operator};
770 use condition::{ConditionBase, ConditionExpression, ConditionTree};
771
772 let qstring = "CREATE VIEW v AS SELECT * FROM users WHERE username = \"bob\";";
773
774 let res = view_creation(qstring.as_bytes());
775 assert_eq!(
776 res.unwrap().1,
777 CreateViewStatement {
778 name: String::from("v"),
779 fields: vec![],
780 definition: Box::new(SelectSpecification::Simple(SelectStatement {
781 tables: vec![Table::from("users")],
782 fields: vec![FieldDefinitionExpression::All],
783 where_clause: Some(ConditionExpression::ComparisonOp(ConditionTree {
784 left: Box::new(ConditionExpression::Base(ConditionBase::Field(
785 "username".into()
786 ))),
787 right: Box::new(ConditionExpression::Base(ConditionBase::Literal(
788 Literal::String("bob".into())
789 ))),
790 operator: Operator::Equal,
791 })),
792 ..Default::default()
793 })),
794 }
795 );
796 }
797
798 #[test]
799 fn compound_create_view() {
800 use common::FieldDefinitionExpression;
801 use compound_select::{CompoundSelectOperator, CompoundSelectStatement};
802
803 let qstring = "CREATE VIEW v AS SELECT * FROM users UNION SELECT * FROM old_users;";
804
805 let res = view_creation(qstring.as_bytes());
806 assert_eq!(
807 res.unwrap().1,
808 CreateViewStatement {
809 name: String::from("v"),
810 fields: vec![],
811 definition: Box::new(SelectSpecification::Compound(CompoundSelectStatement {
812 selects: vec![
813 (
814 None,
815 SelectStatement {
816 tables: vec![Table::from("users")],
817 fields: vec![FieldDefinitionExpression::All],
818 ..Default::default()
819 },
820 ),
821 (
822 Some(CompoundSelectOperator::DistinctUnion),
823 SelectStatement {
824 tables: vec![Table::from("old_users")],
825 fields: vec![FieldDefinitionExpression::All],
826 ..Default::default()
827 },
828 ),
829 ],
830 order: None,
831 limit: None,
832 })),
833 }
834 );
835 }
836
837 #[test]
838 fn format_create_view() {
839 let qstring = "CREATE VIEW `v` AS SELECT * FROM `t`;";
840 let expected = "CREATE VIEW v AS SELECT * FROM t";
841 let res = view_creation(qstring.as_bytes());
842 assert_eq!(format!("{}", res.unwrap().1), expected);
843 }
844
845 #[test]
846 fn lobsters_indexes() {
847 let qstring = "CREATE TABLE `comments` (
848 `id` int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
849 `hat_id` int,
850 fulltext INDEX `index_comments_on_comment` (`comment`),
851 INDEX `confidence_idx` (`confidence`),
852 UNIQUE INDEX `short_id` (`short_id`),
853 INDEX `story_id_short_id` (`story_id`, `short_id`),
854 INDEX `thread_id` (`thread_id`),
855 INDEX `index_comments_on_user_id` (`user_id`))
856 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
857 let res = creation(qstring.as_bytes());
858 assert_eq!(
859 res.unwrap().1,
860 CreateTableStatement {
861 table: Table::from("comments"),
862 fields: vec![
863 ColumnSpecification::with_constraints(
864 Column::from("comments.id"),
865 SqlType::UnsignedInt(32),
866 vec![
867 ColumnConstraint::NotNull,
868 ColumnConstraint::AutoIncrement,
869 ColumnConstraint::PrimaryKey,
870 ],
871 ),
872 ColumnSpecification::new(Column::from("comments.hat_id"), SqlType::Int(32),),
873 ],
874 keys: Some(vec![
875 TableKey::FulltextKey(
876 Some("index_comments_on_comment".into()),
877 vec![Column::from("comments.comment")]
878 ),
879 TableKey::Key(
880 "confidence_idx".into(),
881 vec![Column::from("comments.confidence")]
882 ),
883 TableKey::UniqueKey(
884 Some("short_id".into()),
885 vec![Column::from("comments.short_id")]
886 ),
887 TableKey::Key(
888 "story_id_short_id".into(),
889 vec![
890 Column::from("comments.story_id"),
891 Column::from("comments.short_id")
892 ]
893 ),
894 TableKey::Key("thread_id".into(), vec![Column::from("comments.thread_id")]),
895 TableKey::Key(
896 "index_comments_on_user_id".into(),
897 vec![Column::from("comments.user_id")]
898 ),
899 ]),
900 }
901 );
902 }
903}