1use std::{borrow::Cow, str::FromStr};
2
3use eyre::Result;
4use nom::{
5 branch::alt,
6 bytes::complete::tag,
7 character::complete::{alpha1, alphanumeric1, multispace0},
8 combinator::{map, opt, recognize},
9 error::ParseError,
10 multi::{many0_count, separated_list1},
11 sequence::{delimited, pair},
12 IResult, Slice,
13};
14
15use crate::{cql::query::QueryString, error::DbError, frame::response::error::Error};
16
17pub fn query(query: &str) -> Result<QueryString, Error> {
18 let query = if query.contains("/*") {
19 Cow::Owned(filter_comments(query)?)
20 } else {
21 Cow::Borrowed(query)
22 };
23
24 let result = alt((
25 queries::use_query,
26 queries::select_query,
27 queries::insert_query,
28 queries::update_query,
29 queries::delete_query,
30 queries::create_keyspace_query,
31 queries::create_table_query,
32 queries::create_udt_query,
33 ))(query.as_ref())
34 .map(|(_, it)| it)?;
35
36 Ok(result)
37}
38
39fn filter_comments(mut query: &str) -> Result<String, Error> {
40 let mut output = String::new();
41 let start = query
42 .find("/*")
43 .expect("for start of comment to be present");
44 output += query.slice(..start);
45 loop {
46 let Some(finish) = query.find("*/") else {
47 return Err(Error::new(DbError::Invalid, "Unfinished comment"));
48 };
49 query = &query[finish + 2..];
50
51 if let Some(start) = query.find("/*") {
52 output += &query[..start];
53 } else {
54 output += query;
55 break;
56 }
57 }
58
59 Ok(output)
60}
61
62impl FromStr for QueryString {
63 type Err = Error;
64
65 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
66 query(s)
67 }
68}
69
70pub fn identifier(input: &str) -> IResult<&str, String> {
71 let ident = recognize(pair(
72 alt((alpha1, tag("_"))),
73 many0_count(alt((alphanumeric1, tag("_")))),
74 ));
75
76 map(ident, |it: &str| it.to_lowercase())(input)
77}
78
79pub fn cassandra_type(input: &str) -> IResult<&str, String> {
80 let ident = pair(
81 alt((alpha1, tag("_"))),
82 many0_count(alt((alphanumeric1, tag("_")))),
83 );
84 let generics = opt(delimited(
85 tag("<"),
86 separated_list1(ws(tag(",")), ident),
87 tag(">"),
88 ));
89 let ident = pair(
90 alt((alpha1, tag("_"))),
91 many0_count(alt((alphanumeric1, tag("_")))),
92 );
93 map(recognize(pair(ident, generics)), |it: &str| it.to_owned())(input)
94}
95
96pub fn ws<'a, F, O, E>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
97where
98 F: FnMut(&'a str) -> IResult<&'a str, O, E> + 'a,
99 E: ParseError<&'a str>,
100{
101 delimited(multispace0, inner, multispace0)
102}
103
104mod queries {
105 use nom::{
106 branch::alt,
107 bytes::complete::{tag, tag_no_case},
108 character::complete::{multispace0, multispace1, u32},
109 combinator::{map, opt, value},
110 multi::{many_till, separated_list0, separated_list1},
111 sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
112 IResult,
113 };
114
115 use super::{cassandra_type, identifier, ws};
116 use crate::cql::{
117 functions::CqlFunction,
118 literal::Literal,
119 query::{
120 ColumnSelector, CreateKeyspaceQuery, CreateTableQuery, CreateTypeQuery, DeleteQuery,
121 InsertQuery, QueryString, QueryValue, SelectExpression, SelectQuery, WhereClosure,
122 },
123 types::PreCqlType,
124 };
125
126 fn query_value(input: &str) -> IResult<&str, QueryValue> {
127 let blank = map(tag("?"), |_| QueryValue::Blankslate);
128 let named_bind = map(preceded(tag(":"), identifier), |_| QueryValue::Blankslate);
129 let literal = map(super::literal::parse, QueryValue::Literal);
130 alt((blank, literal, named_bind))(input)
131 }
132
133 fn select_expression(input: &str) -> IResult<&str, SelectExpression> {
134 let all = map(tag("*"), |_| SelectExpression::All);
135
136 let function = alt((
142 value(CqlFunction::ToJson, tag_no_case("toJson")),
143 value(CqlFunction::FromJson, tag_no_case("fromJson")),
144 ));
145 let column1 = map(identifier, |name| ColumnSelector {
146 name,
147 ..Default::default()
148 });
149 let column3 = pair(function, delimited(tag("("), identifier, tag(")")));
150 let column3 = map(column3, |(function, name)| ColumnSelector {
151 name,
152 function: Some(function),
153 ..Default::default()
154 });
155
156 let column = pair(
157 alt((column3, column1)),
158 opt(preceded(ws(tag_no_case("as")), identifier)),
159 );
160 let column = map(column, |(column, alias)| ColumnSelector { alias, ..column });
161
162 let columns = map(
163 separated_list0(pair(tag(","), multispace0), column),
164 SelectExpression::Columns,
165 );
166
167 alt((all, columns))(input)
168 }
169
170 fn where_closure(input: &str) -> IResult<&str, WhereClosure> {
171 let (rest, _) = terminated(tag_no_case("where"), multispace1)(input)?;
172
173 let statement = separated_pair(identifier, ws(tag("=")), query_value);
174
175 let (rest, statements) = separated_list1(ws(tag("AND")), statement)(rest)?;
176
177 Ok((rest, WhereClosure { statements }))
178 }
179
180 pub fn select_query(input: &str) -> IResult<&str, QueryString> {
181 let (rest, _) = terminated(tag_no_case("select"), multispace1)(input)?;
182 let (rest, json) = map(opt(terminated(tag_no_case("json"), multispace1)), |it| {
183 it.is_some()
184 })(rest)?;
185
186 let (rest, columns) = select_expression(rest)?;
187 let (rest, _) = delimited(multispace0, alt((tag("from"), tag("FROM"))), multispace0)(rest)?;
188 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
189 let (rest, table) = terminated(identifier, multispace0)(rest)?;
190
191 let (rest, closure) = opt(terminated(where_closure, multispace0))(rest)?;
192 let limit = preceded(
194 terminated(tag_no_case("limit"), multispace1),
195 terminated(map(u32, |it| it as usize), multispace0),
196 );
197 let (rest, limit) = opt(limit)(rest)?;
198
199 Ok((
200 rest,
201 QueryString::Select(SelectQuery {
202 table,
203 keyspace,
204 columns,
205 r#where: closure.unwrap_or_default(),
206 limit,
207 json,
208 }),
209 ))
210 }
211
212 pub fn insert_query(input: &str) -> IResult<&str, QueryString> {
213 let (rest, _) = terminated(tag_no_case("insert"), multispace1)(input)?;
214 let (rest, _) = terminated(tag_no_case("into"), multispace1)(rest)?;
215 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
216 let (rest, table) = terminated(identifier, multispace0)(rest)?;
217 let (rest, columns) = terminated(
218 delimited(
219 ws(tag("(")),
220 separated_list0(ws(tag(",")), identifier),
221 ws(tag(")")),
222 ),
223 multispace0,
224 )(rest)?;
225 let (rest, _) = terminated(tag_no_case("values"), multispace0)(rest)?;
226 let (rest, values) = terminated(
227 delimited(
228 ws(tag("(")),
229 separated_list0(ws(tag(",")), query_value),
230 ws(tag(")")),
231 ),
232 multispace0,
233 )(rest)?;
234
235 Ok((
236 rest,
237 QueryString::Insert(InsertQuery {
238 table,
239 keyspace,
240 columns,
241 values,
242 }),
243 ))
244 }
245
246 pub fn use_query(input: &str) -> IResult<&str, QueryString> {
247 let (rest, _) = terminated(alt((tag("use"), tag("USE"))), multispace1)(input)?;
248 let (rest, keyspace) =
249 alt((identifier, delimited(tag("\""), identifier, tag("\""))))(rest)?;
250 Ok((rest, QueryString::Use { keyspace }))
251 }
252
253 pub fn create_keyspace_query(input: &str) -> IResult<&str, QueryString> {
254 let (rest, _) = terminated(
255 alt((tag("create keyspace"), tag("CREATE KEYSPACE"))),
256 multispace1,
257 )(input)?;
258 let (rest, if_not_exists) =
259 opt(terminated(tag_no_case("IF NOT EXISTS"), multispace1))(rest)?;
260
261 let (rest, keyspace) = terminated(identifier, multispace1)(rest)?;
262 let (rest, _) = terminated(alt((tag("with"), tag("WITH"))), multispace1)(rest)?;
263 let replication = alt((tag("replication"), tag("REPLICATION")));
264
265 let (rest, (_, replication)) =
266 separated_pair(replication, ws(tag("=")), super::literal::parse)(rest)?;
267
268 Ok((
269 rest,
270 QueryString::CreateKeyspace(CreateKeyspaceQuery {
271 keyspace,
272 ignore_existence: if_not_exists.is_some(),
273 replication,
274 }),
275 ))
276 }
277
278 pub fn create_table_query(rest: &str) -> IResult<&str, QueryString> {
279 fn table_options(rest: &str) -> IResult<&str, Vec<(String, Literal)>> {
280 let ordering = map(
281 preceded(
282 tag("CLUSTERING ORDER BY"),
283 delimited(
284 ws(tag("(")),
285 separated_list1(
286 ws(tag(",")),
287 pair(
288 identifier,
289 alt((
290 map(ws(tag("ASC")), |_| Literal::Bool(true)),
291 map(ws(tag("DESC")), |_| Literal::Bool(false)),
292 )),
293 ),
294 ),
295 ws(tag(")")),
296 ),
297 ),
298 |t| {
299 (
300 "clustering order by".to_owned(),
301 Literal::Map(t.into_iter().collect()),
302 )
303 },
304 );
305
306 let compact_storage = map(tag("COMPACT STORAGE"), |_| {
307 ("compact storage".to_owned(), Literal::Bool(true))
308 });
309
310 let key_value = separated_pair(identifier, ws(tag("=")), super::literal::parse);
311
312 separated_list1(ws(tag("AND")), alt((ordering, compact_storage, key_value)))(rest)
313 }
314
315 fn column_definition(rest: &str) -> IResult<&str, (String, PreCqlType, Option<&str>)> {
316 tuple((
317 terminated(identifier, multispace0),
318 terminated(super::types::parse, multispace0),
319 opt(terminated(tag("PRIMARY KEY"), multispace0)),
320 ))(rest)
321 }
322
323 fn column_definition_without_primary(rest: &str) -> IResult<&str, (String, PreCqlType)> {
324 tuple((
325 terminated(identifier, multispace0),
326 terminated(super::types::parse, multispace0),
327 ))(rest)
328 }
329
330 fn primary_key_definition(rest: &str) -> IResult<&str, (Vec<String>, Vec<String>)> {
332 let partition_key = delimited(
333 ws(tag("(")),
334 separated_list1(ws(tag(",")), identifier),
335 ws(tag(")")),
336 );
337 let composite_key = delimited(
338 ws(tag("(")),
339 pair(
340 terminated(partition_key, opt(ws(tag(",")))),
341 separated_list0(ws(tag(",")), identifier),
342 ),
343 ws(tag(")")),
344 );
345
346 let compound_key = map(
348 delimited(
349 ws(tag("(")),
350 separated_list1(ws(tag(",")), identifier),
351 ws(tag(")")),
352 ),
353 |it| {
354 let (head, tail) = it.split_first().unwrap();
355
356 (vec![head.clone()], tail.to_vec())
357 },
358 );
359
360 let mut primary_key_definition =
361 preceded(ws(tag("PRIMARY KEY")), alt((composite_key, compound_key)));
362
363 primary_key_definition(rest)
364 }
365
366 let (rest, _) =
367 terminated(alt((tag("create table"), tag("CREATE TABLE"))), multispace1)(rest)?;
368 let (rest, if_not_exists) =
369 opt(terminated(tag_no_case("IF NOT EXISTS"), multispace1))(rest)?;
370 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
371 let (rest, table) = terminated(identifier, multispace0)(rest)?;
372
373 let with_primary_key_definition = map(
374 many_till(
375 terminated(column_definition_without_primary, ws(tag(","))),
376 primary_key_definition,
377 ),
378 |(c, (pk, ck))| (c, pk, ck),
379 );
380 let with_primary_key_inline = map(separated_list1(ws(tag(",")), column_definition), |c| {
381 let pk = c
382 .iter()
383 .find_map(|(name, _, pk)| {
384 if pk.is_some() {
385 Some(name.clone())
386 } else {
387 None
388 }
389 })
390 .into_iter()
391 .collect();
392 let columns = c.into_iter().map(|(n, t, _)| (n, t)).collect();
393 (columns, pk, vec![])
394 });
395
396 let (rest, (columns, primary_key, clustering_keys)) = delimited(
397 ws(tag("(")),
398 alt((with_primary_key_definition, with_primary_key_inline)),
399 ws(tag(")")),
400 )(rest)?;
401
402 let (rest, options) = opt(preceded(ws(tag("WITH")), table_options))(rest)?;
403
404 Ok((
405 rest,
406 QueryString::CreateTable(CreateTableQuery {
407 keyspace,
408 table,
409 ignore_existence: if_not_exists.is_some(),
410 columns,
411 partition_keys: primary_key,
412 clustering_keys,
413 options: options.unwrap_or_default(),
414 }),
415 ))
416 }
417
418 pub fn update_query(rest: &str) -> IResult<&str, QueryString> {
419 let (rest, _) = terminated(tag_no_case("update"), multispace1)(rest)?;
420 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
421 let (rest, table) = terminated(identifier, multispace1)(rest)?;
422 let (rest, _) = terminated(tag_no_case("set"), multispace1)(rest)?;
423
424 let (rest, columns_specification) = terminated(
425 separated_list1(
426 ws(tag(",")),
427 separated_pair(identifier, ws(tag("=")), query_value),
428 ),
429 multispace1,
430 )(rest)?;
431 let (rest, _) = terminated(tag_no_case("where"), multispace1)(rest)?;
432
433 let (rest, row_specification) = terminated(
434 separated_list1(
435 ws(tag("AND")),
436 separated_pair(identifier, ws(tag("=")), query_value),
437 ),
438 multispace0,
439 )(rest)?;
440
441 let (columns, values) = columns_specification
442 .into_iter()
443 .chain(row_specification)
444 .unzip();
445
446 Ok((
447 rest,
448 QueryString::Insert(InsertQuery {
449 table,
450 keyspace,
451 columns,
452 values,
453 }),
454 ))
455 }
456
457 pub fn delete_query(rest: &str) -> IResult<&str, QueryString> {
458 let (rest, _) = terminated(tag_no_case("delete"), multispace1)(rest)?;
459
460 let columns_list = terminated(separated_list1(ws(tag(",")), identifier), multispace1);
461
462 let from_tag = terminated(tag_no_case("from"), multispace1);
463 let from_tag_empty = map(terminated(tag_no_case("from"), multispace1), |_| vec![]);
464 let (rest, columns) = alt((terminated(columns_list, from_tag), from_tag_empty))(rest)?;
465
466 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
467 let (rest, table) = terminated(identifier, multispace1)(rest)?;
468 let (rest, _) = terminated(tag_no_case("where"), multispace1)(rest)?;
469
470 let (rest, statements) = terminated(
471 separated_list1(
472 ws(tag("AND")),
473 separated_pair(identifier, ws(tag("=")), query_value),
474 ),
475 multispace0,
476 )(rest)?;
477
478 let r#where = WhereClosure { statements };
479
480 Ok((
481 rest,
482 QueryString::Delete(DeleteQuery {
483 table,
484 keyspace,
485 columns,
486 r#where,
487 }),
488 ))
489 }
490
491 pub fn create_udt_query(rest: &str) -> IResult<&str, QueryString> {
492 let (rest, _) = terminated(tag_no_case("create type"), multispace1)(rest)?;
493 let (rest, _) = opt(terminated(tag_no_case("if not exists"), multispace1))(rest)?;
494
495 let (rest, keyspace) = opt(terminated(identifier, tag(".")))(rest)?;
496 let (rest, table) = terminated(identifier, multispace0)(rest)?;
497
498 let ident_type = tuple((
499 terminated(identifier, multispace0),
500 terminated(cassandra_type, multispace0),
501 ));
502 let (rest, columns) = delimited(
503 ws(tag("(")),
504 separated_list1(ws(tag(",")), ident_type),
505 ws(tag(")")),
506 )(rest)?;
507
508 Ok((
509 rest,
510 QueryString::CreateType(CreateTypeQuery {
511 keyspace,
512 name: table,
513 columns,
514 }),
515 ))
516 }
517
518 #[test]
519 fn test_select_expression() {
520 let (r, p) = select_expression("a, toJson(x) as y, toJson(z), b").unwrap();
521 assert!(r.is_empty());
522 println!("{p:?}");
523 }
524}
525
526mod types {
527 use std::str::FromStr;
528
529 use nom::{
530 bytes::{complete::tag, streaming::take_while},
531 character::is_alphanumeric,
532 error::ErrorKind,
533 multi::separated_list1,
534 sequence::terminated,
535 IResult,
536 };
537
538 use super::{identifier, ws};
539 use crate::cql::types::{NativeType, PreCqlType};
540
541 type ParseResult<'a, T> = IResult<&'a str, T, nom::error::Error<&'a str>>;
542
543 pub fn parse(p: &str) -> ParseResult<PreCqlType> {
544 if let Ok((_rest, _)) = tag::<_, _, nom::error::Error<_>>("frozen<")(p) {
545 let (p, inner_type) = parse(p)?;
546 let frozen_type = inner_type.freeze();
547 Ok((p, frozen_type))
548 } else if let Ok((p, _)) = tag::<_, _, nom::error::Error<_>>("map<")(p) {
549 let (p, key) = terminated(parse, ws(tag(",")))(p)?;
550 let (p, value) = parse(p)?;
551 let (p, _) = tag(">")(p)?;
552
553 let typ = PreCqlType::Map {
554 frozen: false,
555 key: Box::new(key),
556 value: Box::new(value),
557 };
558
559 Ok((p, typ))
560 } else if let Ok((p, _)) = tag::<_, _, nom::error::Error<_>>("list<")(p) {
561 let (p, inner_type) = parse(p)?;
562 let (p, _) = tag(">")(p)?;
563
564 let typ = PreCqlType::List {
565 frozen: false,
566 item: Box::new(inner_type),
567 };
568
569 Ok((p, typ))
570 } else if let Ok((p, _)) = tag::<_, _, nom::error::Error<_>>("set<")(p) {
571 let (p, inner_type) = parse(p)?;
572 let (p, _) = tag(">")(p)?;
573
574 let typ = PreCqlType::Set {
575 frozen: false,
576 item: Box::new(inner_type),
577 };
578
579 Ok((p, typ))
580 } else if let Ok((p, _)) = tag::<_, _, nom::error::Error<_>>("tuple<")(p) {
581 let (p, types) = separated_list1(ws(tag(",")), parse)(p)?;
582 let (p, _) = tag(">")(p)?;
583 Ok((p, PreCqlType::Tuple(types)))
584 } else if let Ok((p, typ)) = parse_native_type(p) {
585 Ok((p, PreCqlType::Native(typ)))
586 } else if let Ok((name, p)) = parse_user_defined_type(p) {
587 let typ = PreCqlType::UserDefinedType {
588 frozen: false,
589 name: name.to_string(),
590 };
591 Ok((p, typ))
592 } else {
593 panic!("invalid cql type")
595 }
596 }
597
598 fn parse_native_type(p: &str) -> ParseResult<NativeType> {
599 let (p, tok) = identifier(p)?;
600 let typ = NativeType::from_str(&tok)
601 .map_err(|_| nom::Err::Error(nom::error::make_error(p, ErrorKind::Tag)))?;
602 Ok((p, typ))
603 }
604
605 fn parse_user_defined_type(p: &str) -> ParseResult<&str> {
606 let (p, tok) =
610 take_while(|c| is_alphanumeric(c as u8) || c == '.' || c == '_' || c == '$')(p)?;
611
612 if tok.is_empty() {
613 return Err(nom::Err::Error(nom::error::make_error(p, ErrorKind::Tag)));
614 }
615 Ok((p, tok))
616 }
617}
618
619mod literal {
620 use std::str::FromStr;
621
622 use nom::{
623 branch::alt,
624 bytes::complete::{tag, tag_no_case, take_until, take_while_m_n},
625 character::complete::multispace0,
626 combinator::{map, recognize},
627 multi::separated_list0,
628 sequence::{delimited, separated_pair, terminated, tuple},
629 IResult,
630 };
631 use uuid::Uuid;
632
633 use super::ws;
634 use crate::cql::literal::Literal;
635
636 pub fn parse(input: &str) -> IResult<&str, Literal> {
637 alt((
638 uuid_literal,
639 null_literal,
640 map_literal,
641 string_literal,
642 number_literal,
643 float_literal,
644 list_literal,
645 ))(input)
646 }
647
648 fn string_literal(input: &str) -> IResult<&str, Literal> {
649 map(
650 delimited(tag("'"), take_until("'"), tag("'")),
651 |it: &str| Literal::String(it.to_owned()),
652 )(input)
653 }
654
655 fn number_literal(input: &str) -> IResult<&str, Literal> {
656 map(nom::character::complete::i64, Literal::Number)(input)
657 }
658
659 fn null_literal(input: &str) -> IResult<&str, Literal> {
660 map(tag_no_case("null"), |_| Literal::Null)(input)
661 }
662
663 fn float_literal(input: &str) -> IResult<&str, Literal> {
664 map(nom::number::complete::double, Literal::Float)(input)
665 }
666
667 fn list_literal(input: &str) -> IResult<&str, Literal> {
668 let values = separated_list0(ws(tag(",")), ws(parse));
669 map(delimited(ws(tag("[")), values, ws(tag("]"))), Literal::List)(input)
670 }
671
672 fn map_literal(input: &str) -> IResult<&str, Literal> {
673 let quoted_string = delimited(tag("'"), take_until("'"), tag("'"));
674 let value = separated_pair(ws(quoted_string), tag(":"), ws(parse));
675
676 let values = separated_list0(terminated(tag(","), multispace0), value);
677
678 map(
679 delimited(tag("{"), values, tag("}")),
680 |it: Vec<(&str, Literal)>| {
681 Literal::Map(
682 it.into_iter()
683 .map(|(key, value)| (key.to_owned(), value))
684 .collect(),
685 )
686 },
687 )(input)
688 }
689
690 fn uuid_literal(input: &str) -> IResult<&str, Literal> {
691 let lower_hex = tuple((
692 take_while_m_n(8, 8, is_lower_hex_digit),
693 tag("-"),
694 take_while_m_n(4, 4, is_lower_hex_digit),
695 tag("-"),
696 take_while_m_n(4, 4, is_lower_hex_digit),
697 tag("-"),
698 take_while_m_n(4, 4, is_lower_hex_digit),
699 tag("-"),
700 take_while_m_n(12, 12, is_lower_hex_digit),
701 ));
702 let upper_hex = tuple((
703 take_while_m_n(8, 8, is_upper_hex_digit),
704 tag("-"),
705 take_while_m_n(4, 4, is_upper_hex_digit),
706 tag("-"),
707 take_while_m_n(4, 4, is_upper_hex_digit),
708 tag("-"),
709 take_while_m_n(4, 4, is_upper_hex_digit),
710 tag("-"),
711 take_while_m_n(12, 12, is_upper_hex_digit),
712 ));
713 let parser = alt((lower_hex, upper_hex));
714 let (rest, lit) = recognize(parser)(input)?;
715 let uuid = Uuid::from_str(lit).expect("to be valid uuid");
716 Ok((rest, Literal::Uuid(uuid)))
717 }
718
719 #[inline]
720 fn is_lower_hex_digit(i: char) -> bool {
721 ('a'..='f').contains(&i) || i.is_ascii_digit()
722 }
723
724 #[inline]
725 fn is_upper_hex_digit(i: char) -> bool {
726 ('A'..='F').contains(&i) || i.is_ascii_digit()
727 }
728
729 #[cfg(test)]
730 mod tests {
731 use super::{map_literal, parse};
732
733 #[test]
734 fn test_map() {
735 let v = "{ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }";
736 let (_, m) = map_literal(v).unwrap();
737 println!("{m:?}");
738 }
739
740 #[test]
741 fn test_uuid() {
742 let v = "6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47";
743 let (_, m) = parse(v).unwrap();
744 println!("{m:?}");
745 }
746 }
747}
748
749#[cfg(test)]
750mod tests {
751 use super::query;
752 use crate::cql::{
753 functions::CqlFunction,
754 parser::filter_comments,
755 query::{ColumnSelector, QueryString, SelectExpression, SelectQuery},
756 };
757
758 #[test]
759 fn test_select() {
760 let q = "select keyspace_name, table_name, column_name, kind, position, type from system_schema.columns";
761 let s = query(q).unwrap();
762 println!("{s:#?}");
763 }
764
765 #[test]
766 fn test_select_where() {
767 let q = "SELECT field1,field2,field3 FROM table WHERE field0 = ?";
768 let s = query(q).unwrap();
769 println!("{s:#?}");
770 }
771
772 #[test]
773 fn test_select_where_limit() {
774 let q = "SELECT field1,field2,field3 FROM table WHERE field0 = ? limit 500";
775 let QueryString::Select(s) = query(q).unwrap() else {
776 panic!("was supposed to be parsed as select query")
777 };
778 assert_eq!(s.limit, Some(500));
779 println!("{s:#?}");
780 }
781
782 #[test]
783 fn test_insert_into() {
784 let q = "INSERT INTO table (field1,field2,field3,field4) VALUES (?,?,?,?)";
785 let i = query(q).unwrap();
786 println!("{i:#?}")
787 }
788
789 #[test]
790 fn test_create_keyspace() {
791 let q = "CREATE KEYSPACE keyspace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }";
792 let k = query(q).unwrap();
793 println!("{k:#?}");
794 }
795
796 #[test]
797 fn test_update_query() {
798 let q = "UPDATE table SET field1=?,field2=?,field3=? WHERE field0=?";
799 let k = query(q).unwrap();
800 println!("{k:#?}");
801 }
802
803 #[test]
804 fn test_delete_row_query() {
805 let q = "DELETE FROM table WHERE field1=? AND field2=? AND field3=?";
806 let k = query(q).unwrap();
807 println!("{k:#?}");
808 }
809
810 #[test]
811 fn test_delete_columns_query() {
812 let q = "DELETE field4, field5 FROM table WHERE field1=? AND field2=? AND field3=?";
813 let k = query(q).unwrap();
814 println!("{k:#?}");
815 }
816
817 #[test]
818 fn test_create_table() {
819 let q = r#"CREATE TABLE keyspace.table (
820 field1 uuid,
821 field2 text,
822 field3 text,
823 field4 text,
824 field5 timestamp,
825 field6 boolean,
826 field7 text,
827 field8 timestamp,
828 PRIMARY KEY ((field1, field2))
829 ) WITH bloom_filter_fp_chance = 0.01
830 AND comment = ''
831 AND parameter = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
832 AND crc_check_chance = 1.0
833 AND gc_grace_seconds = 1234
834 AND speculative_retry = '99PERCENTILE'
835 "#;
836
837 let k = query(q).unwrap();
838 println!("{k:#?}");
839 }
840
841 #[test]
842 fn test_udt() {
843 let q = r#"CREATE TYPE cycling.basic_info (
844 birthday timestamp,
845 nationality text,
846 weight text,
847 height text
848 );
849 "#;
850 let k = query(q).unwrap();
851 println!("{k:#?}");
852 }
853
854 #[test]
855 fn test_named_bind() {
856 let q = "INSERT INTO table (field1,field2,field3,field4,field5,field6) VALUES (:field1,:field2,:field3,:field4,:field5,:field6)";
857 let k = query(q).unwrap();
858 println!("{k:#?}")
859 }
860
861 #[test]
862 fn test_update_with_lit_null() {
863 let q = "UPDATE table SET field1=null,field2=null WHERE field3=? AND field4=?;";
864 let k = query(q).unwrap();
865 println!("{k:?}");
866 }
867
868 #[test]
869 fn test_regressions() {
870 let qs = [ "CREATE TABLE cycling.cyclist_name ( id UUID PRIMARY KEY, lastname text, firstname text );", "INSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47, 'KRUIKSWIJK','Steven');"];
871
872 for q in qs {
873 let _ = query(q).unwrap();
874 }
875 }
876
877 #[test]
878 fn test_select_json() {
879 let q = "SELECT JSON field1,field2,field3 FROM table WHERE field0 = ? limit 500";
880 let QueryString::Select(s) = query(q).unwrap() else {
881 panic!("was supposed to be parsed as select query")
882 };
883 assert!(s.json);
884 }
885
886 #[test]
887 fn name_alias() {
888 let q = "SELECT field1 as field2 FROM table";
889 let QueryString::Select(SelectQuery {
890 columns: SelectExpression::Columns(c),
891 ..
892 }) = query(q).unwrap()
893 else {
894 panic!("was supposed to be parsed as select query")
895 };
896 assert_eq!(
897 c[0],
898 ColumnSelector {
899 name: "field1".to_string(),
900 alias: Some("field2".to_string()),
901 function: None,
902 }
903 )
904 }
905
906 #[test]
907 fn function() {
908 let q = "SELECT toJson(field1), field2 FROM table";
909 let QueryString::Select(SelectQuery {
910 columns: SelectExpression::Columns(c),
911 ..
912 }) = query(q).unwrap()
913 else {
914 panic!("was supposed to be parsed as select query")
915 };
916 assert_eq!(
917 c[0],
918 ColumnSelector {
919 name: "field1".to_string(),
920 alias: None,
921 function: Some(CqlFunction::ToJson),
922 }
923 )
924 }
925
926 #[test]
927 fn test_filter_comments() {
928 let s = "hello /* blabla */ world /* blabla */!";
929 assert_eq!(filter_comments(s).unwrap(), "hello world !");
930 }
931
932 #[test]
933 fn query_with_comment() {
934 let q = "SELECT table_name AS name,\n comment,\n bloom_filter_fp_chance,\n toJson(caching) as caching,\n /* cdc, */\n toJson(compaction) as compaction,\n toJson(compression) as compression,\n crc_check_chance,\n dclocal_read_repair_chance,\n default_time_to_live,\n speculative_retry,\n /* additional_write_policy, */\n gc_grace_seconds,\n max_index_interval,\n memtable_flush_period_in_ms,\n min_index_interval,\n read_repair_chance\nFROM system_schema.tables\nWHERE keyspace_name = ?";
935 let k = query(q).unwrap();
936 println!("{k:?}");
937 }
938}