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::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();
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();
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();
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();
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();
286 let table = String::from_utf8(table.to_vec()).unwrap();
287 let on_delete = if let Some(on_delete) = on_delete {
288 let (_, _, _, on_delete) = on_delete;
289 Some(on_delete)
290 } else if let Some(on_delete) = on_delete2 {
291 let (_, _, _, on_delete) = on_delete;
292 Some(on_delete)
293 } else {
294 None
295 };
296 let on_update = if let Some(on_update) = on_update {
297 let (_, _, _, on_update) = on_update;
298 Some(on_update)
299 } else {
300 None
301 };
302 Ok((
303 remaining_input,
304 TableKey::Constraint(name, columns, table, foreign, on_delete, on_update),
305 ))
306}
307
308pub fn key_specification_list(i: &[u8]) -> IResult<&[u8], Vec<TableKey>> {
310 many1(terminated(key_specification, opt(ws_sep_comma))).parse(i)
311}
312
313fn field_specification(i: &[u8]) -> IResult<&[u8], ColumnSpecification> {
314 let (remaining_input, (column, field_type, constraints, comment, _)) = (
315 column_identifier_no_alias,
316 opt(delimited(multispace1, type_identifier, multispace0)),
317 many0(column_constraint),
318 opt(parse_comment),
319 opt(ws_sep_comma),
320 )
321 .parse(i)?;
322
323 let sql_type = match field_type {
324 None => SqlType::Text,
325 Some(ref t) => t.clone(),
326 };
327 Ok((
328 remaining_input,
329 ColumnSpecification {
330 column,
331 sql_type,
332 constraints: constraints.into_iter().flatten().collect(),
333 comment,
334 },
335 ))
336}
337
338pub fn field_specification_list(i: &[u8]) -> IResult<&[u8], Vec<ColumnSpecification>> {
340 many1(field_specification).parse(i)
341}
342
343pub fn column_constraint(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
345 let not_null = map(
346 delimited(multispace0, tag_no_case("not null"), multispace0),
347 |_| Some(ColumnConstraint::NotNull),
348 );
349 let null = map(
350 delimited(multispace0, tag_no_case("null"), multispace0),
351 |_| None,
352 );
353 let auto_increment = map(
354 delimited(multispace0, tag_no_case("auto_increment"), multispace0),
355 |_| Some(ColumnConstraint::AutoIncrement),
356 );
357 let primary_key = map(
358 delimited(multispace0, tag_no_case("primary key"), multispace0),
359 |_| Some(ColumnConstraint::PrimaryKey),
360 );
361 let unique = map(
362 delimited(multispace0, tag_no_case("unique"), multispace0),
363 |_| Some(ColumnConstraint::Unique),
364 );
365 let character_set = map(
366 preceded(
367 delimited(multispace0, tag_no_case("character set"), multispace1),
368 sql_identifier,
369 ),
370 |cs| {
371 let char_set = str::from_utf8(cs).unwrap().to_owned();
372 Some(ColumnConstraint::CharacterSet(char_set))
373 },
374 );
375 let collate = map(
376 preceded(
377 delimited(multispace0, tag_no_case("collate"), multispace1),
378 sql_identifier,
379 ),
380 |c| {
381 let collation = str::from_utf8(c).unwrap().to_owned();
382 Some(ColumnConstraint::Collation(collation))
383 },
384 );
385 let srid = map(
386 (
387 multispace0,
388 tag_no_case("/*!80003 SRID "),
389 digit1,
390 tag_no_case(" */"),
391 multispace0,
392 ),
393 |t| Some(ColumnConstraint::Srid(super::common::len_as_u32(t.2))),
394 );
395
396 let generated = map(
397 (
398 multispace0,
399 tag_no_case("GENERATED ALWAYS AS"),
400 multispace1,
401 tag("("),
402 take_until_unbalanced('(', ')'),
403 tag(")"),
404 multispace1,
405 alt((tag_no_case("VIRTUAL"), tag_no_case("STORED"))),
406 multispace0,
407 ),
408 |t| {
409 let query = str::from_utf8(t.4).unwrap().to_owned();
410 let stored = str::from_utf8(t.7).unwrap().eq_ignore_ascii_case("STORED");
411 Some(ColumnConstraint::Generated(query, stored))
412 },
413 );
414
415 alt((
416 not_null,
417 null,
418 auto_increment,
419 default,
420 primary_key,
421 unique,
422 character_set,
423 collate,
424 srid,
425 generated,
426 ))
427 .parse(i)
428}
429
430fn fixed_point(i: &[u8]) -> IResult<&[u8], Literal> {
431 let (remaining_input, (i, _, f)) = (digit1, tag("."), digit1).parse(i)?;
432
433 Ok((
434 remaining_input,
435 Literal::FixedPoint(Real {
436 integral: i32::from_str(str::from_utf8(i).unwrap()).unwrap(),
437 fractional: i32::from_str(str::from_utf8(f).unwrap()).unwrap(),
438 }),
439 ))
440}
441
442fn default(i: &[u8]) -> IResult<&[u8], Option<ColumnConstraint>> {
443 let (remaining_input, (_, _, _, def, _)) = (
444 multispace0,
445 tag_no_case("default"),
446 multispace1,
447 alt((
448 map(
449 delimited(tag("'"), take_until("'"), tag("'")),
450 |s: &[u8]| Literal::String(String::from_utf8(s.to_vec()).unwrap()),
451 ),
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("''"), |_| Literal::String(String::from(""))),
458 map(tag_no_case("null"), |_| Literal::Null),
459 map(
460 tag_no_case("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
461 |_| Literal::CurrentTimestamp,
462 ),
463 map(tag_no_case("current_timestamp"), |_| {
464 Literal::CurrentTimestamp
465 }),
466 map(tag_no_case("(now())"), |_| Literal::CurrentTimestamp),
467 )),
468 multispace0,
469 )
470 .parse(i)?;
471 if def == Literal::Null {
472 return Ok((remaining_input, None));
473 }
474 Ok((remaining_input, Some(ColumnConstraint::DefaultValue(def))))
475}
476
477pub fn creation(i: &[u8]) -> IResult<&[u8], CreateTableStatement> {
479 let (remaining_input, (_, _, _, _, table, _, _, _, fields, _, keys, _, _, _, options, _)) = (
480 tag_no_case("create"),
481 multispace1,
482 tag_no_case("table"),
483 multispace1,
484 schema_table_reference,
485 multispace0,
486 tag("("),
487 multispace0,
488 field_specification_list,
489 multispace0,
490 opt(key_specification_list),
491 multispace0,
492 tag(")"),
493 multispace0,
494 table_options,
495 statement_terminator,
496 )
497 .parse(i)?;
498 Ok((
499 remaining_input,
500 CreateTableStatement {
501 table,
502 fields,
503 keys,
504 options,
505 },
506 ))
507}