1use nom::branch::alt;
2use nom::bytes::complete::{tag, tag_no_case};
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 constraint,
98 ))
99 .parse(i)
100}
101
102fn full_text_key(i: &[u8]) -> IResult<&[u8], TableKey> {
103 let (remaining_input, (_, _, _, _, name, _, columns, _, parser, _)) = (
104 tag_no_case("fulltext"),
105 multispace1,
106 alt((tag_no_case("key"), tag_no_case("index"))),
107 multispace1,
108 sql_identifier,
109 multispace0,
110 delimited(
111 tag("("),
112 delimited(multispace0, index_col_list, multispace0),
113 tag(")"),
114 ),
115 multispace0,
116 opt(delimited(
117 tag("/*!50100 WITH PARSER"),
118 delimited(multispace0, sql_identifier, multispace0),
119 tag("*/"),
120 )),
121 multispace0,
122 )
123 .parse(i)?;
124
125 let name = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
126 let parser = parser.map(|v| String::from_utf8(v.to_vec()).unwrap());
127 Ok((
128 remaining_input,
129 TableKey::FulltextKey(name, columns, parser),
130 ))
131}
132
133fn primary_key(i: &[u8]) -> IResult<&[u8], TableKey> {
134 let (remaining_input, (_, _, columns, _, _, _)) = (
135 tag_no_case("primary key"),
136 multispace0,
137 delimited(
138 tag("("),
139 delimited(multispace0, index_col_list, multispace0),
140 tag(")"),
141 ),
142 opt(map(
143 preceded(multispace1, tag_no_case("auto_increment")),
144 |_| (),
145 )),
146 multispace0,
147 opt(tag_no_case("USING BTREE")),
148 )
149 .parse(i)?;
150
151 Ok((remaining_input, TableKey::PrimaryKey(columns)))
152}
153
154fn unique(i: &[u8]) -> IResult<&[u8], TableKey> {
155 let (remaining_input, (_, _, _, name, _, columns, _, _)) = (
157 tag_no_case("unique"),
158 opt(preceded(
159 multispace1,
160 alt((tag_no_case("key"), tag_no_case("index"))),
161 )),
162 multispace0,
163 sql_identifier,
164 multispace0,
165 delimited(
166 tag("("),
167 delimited(multispace0, index_col_list, multispace0),
168 tag(")"),
169 ),
170 multispace0,
171 opt(tag_no_case("USING BTREE")),
172 )
173 .parse(i)?;
174
175 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
176 Ok((remaining_input, TableKey::UniqueKey(n, columns)))
177}
178
179fn key_or_index(i: &[u8]) -> IResult<&[u8], TableKey> {
180 let (remaining_input, (_, _, name, _, columns, _, _)) = (
181 alt((tag_no_case("key"), tag_no_case("index"))),
182 multispace0,
183 sql_identifier,
184 multispace0,
185 delimited(
186 tag("("),
187 delimited(multispace0, index_col_list, multispace0),
188 tag(")"),
189 ),
190 multispace0,
191 opt(tag_no_case("USING BTREE")),
192 )
193 .parse(i)?;
194
195 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
196 Ok((remaining_input, TableKey::Key(n, columns)))
197}
198
199fn spatial(i: &[u8]) -> IResult<&[u8], TableKey> {
200 let (remaining_input, (_, _, _, name, _, columns)) = (
201 tag_no_case("spatial"),
202 opt(preceded(
203 multispace1,
204 alt((tag_no_case("key"), tag_no_case("index"))),
205 )),
206 multispace0,
207 sql_identifier,
208 multispace0,
209 delimited(
210 tag("("),
211 delimited(multispace0, index_col_list, multispace0),
212 tag(")"),
213 ),
214 )
215 .parse(i)?;
216
217 let n = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
218 Ok((remaining_input, TableKey::SpatialKey(n, columns)))
219}
220
221fn constraint(i: &[u8]) -> IResult<&[u8], TableKey> {
222 let (
223 remaining_input,
224 (
225 _,
226 _,
227 name,
228 _,
229 _,
230 _,
231 columns,
232 _,
233 _,
234 _,
235 table,
236 _,
237 foreign,
238 on_delete,
239 on_update,
240 on_delete2,
241 ),
242 ) = (
243 tag_no_case("CONSTRAINT"),
244 multispace1,
245 sql_identifier,
246 multispace1,
247 tag_no_case("FOREIGN KEY"),
248 multispace0,
249 delimited(
250 tag("("),
251 delimited(multispace0, index_col_list, multispace0),
252 tag(")"),
253 ),
254 multispace1,
255 tag_no_case("REFERENCES"),
256 multispace1,
257 sql_identifier,
258 multispace0,
259 delimited(
260 tag("("),
261 delimited(multispace0, index_col_list, multispace0),
262 tag(")"),
263 ),
264 opt((
265 multispace1,
266 tag_no_case("ON DELETE"),
267 multispace1,
268 reference_option,
269 )),
270 opt((
271 multispace1,
272 tag_no_case("ON UPDATE"),
273 multispace1,
274 reference_option,
275 )),
276 opt((
277 multispace1,
278 tag_no_case("ON DELETE"),
279 multispace1,
280 reference_option,
281 )),
282 )
283 .parse(i)?;
284
285 let name = String::from_utf8(name.to_vec()).unwrap().replace("``", "`");
286 let table = String::from_utf8(table.to_vec())
287 .unwrap()
288 .replace("``", "`");
289 let on_delete = if let Some(on_delete) = on_delete {
290 let (_, _, _, on_delete) = on_delete;
291 Some(on_delete)
292 } else if let Some(on_delete) = on_delete2 {
293 let (_, _, _, on_delete) = on_delete;
294 Some(on_delete)
295 } else {
296 None
297 };
298 let on_update = if let Some(on_update) = on_update {
299 let (_, _, _, on_update) = on_update;
300 Some(on_update)
301 } else {
302 None
303 };
304 Ok((
305 remaining_input,
306 TableKey::Constraint(name, columns, table, foreign, on_delete, on_update),
307 ))
308}
309
310pub fn key_specification_list(i: &[u8]) -> IResult<&[u8], Vec<TableKey>> {
312 many1(terminated(key_specification, opt(ws_sep_comma))).parse(i)
313}
314
315fn field_specification(i: &[u8]) -> IResult<&[u8], ColumnSpecification> {
316 let (remaining_input, (column, field_type, constraints, comment, _)) = (
317 column_identifier_no_alias,
318 opt(delimited(multispace1, type_identifier, multispace0)),
319 many0(column_constraint),
320 opt(parse_comment),
321 opt(ws_sep_comma),
322 )
323 .parse(i)?;
324
325 let sql_type = match field_type {
326 None => SqlType::Text,
327 Some(ref t) => t.clone(),
328 };
329 Ok((
330 remaining_input,
331 ColumnSpecification {
332 column,
333 sql_type,
334 constraints: constraints.into_iter().flatten().collect(),
335 comment,
336 },
337 ))
338}
339
340pub fn field_specification_list(i: &[u8]) -> IResult<&[u8], Vec<ColumnSpecification>> {
342 many1(field_specification).parse(i)
343}
344
345pub fn column_constraint(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
347 let not_null = map(
348 delimited(multispace0, tag_no_case("not null"), multispace0),
349 |_| Some(ColumnConstraint::NotNull),
350 );
351 let null = map(
352 delimited(multispace0, tag_no_case("null"), multispace0),
353 |_| None,
354 );
355 let auto_increment = map(
356 delimited(multispace0, tag_no_case("auto_increment"), multispace0),
357 |_| Some(ColumnConstraint::AutoIncrement),
358 );
359 let primary_key = map(
360 delimited(multispace0, tag_no_case("primary key"), multispace0),
361 |_| Some(ColumnConstraint::PrimaryKey),
362 );
363 let unique = map(
364 delimited(multispace0, tag_no_case("unique"), multispace0),
365 |_| Some(ColumnConstraint::Unique),
366 );
367 let character_set = map(
368 preceded(
369 delimited(multispace0, tag_no_case("character set"), multispace1),
370 sql_identifier,
371 ),
372 |cs| {
373 let char_set = str::from_utf8(cs).unwrap().to_owned();
374 Some(ColumnConstraint::CharacterSet(char_set))
375 },
376 );
377 let collate = map(
378 preceded(
379 delimited(multispace0, tag_no_case("collate"), multispace1),
380 sql_identifier,
381 ),
382 |c| {
383 let collation = str::from_utf8(c).unwrap().to_owned();
384 Some(ColumnConstraint::Collation(collation))
385 },
386 );
387 let srid = map(
388 (
389 multispace0,
390 tag_no_case("/*!80003 SRID "),
391 digit1,
392 tag_no_case(" */"),
393 multispace0,
394 ),
395 |t| Some(ColumnConstraint::Srid(super::common::len_as_u32(t.2))),
396 );
397
398 let generated = map(
399 (
400 multispace0,
401 tag_no_case("GENERATED ALWAYS AS"),
402 multispace1,
403 tag("("),
404 take_until_unbalanced('(', ')'),
405 tag(")"),
406 multispace1,
407 alt((tag_no_case("VIRTUAL"), tag_no_case("STORED"))),
408 multispace0,
409 ),
410 |t| {
411 let query = str::from_utf8(t.4).unwrap().to_owned();
412 let stored = str::from_utf8(t.7).unwrap().eq_ignore_ascii_case("STORED");
413 Some(ColumnConstraint::Generated(query, stored))
414 },
415 );
416
417 alt((
418 not_null,
419 null,
420 auto_increment,
421 default,
422 primary_key,
423 unique,
424 character_set,
425 collate,
426 srid,
427 generated,
428 ))
429 .parse(i)
430}
431
432fn fixed_point(i: &[u8]) -> IResult<&[u8], Literal> {
433 let (remaining_input, (i, _, f)) = (digit1, tag("."), digit1).parse(i)?;
434
435 Ok((
436 remaining_input,
437 Literal::FixedPoint(Real {
438 integral: i32::from_str(str::from_utf8(i).unwrap()).unwrap(),
439 fractional: i32::from_str(str::from_utf8(f).unwrap()).unwrap(),
440 }),
441 ))
442}
443
444fn default(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
445 let (remaining_input, (_, _, _, def, _)) = (
446 multispace0,
447 tag_no_case("default"),
448 multispace1,
449 alt((
450 map(tag("''"), |_| Literal::String(String::from(""))),
451 string_literal,
452 fixed_point,
453 map(digit1, |d| {
454 let d_i64 = i64::from_str(str::from_utf8(d).unwrap()).unwrap();
455 Literal::Integer(d_i64)
456 }),
457 map(tag_no_case("null"), |_| Literal::Null),
458 map(
459 tag_no_case("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
460 |_| Literal::CurrentTimestamp,
461 ),
462 map(tag_no_case("current_timestamp"), |_| {
463 Literal::CurrentTimestamp
464 }),
465 map(tag_no_case("(now())"), |_| Literal::CurrentTimestamp),
466 )),
467 multispace0,
468 )
469 .parse(i)?;
470 if def == Literal::Null {
471 return Ok((remaining_input, None));
472 }
473 Ok((remaining_input, Some(ColumnConstraint::DefaultValue(def))))
474}
475
476pub fn creation(i: &[u8]) -> IResult<&[u8], CreateTableStatement> {
478 let (remaining_input, (_, _, _, _, table, _, _, _, fields, _, keys, _, _, _, options, _)) = (
479 tag_no_case("create"),
480 multispace1,
481 tag_no_case("table"),
482 multispace1,
483 schema_table_reference,
484 multispace0,
485 tag("("),
486 multispace0,
487 field_specification_list,
488 multispace0,
489 opt(key_specification_list),
490 multispace0,
491 tag(")"),
492 multispace0,
493 table_options,
494 statement_terminator,
495 )
496 .parse(i)?;
497 Ok((
498 remaining_input,
499 CreateTableStatement {
500 table,
501 fields,
502 keys,
503 options,
504 },
505 ))
506}