1use nom::branch::alt;
2use nom::bytes::complete::{tag, tag_no_case, take_until};
3use nom::character::complete::{digit1, multispace0, multispace1};
4use nom::combinator::{map, opt};
5use nom::multi::{many0, many1};
6use nom::sequence::{delimited, preceded, terminated};
7use nom::{IResult, Parser};
8use serde::Deserialize;
9use serde::Serialize;
10use std::fmt;
11use std::str;
12use std::str::FromStr;
13
14use super::column::{Column, ColumnConstraint, ColumnSpecification};
15use super::common::{
16 Literal, Real, SqlType, TableKey, column_identifier_no_alias, column_identifier_query,
17 parse_comment, reference_option, schema_table_reference, sql_identifier, statement_terminator,
18 type_identifier, ws_sep_comma,
19};
20use super::create_table_options::table_options;
21use super::keywords::escape;
22use super::order::{OrderType, order_type};
23use crate::common::{string_literal, take_until_unbalanced};
24use crate::create_table_options::TableOption;
25
26#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
27pub struct CreateTableStatement {
28 pub table: String,
29 pub fields: Vec<ColumnSpecification>,
30 pub keys: Option<Vec<TableKey>>,
31 pub options: Vec<TableOption>,
32}
33
34impl fmt::Display for CreateTableStatement {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 write!(f, "CREATE TABLE {} ", escape(&self.table))?;
37 write!(f, "(")?;
38 write!(
39 f,
40 "{}",
41 self.fields
42 .iter()
43 .map(|field| format!("{}", field))
44 .collect::<Vec<_>>()
45 .join(", ")
46 )?;
47 if let Some(ref keys) = self.keys {
48 write!(
49 f,
50 ", {}",
51 keys.iter()
52 .map(|key| format!("{}", key))
53 .collect::<Vec<_>>()
54 .join(", ")
55 )?;
56 }
57 write!(f, ")")?;
58 for option in &self.options {
59 write!(f, "\n{}", option)?;
60 }
61 write!(f, ";")
62 }
63}
64
65pub fn index_col_name(i: &[u8]) -> IResult<&[u8], (Column, Option<OrderType>)> {
67 let (remaining_input, (mut column, order)) = (
68 terminated(
69 alt((column_identifier_no_alias, column_identifier_query)),
70 multispace0,
71 ),
72 opt(order_type),
73 )
74 .parse(i)?;
75 column.desc = order == Some(OrderType::OrderDescending);
76 Ok((remaining_input, (column, order)))
77}
78
79pub fn index_col_list(i: &[u8]) -> IResult<&[u8], Vec<Column>> {
81 many0(map(
82 terminated(index_col_name, opt(ws_sep_comma)),
83 |e| e.0,
85 ))
86 .parse(i)
87}
88
89pub fn key_specification(i: &[u8]) -> IResult<&[u8], TableKey> {
91 alt((
92 full_text_key,
93 primary_key,
94 unique,
95 key_or_index,
96 spatial,
97 check_constraint,
98 constraint,
99 ))
100 .parse(i)
101}
102
103fn balanced_parens(i: &[u8]) -> IResult<&[u8], &[u8]> {
106 if i.first() != Some(&b'(') {
107 return Err(nom::Err::Error(nom::error::Error::new(
108 i,
109 nom::error::ErrorKind::Char,
110 )));
111 }
112 let mut depth = 0usize;
113 let mut quote: Option<u8> = None;
114 let mut idx = 0usize;
115 while idx < i.len() {
116 let c = i[idx];
117 if let Some(q) = quote {
118 if c == b'\\' && q != b'`' {
119 idx += 1; } else if c == q {
121 if i.get(idx + 1) == Some(&q) {
122 idx += 1; } else {
124 quote = None;
125 }
126 }
127 } else {
128 match c {
129 b'(' => depth += 1,
130 b')' => {
131 depth -= 1;
132 if depth == 0 {
133 return Ok((&i[idx + 1..], &i[..=idx]));
134 }
135 }
136 b'\'' | b'"' | b'`' => quote = Some(c),
137 _ => {}
138 }
139 }
140 idx += 1;
141 }
142 Err(nom::Err::Error(nom::error::Error::new(
143 i,
144 nom::error::ErrorKind::Eof,
145 )))
146}
147
148fn check_constraint(i: &[u8]) -> IResult<&[u8], TableKey> {
150 let (remaining_input, (_, _, name, _, _, _, clause, _, _)) = (
151 tag_no_case("CONSTRAINT"),
152 multispace1,
153 sql_identifier,
154 multispace1,
155 tag_no_case("CHECK"),
156 multispace0,
157 balanced_parens,
158 opt((multispace1, opt(tag_no_case("NOT ")), tag_no_case("ENFORCED"))),
159 opt((
160 multispace0,
161 tag("/*!80016"),
162 take_until("*/"),
163 tag("*/"),
164 )),
165 )
166 .parse(i)?;
167
168 let name = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
169 let clause = String::from_utf8(clause.to_vec()).unwrap();
170 Ok((
171 remaining_input,
172 TableKey::CheckConstraint(name, clause),
173 ))
174}
175
176fn full_text_key(i: &[u8]) -> IResult<&[u8], TableKey> {
177 let (remaining_input, (_, _, _, _, name, _, columns, _, parser, _)) = (
178 tag_no_case("fulltext"),
179 multispace1,
180 alt((tag_no_case("key"), tag_no_case("index"))),
181 multispace1,
182 sql_identifier,
183 multispace0,
184 delimited(
185 tag("("),
186 delimited(multispace0, index_col_list, multispace0),
187 tag(")"),
188 ),
189 multispace0,
190 opt(delimited(
191 tag("/*!50100 WITH PARSER"),
192 delimited(multispace0, sql_identifier, multispace0),
193 tag("*/"),
194 )),
195 multispace0,
196 )
197 .parse(i)?;
198
199 let name = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
200 let parser = parser.map(|v| String::from_utf8(v.to_vec()).unwrap());
201 Ok((
202 remaining_input,
203 TableKey::FulltextKey(name, columns, parser),
204 ))
205}
206
207fn primary_key(i: &[u8]) -> IResult<&[u8], TableKey> {
208 let (remaining_input, (_, _, columns, _, _, _)) = (
209 tag_no_case("primary key"),
210 multispace0,
211 delimited(
212 tag("("),
213 delimited(multispace0, index_col_list, multispace0),
214 tag(")"),
215 ),
216 opt(map(
217 preceded(multispace1, tag_no_case("auto_increment")),
218 |_| (),
219 )),
220 multispace0,
221 opt(tag_no_case("USING BTREE")),
222 )
223 .parse(i)?;
224
225 Ok((remaining_input, TableKey::PrimaryKey(columns)))
226}
227
228fn unique(i: &[u8]) -> IResult<&[u8], TableKey> {
229 let (remaining_input, (_, _, _, name, _, columns, _, _)) = (
231 tag_no_case("unique"),
232 opt(preceded(
233 multispace1,
234 alt((tag_no_case("key"), tag_no_case("index"))),
235 )),
236 multispace0,
237 sql_identifier,
238 multispace0,
239 delimited(
240 tag("("),
241 delimited(multispace0, index_col_list, multispace0),
242 tag(")"),
243 ),
244 multispace0,
245 opt(tag_no_case("USING BTREE")),
246 )
247 .parse(i)?;
248
249 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
250 Ok((remaining_input, TableKey::UniqueKey(n, columns)))
251}
252
253fn key_or_index(i: &[u8]) -> IResult<&[u8], TableKey> {
254 let (remaining_input, (_, _, name, _, columns, _, _)) = (
255 alt((tag_no_case("key"), tag_no_case("index"))),
256 multispace0,
257 sql_identifier,
258 multispace0,
259 delimited(
260 tag("("),
261 delimited(multispace0, index_col_list, multispace0),
262 tag(")"),
263 ),
264 multispace0,
265 opt(tag_no_case("USING BTREE")),
266 )
267 .parse(i)?;
268
269 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
270 Ok((remaining_input, TableKey::Key(n, columns)))
271}
272
273fn spatial(i: &[u8]) -> IResult<&[u8], TableKey> {
274 let (remaining_input, (_, _, _, name, _, columns)) = (
275 tag_no_case("spatial"),
276 opt(preceded(
277 multispace1,
278 alt((tag_no_case("key"), tag_no_case("index"))),
279 )),
280 multispace0,
281 sql_identifier,
282 multispace0,
283 delimited(
284 tag("("),
285 delimited(multispace0, index_col_list, multispace0),
286 tag(")"),
287 ),
288 )
289 .parse(i)?;
290
291 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
292 Ok((remaining_input, TableKey::SpatialKey(n, columns)))
293}
294
295fn constraint(i: &[u8]) -> IResult<&[u8], TableKey> {
296 let (
297 remaining_input,
298 (
299 _,
300 _,
301 name,
302 _,
303 _,
304 _,
305 columns,
306 _,
307 _,
308 _,
309 table,
310 _,
311 foreign,
312 on_delete,
313 on_update,
314 on_delete2,
315 ),
316 ) = (
317 tag_no_case("CONSTRAINT"),
318 multispace1,
319 sql_identifier,
320 multispace1,
321 tag_no_case("FOREIGN KEY"),
322 multispace0,
323 delimited(
324 tag("("),
325 delimited(multispace0, index_col_list, multispace0),
326 tag(")"),
327 ),
328 multispace1,
329 tag_no_case("REFERENCES"),
330 multispace1,
331 sql_identifier,
332 multispace0,
333 delimited(
334 tag("("),
335 delimited(multispace0, index_col_list, multispace0),
336 tag(")"),
337 ),
338 opt((
339 multispace1,
340 tag_no_case("ON DELETE"),
341 multispace1,
342 reference_option,
343 )),
344 opt((
345 multispace1,
346 tag_no_case("ON UPDATE"),
347 multispace1,
348 reference_option,
349 )),
350 opt((
351 multispace1,
352 tag_no_case("ON DELETE"),
353 multispace1,
354 reference_option,
355 )),
356 )
357 .parse(i)?;
358
359 let name = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
360 let table = String::from_utf8(table.to_vec())
361 .unwrap()
362 .replace("``", "`");
363 let on_delete = if let Some(on_delete) = on_delete {
364 let (_, _, _, on_delete) = on_delete;
365 Some(on_delete)
366 } else if let Some(on_delete) = on_delete2 {
367 let (_, _, _, on_delete) = on_delete;
368 Some(on_delete)
369 } else {
370 None
371 };
372 let on_update = if let Some(on_update) = on_update {
373 let (_, _, _, on_update) = on_update;
374 Some(on_update)
375 } else {
376 None
377 };
378 Ok((
379 remaining_input,
380 TableKey::Constraint(name, columns, table, foreign, on_delete, on_update),
381 ))
382}
383
384pub fn key_specification_list(i: &[u8]) -> IResult<&[u8], Vec<TableKey>> {
386 many1(terminated(key_specification, opt(ws_sep_comma))).parse(i)
387}
388
389fn field_specification(i: &[u8]) -> IResult<&[u8], ColumnSpecification> {
390 let (remaining_input, (column, field_type, constraints, comment, _)) = (
391 column_identifier_no_alias,
392 opt(delimited(multispace1, type_identifier, multispace0)),
393 many0(column_constraint),
394 opt(parse_comment),
395 opt(ws_sep_comma),
396 )
397 .parse(i)?;
398
399 let sql_type = match field_type {
400 None => SqlType::Text,
401 Some(ref t) => t.clone(),
402 };
403 Ok((
404 remaining_input,
405 ColumnSpecification {
406 column,
407 sql_type,
408 constraints: constraints.into_iter().flatten().collect(),
409 comment,
410 },
411 ))
412}
413
414pub fn field_specification_list(i: &[u8]) -> IResult<&[u8], Vec<ColumnSpecification>> {
416 many1(field_specification).parse(i)
417}
418
419pub fn column_constraint(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
421 let not_null = map(
422 delimited(multispace0, tag_no_case("not null"), multispace0),
423 |_| Some(ColumnConstraint::NotNull),
424 );
425 let null = map(
426 delimited(multispace0, tag_no_case("null"), multispace0),
427 |_| None,
428 );
429 let auto_increment = map(
430 delimited(multispace0, tag_no_case("auto_increment"), multispace0),
431 |_| Some(ColumnConstraint::AutoIncrement),
432 );
433 let primary_key = map(
434 delimited(multispace0, tag_no_case("primary key"), multispace0),
435 |_| Some(ColumnConstraint::PrimaryKey),
436 );
437 let unique = map(
438 delimited(multispace0, tag_no_case("unique"), multispace0),
439 |_| Some(ColumnConstraint::Unique),
440 );
441 let character_set = map(
442 preceded(
443 delimited(multispace0, tag_no_case("character set"), multispace1),
444 sql_identifier,
445 ),
446 |cs| {
447 let char_set = str::from_utf8(cs).unwrap().to_owned();
448 Some(ColumnConstraint::CharacterSet(char_set))
449 },
450 );
451 let collate = map(
452 preceded(
453 delimited(multispace0, tag_no_case("collate"), multispace1),
454 sql_identifier,
455 ),
456 |c| {
457 let collation = str::from_utf8(c).unwrap().to_owned();
458 Some(ColumnConstraint::Collation(collation))
459 },
460 );
461 let srid = map(
462 (
463 multispace0,
464 tag_no_case("/*!80003 SRID "),
465 digit1,
466 tag_no_case(" */"),
467 multispace0,
468 ),
469 |t| Some(ColumnConstraint::Srid(super::common::len_as_u32(t.2))),
470 );
471
472 let generated = map(
473 (
474 multispace0,
475 tag_no_case("GENERATED ALWAYS AS"),
476 multispace1,
477 tag("("),
478 take_until_unbalanced('(', ')'),
479 tag(")"),
480 multispace1,
481 alt((tag_no_case("VIRTUAL"), tag_no_case("STORED"))),
482 multispace0,
483 ),
484 |t| {
485 let query = str::from_utf8(t.4).unwrap().to_owned();
486 let stored = str::from_utf8(t.7).unwrap().eq_ignore_ascii_case("STORED");
487 Some(ColumnConstraint::Generated(query, stored))
488 },
489 );
490
491 alt((
492 not_null,
493 null,
494 auto_increment,
495 default,
496 primary_key,
497 unique,
498 character_set,
499 collate,
500 srid,
501 generated,
502 ))
503 .parse(i)
504}
505
506fn fixed_point(i: &[u8]) -> IResult<&[u8], Literal> {
507 let (remaining_input, (i, _, f)) = (digit1, tag("."), digit1).parse(i)?;
508
509 Ok((
510 remaining_input,
511 Literal::FixedPoint(Real {
512 integral: i32::from_str(str::from_utf8(i).unwrap()).unwrap(),
513 fractional: i32::from_str(str::from_utf8(f).unwrap()).unwrap(),
514 }),
515 ))
516}
517
518fn default(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
519 let (remaining_input, (_, _, _, def, _)) = (
520 multispace0,
521 tag_no_case("default"),
522 multispace1,
523 alt((
524 map(tag("''"), |_| Literal::String(String::from(""))),
525 string_literal,
526 fixed_point,
527 map(digit1, |d| {
528 let d_i64 = i64::from_str(str::from_utf8(d).unwrap()).unwrap();
529 Literal::Integer(d_i64)
530 }),
531 map(tag_no_case("null"), |_| Literal::Null),
532 map(
533 tag_no_case("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
534 |_| Literal::CurrentTimestamp,
535 ),
536 map(tag_no_case("current_timestamp"), |_| {
537 Literal::CurrentTimestamp
538 }),
539 map(tag_no_case("(now())"), |_| Literal::CurrentTimestamp),
540 )),
541 multispace0,
542 )
543 .parse(i)?;
544 if def == Literal::Null {
545 return Ok((remaining_input, None));
546 }
547 Ok((remaining_input, Some(ColumnConstraint::DefaultValue(def))))
548}
549
550pub fn creation(i: &[u8]) -> IResult<&[u8], CreateTableStatement> {
552 let (remaining_input, (_, _, _, _, table, _, _, _, fields, _, keys, _, _, _, options, _)) = (
553 tag_no_case("create"),
554 multispace1,
555 tag_no_case("table"),
556 multispace1,
557 schema_table_reference,
558 multispace0,
559 tag("("),
560 multispace0,
561 field_specification_list,
562 multispace0,
563 opt(key_specification_list),
564 multispace0,
565 tag(")"),
566 multispace0,
567 table_options,
568 statement_terminator,
569 )
570 .parse(i)?;
571 Ok((
572 remaining_input,
573 CreateTableStatement {
574 table,
575 fields,
576 keys,
577 options,
578 },
579 ))
580}