1use core::hash::Hash;
4
5use crate::{
6 DiffSetBuilder, PatchsetFormat, SchemaWithPK, Value, builders::operation::Operation,
7 schema::NamedColumns,
8};
9use alloc::borrow::Cow;
10use alloc::string::String;
11use alloc::vec;
12use alloc::vec::Vec;
13
14use super::lexer::{Lexer, LexerError, Token, TokenKind};
15
16#[non_exhaustive]
18#[derive(Debug, Clone, PartialEq, thiserror::Error)]
19pub enum ParseError<'a> {
20 #[error("Lexer error: {0}")]
22 Lexer(#[from] LexerError),
23 #[error("Unexpected token {found:?} at position {pos}, expected {expected}")]
25 UnexpectedToken {
26 expected: &'static str,
28 found: TokenKind<'a>,
30 pos: usize,
32 },
33 #[error("Unexpected end of input, expected {expected}")]
35 UnexpectedEof {
36 expected: &'static str,
38 },
39 #[error("Empty column list in CREATE TABLE")]
41 EmptyColumnList,
42 #[error("Duplicate column name: {0}")]
44 DuplicateColumn(String),
45 #[error("Unknown column '{column}' in PRIMARY KEY constraint")]
47 UnknownPKColumn {
48 column: String,
50 },
51 #[error("Unknown table name: {0}")]
53 UnknownTable(&'a str),
54 #[error("Unknown column name: {0}")]
56 UnknownColumn(&'a str),
57 #[error("Missing WHERE clause in {statement}")]
59 MissingWhere {
60 statement: &'static str,
62 },
63 #[error("WHERE clause on non-primary key column '{column}'")]
65 WhereNonPKColumn {
66 column: &'a str,
68 },
69 #[error("column list names {expected} columns but VALUES has {found}")]
71 ValuesLengthMismatch {
72 expected: usize,
74 found: usize,
76 },
77 #[error("OR is not supported in WHERE. Use separate statements for multiple rows")]
79 OrInWhere,
80 #[error("WHERE must name all {expected} primary key columns, but only {found} were provided")]
82 IncompleteWhereKey {
83 expected: usize,
85 found: usize,
87 },
88}
89
90pub(crate) struct Parser<'input, 'builder, T: SchemaWithPK, S> {
93 lexer: Lexer<'input>,
94 builder: &'builder DiffSetBuilder<PatchsetFormat, T, S, Vec<u8>>,
96 pending: Vec<PendingOp<T, S>>,
98}
99
100type PendingOp<T, S> = (
103 T,
104 Vec<Value<S, Vec<u8>>>,
105 Operation<PatchsetFormat, S, Vec<u8>>,
106);
107
108impl<'input, 'builder, T: NamedColumns, S: Clone + Hash + Eq + AsRef<str> + for<'a> From<&'a str>>
109 Parser<'input, 'builder, T, S>
110{
111 #[must_use]
113 pub(crate) fn new(
114 input: &'input str,
115 builder: &'builder DiffSetBuilder<PatchsetFormat, T, S, Vec<u8>>,
116 ) -> Self {
117 Self {
118 lexer: Lexer::new(input),
119 builder,
120 pending: Vec::new(),
121 }
122 }
123
124 pub(crate) fn into_pending(self) -> Vec<PendingOp<T, S>> {
126 self.pending
127 }
128
129 pub(crate) fn digest_all(&mut self) -> Result<(), ParseError<'input>> {
135 loop {
136 while self.lexer.peek()?.kind == TokenKind::Semicolon {
138 self.lexer.next()?;
139 }
140
141 if self.lexer.peek()?.kind == TokenKind::Eof {
142 break;
143 }
144
145 self.digest_statement()?;
146 }
147
148 Ok(())
149 }
150
151 fn digest_statement(&mut self) -> Result<(), ParseError<'input>> {
153 let token = self.lexer.peek()?;
154 match &token.kind {
155 TokenKind::Insert => self.digest_insert(),
156 TokenKind::Update => self.digest_update(),
157 TokenKind::Delete => self.digest_delete(),
158 other => Err(ParseError::UnexpectedToken {
159 expected: "INSERT, UPDATE, or DELETE",
160 found: other.clone(),
161 pos: token.pos,
162 }),
163 }
164 }
165
166 fn digest_insert(&mut self) -> Result<(), ParseError<'input>> {
168 self.expect(&TokenKind::Insert)?;
169 self.expect(&TokenKind::Into)?;
170
171 let table = self.expect_table()?;
172
173 let mut column_identifiers: Vec<u16> = Vec::new();
177 if self.lexer.peek()?.kind == TokenKind::LParen {
178 self.lexer.next()?;
179
180 loop {
181 column_identifiers.push(self.expect_column(&table)?.0);
182 if self.lexer.peek()?.kind != TokenKind::Comma {
183 break;
184 }
185 self.lexer.next()?;
186 }
187
188 self.expect(&TokenKind::RParen)?;
189 }
190
191 self.expect(&TokenKind::Values)?;
192 self.expect(&TokenKind::LParen)?;
193
194 let mut values = vec![Value::Null; table.number_of_columns()];
195 let mut pks = vec![Value::Null; table.number_of_primary_keys()];
196
197 if column_identifiers.is_empty() {
198 for (col_idx, value_ref) in values.iter_mut().enumerate() {
200 if col_idx > 0 {
201 self.expect(&TokenKind::Comma)?;
202 }
203 *value_ref = self.parse_value()?;
204 if let Some(pk_idx) = table.primary_key_index(col_idx) {
205 pks[pk_idx] = (*value_ref).clone();
206 }
207 }
208 } else {
209 let expected = column_identifiers.len();
211 let mut parsed = 0usize;
212 for column_index in &column_identifiers {
213 values[usize::from(*column_index)] = self.parse_value()?;
214 if let Some(primary_key_index) = table.primary_key_index(usize::from(*column_index))
215 {
216 pks[primary_key_index] = values[usize::from(*column_index)].clone();
217 }
218 parsed += 1;
219 if self.lexer.peek()?.kind != TokenKind::Comma {
220 break;
221 }
222 self.lexer.next()?;
223 }
224 if parsed != expected {
225 return Err(ParseError::ValuesLengthMismatch {
226 expected,
227 found: parsed,
228 });
229 }
230 }
231
232 self.expect(&TokenKind::RParen)?;
233
234 self.pending.push((
235 table,
236 pks,
237 Operation::Insert {
238 values,
239 indirect: false,
240 },
241 ));
242
243 Ok(())
244 }
245
246 fn digest_update(&mut self) -> Result<(), ParseError<'input>> {
248 self.expect(&TokenKind::Update)?;
249
250 let table = self.expect_table()?;
251 self.expect(&TokenKind::Set)?;
252
253 let mut new_values = vec![((), None); table.number_of_columns()];
254
255 loop {
256 let (col_idx, _) = self.expect_column(&table)?;
257 self.expect(&TokenKind::Equals)?;
258 let val = self.parse_value()?;
259 new_values[usize::from(col_idx)] = ((), Some(val));
260
261 if self.lexer.peek()?.kind != TokenKind::Comma {
262 break;
263 }
264 self.lexer.next()?;
265 }
266
267 if self.lexer.peek()?.kind != TokenKind::Where {
268 return Err(ParseError::MissingWhere {
269 statement: "UPDATE",
270 });
271 }
272
273 let n_pk = table.number_of_primary_keys();
274 let mut pk = vec![Value::Null; n_pk];
275 let mut pk_seen = vec![false; n_pk];
276
277 self.digest_where(&table, |col_idx, col_name, val| {
278 if let Some(primary_key_index) = table.primary_key_index(usize::from(col_idx)) {
279 pk[primary_key_index] = val.clone();
280 pk_seen[primary_key_index] = true;
281 Ok(())
282 } else {
283 Err(ParseError::WhereNonPKColumn { column: col_name })
284 }
285 })?;
286
287 let found = pk_seen.iter().filter(|&&s| s).count();
288 if found != n_pk {
289 return Err(ParseError::IncompleteWhereKey {
290 expected: n_pk,
291 found,
292 });
293 }
294
295 self.pending.push((
296 table,
297 pk,
298 Operation::Update {
299 values: new_values,
300 indirect: false,
301 },
302 ));
303
304 Ok(())
305 }
306
307 fn digest_delete(&mut self) -> Result<(), ParseError<'input>> {
309 self.expect(&TokenKind::Delete)?;
310 self.expect(&TokenKind::From)?;
311
312 let table = self.expect_table()?;
313
314 if self.lexer.peek()?.kind != TokenKind::Where {
315 return Err(ParseError::MissingWhere {
316 statement: "DELETE",
317 });
318 }
319
320 let n_pk = table.number_of_primary_keys();
321 let mut pks = vec![Value::Null; n_pk];
322 let mut pk_seen = vec![false; n_pk];
323
324 self.digest_where(&table, |col_idx, col_name, val| {
325 if let Some(primary_key_index) = table.primary_key_index(usize::from(col_idx)) {
326 pks[primary_key_index] = val.clone();
327 pk_seen[primary_key_index] = true;
328 Ok(())
329 } else {
330 Err(ParseError::WhereNonPKColumn { column: col_name })
331 }
332 })?;
333
334 let found = pk_seen.iter().filter(|&&s| s).count();
335 if found != n_pk {
336 return Err(ParseError::IncompleteWhereKey {
337 expected: n_pk,
338 found,
339 });
340 }
341
342 self.pending.push((
343 table,
344 pks,
345 Operation::Delete {
346 data: (),
347 indirect: false,
348 },
349 ));
350
351 Ok(())
352 }
353
354 fn digest_where<D>(&mut self, table: &T, mut digestor: D) -> Result<(), ParseError<'input>>
357 where
358 D: FnMut(u16, &'input str, Value<S, Vec<u8>>) -> Result<(), ParseError<'input>>,
359 {
360 self.expect(&TokenKind::Where)?;
361
362 loop {
363 let (col_idx, col_name) = self.expect_column(table)?;
364 self.expect(&TokenKind::Equals)?;
365 let val = self.parse_value()?;
366 digestor(col_idx, col_name, val)?;
367
368 if self.lexer.peek()?.kind != TokenKind::And {
369 if self.lexer.peek()?.kind == TokenKind::Or {
370 return Err(ParseError::OrInWhere);
371 }
372 break;
373 }
374 self.lexer.next()?;
375 }
376
377 Ok(())
378 }
379
380 fn parse_value(&mut self) -> Result<Value<S, Vec<u8>>, ParseError<'input>> {
382 let token = self.lexer.next()?;
383 match token.kind {
384 TokenKind::Null => Ok(Value::Null),
385 TokenKind::IntegerLiteral(v) => Ok(Value::Integer(v)),
386 TokenKind::RealLiteral(v) => Ok(Value::Real(v)),
387 TokenKind::StringLiteral(s) => {
388 let text: S = match s {
389 Cow::Borrowed(b) => S::from(b),
390 Cow::Owned(o) => S::from(o.as_str()),
391 };
392 Ok(Value::Text(text))
393 }
394 TokenKind::BlobLiteral(b) => Ok(Value::Blob(b)),
395 TokenKind::Minus => {
396 let next = self.lexer.next()?;
398 match next.kind {
399 TokenKind::IntegerLiteral(v) => Ok(Value::Integer(-v)),
400 TokenKind::RealLiteral(v) => {
401 let neg = -v;
405 #[allow(
406 clippy::cast_precision_loss,
407 clippy::float_cmp,
408 clippy::cast_possible_truncation
409 )]
410 if neg >= i64::MIN as f64
411 && neg <= i64::MAX as f64
412 && neg == (neg as i64 as f64)
413 {
414 Ok(Value::Integer(neg as i64))
415 } else {
416 Ok(Value::Real(neg))
417 }
418 }
419 other => Err(ParseError::UnexpectedToken {
420 expected: "number after minus",
421 found: other,
422 pos: next.pos,
423 }),
424 }
425 }
426 other => Err(ParseError::UnexpectedToken {
427 expected: "value (NULL, number, string, or blob)",
428 found: other,
429 pos: token.pos,
430 }),
431 }
432 }
433
434 fn expect(
436 &mut self,
437 expected: &TokenKind<'input>,
438 ) -> Result<Token<'input>, ParseError<'input>> {
439 let token = self.lexer.next()?;
440 if core::mem::discriminant(&token.kind) == core::mem::discriminant(expected) {
441 Ok(token)
442 } else {
443 Err(ParseError::UnexpectedToken {
444 expected: expected.static_name(),
445 found: token.kind,
446 pos: token.pos,
447 })
448 }
449 }
450
451 fn expect_column(&mut self, table: &T) -> Result<(u16, &'input str), ParseError<'input>> {
453 let column_name = self.expect_identifier()?;
454 #[allow(clippy::cast_possible_truncation)]
455 table
456 .column_index(column_name)
457 .map(|idx| (idx as u16, column_name))
458 .ok_or(ParseError::UnknownColumn(column_name))
459 }
460
461 fn expect_table(&mut self) -> Result<T, ParseError<'input>> {
463 let table_name = self.expect_identifier()?;
464 self.builder
465 .table(table_name)
466 .cloned()
467 .ok_or(ParseError::UnknownTable(table_name))
468 }
469
470 fn expect_identifier(&mut self) -> Result<&'input str, ParseError<'input>> {
472 let token = self.lexer.next()?;
473 match token.kind {
474 TokenKind::Identifier(name) => Ok(name),
475 TokenKind::Insert => Ok("INSERT"),
477 TokenKind::Into => Ok("INTO"),
478 TokenKind::Values => Ok("VALUES"),
479 TokenKind::Update => Ok("UPDATE"),
480 TokenKind::Set => Ok("SET"),
481 TokenKind::Delete => Ok("DELETE"),
482 TokenKind::From => Ok("FROM"),
483 TokenKind::Where => Ok("WHERE"),
484 TokenKind::And => Ok("AND"),
485 TokenKind::Or => Ok("OR"),
486 TokenKind::Primary => Ok("PRIMARY"),
487 TokenKind::Key => Ok("KEY"),
488 TokenKind::Null => Ok("NULL"),
489 TokenKind::Integer => Ok("INTEGER"),
490 TokenKind::Int => Ok("INT"),
491 TokenKind::Real => Ok("REAL"),
492 TokenKind::Text => Ok("TEXT"),
493 TokenKind::Blob => Ok("BLOB"),
494 TokenKind::Not => Ok("NOT"),
495 other => Err(ParseError::UnexpectedToken {
496 expected: "identifier",
497 found: other,
498 pos: token.pos,
499 }),
500 }
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use alloc::string::String;
507 use alloc::vec::Vec;
508
509 use crate::schema::SimpleTable;
510 use crate::{DiffSetBuilder, PatchsetFormat};
511
512 fn make_builder(
513 tables: &[SimpleTable],
514 ) -> DiffSetBuilder<PatchsetFormat, SimpleTable, String, Vec<u8>> {
515 let mut builder = DiffSetBuilder::default();
516 for t in tables {
517 builder.add_table(t);
518 }
519 builder
520 }
521
522 #[test]
523 fn test_digest_insert() {
524 let users = SimpleTable::new("users", &["id", "name"], &[0]);
525 let mut builder = make_builder(&[users]);
526 builder
527 .digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice')")
528 .unwrap();
529 assert_eq!(builder.len(), 1);
530 assert_ne!(builder.build(), [] as [u8; 0]);
531 }
532
533 #[test]
534 fn test_digest_insert_positional() {
535 let users = SimpleTable::new("users", &["id", "name"], &[0]);
536 let mut builder = make_builder(&[users]);
537 builder
538 .digest_sql("INSERT INTO users VALUES (1, 'Alice')")
539 .unwrap();
540 assert_eq!(builder.len(), 1);
541 }
542
543 #[test]
544 fn test_digest_update() {
545 let users = SimpleTable::new("users", &["id", "name"], &[0]);
546 let mut builder = make_builder(&[users]);
547 builder
548 .digest_sql("UPDATE users SET name = 'Bob' WHERE id = 1")
549 .unwrap();
550 assert_eq!(builder.len(), 1);
551 assert_ne!(builder.build(), [] as [u8; 0]);
552 }
553
554 #[test]
555 fn test_digest_delete() {
556 let users = SimpleTable::new("users", &["id", "name"], &[0]);
557 let mut builder = make_builder(&[users]);
558 builder
559 .digest_sql("DELETE FROM users WHERE id = 1")
560 .unwrap();
561 assert_eq!(builder.len(), 1);
562 assert_ne!(builder.build(), [] as [u8; 0]);
563 }
564
565 #[test]
566 fn test_digest_delete_rejects_non_pk_in_where() {
567 let users = SimpleTable::new("users", &["id", "name", "status"], &[0]);
568 let mut builder = make_builder(&[users]);
569 let result = builder.digest_sql("DELETE FROM users WHERE id = 1 AND status = 'active'");
570 assert!(result.is_err());
571 }
572
573 #[test]
574 fn test_digest_multiple_dml() {
575 let users = SimpleTable::new("users", &["id", "name"], &[0]);
576 let mut builder = make_builder(&[users]);
577 builder
578 .digest_sql(
579 "INSERT INTO users (id, name) VALUES (1, 'Alice');\
580 INSERT INTO users (id, name) VALUES (2, 'Bob');\
581 DELETE FROM users WHERE id = 1;",
582 )
583 .unwrap();
584 assert_eq!(builder.len(), 1);
586 assert_ne!(builder.build(), [] as [u8; 0]);
587 }
588
589 #[test]
590 fn test_digest_create_table_rejected() {
591 let mut builder: DiffSetBuilder<PatchsetFormat, SimpleTable, String, Vec<u8>> =
592 DiffSetBuilder::default();
593 let result = builder.digest_sql("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
594 assert!(result.is_err());
595 }
596
597 #[test]
598 fn test_digest_blob_value() {
599 let t = SimpleTable::new("t", &["data"], &[0]);
600 let mut builder = make_builder(&[t]);
601 builder
602 .digest_sql("INSERT INTO t (data) VALUES (X'DEADBEEF')")
603 .unwrap();
604 assert_eq!(builder.len(), 1);
605 }
606
607 #[test]
608 fn test_digest_null_value() {
609 let t = SimpleTable::new("t", &["id", "v"], &[0]);
610 let mut builder = make_builder(&[t]);
611 builder
612 .digest_sql("INSERT INTO t (id, v) VALUES (1, NULL)")
613 .unwrap();
614 assert_eq!(builder.len(), 1);
615 }
616
617 #[test]
618 fn test_digest_negative_numbers() {
619 let t = SimpleTable::new("t", &["a", "b"], &[0]);
620 let mut builder = make_builder(&[t]);
621 builder
622 .digest_sql("INSERT INTO t (a, b) VALUES (-42, -3.14)")
623 .unwrap();
624 assert_eq!(builder.len(), 1);
625 }
626
627 #[test]
628 fn test_digest_keyword_column_names() {
629 let cols = [
633 "INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE", "FROM", "WHERE", "AND",
634 "PRIMARY", "KEY", "NULL", "INTEGER", "INT", "REAL", "TEXT", "BLOB", "NOT",
635 ];
636 let t = SimpleTable::new("kwords", &cols, &[0]);
637 let mut builder = make_builder(&[t]);
638 builder
639 .digest_sql(
640 "INSERT INTO kwords (insert, into, values, update, set, delete, from, where, and, primary, key, null, integer, int, real, text, blob, not) \
641 VALUES (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)",
642 )
643 .unwrap();
644 assert_eq!(builder.len(), 1);
645 }
646
647 use crate::builders::sql::ParseError;
650
651 #[test]
652 fn test_digest_insert_missing_into() {
653 let t = SimpleTable::new("t", &["id"], &[0]);
654 let mut builder = make_builder(&[t]);
655 let err = builder.digest_sql("INSERT FROM t").unwrap_err();
656 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
657 }
658
659 #[test]
660 fn test_digest_insert_unknown_table() {
661 let t = SimpleTable::new("t", &["id"], &[0]);
662 let mut builder = make_builder(&[t]);
663 let err = builder
664 .digest_sql("INSERT INTO unknown_table VALUES (1)")
665 .unwrap_err();
666 assert!(matches!(err, ParseError::UnknownTable("unknown_table")));
667 }
668
669 #[test]
670 fn test_digest_update_missing_where() {
671 let t = SimpleTable::new("t", &["id", "v"], &[0]);
672 let mut builder = make_builder(&[t]);
673 let err = builder.digest_sql("UPDATE t SET v = 1").unwrap_err();
674 assert!(matches!(
675 err,
676 ParseError::MissingWhere {
677 statement: "UPDATE"
678 }
679 ));
680 }
681
682 #[test]
683 fn test_digest_delete_missing_where() {
684 let t = SimpleTable::new("t", &["id"], &[0]);
685 let mut builder = make_builder(&[t]);
686 let err = builder.digest_sql("DELETE FROM t").unwrap_err();
687 assert!(matches!(
688 err,
689 ParseError::MissingWhere {
690 statement: "DELETE"
691 }
692 ));
693 }
694
695 #[test]
696 fn test_digest_update_where_non_pk_column() {
697 let t = SimpleTable::new("t", &["id", "v"], &[0]);
698 let mut builder = make_builder(&[t]);
699 let err = builder
700 .digest_sql("UPDATE t SET v = 2 WHERE v = 1")
701 .unwrap_err();
702 assert!(matches!(err, ParseError::WhereNonPKColumn { column: "v" }));
703 }
704
705 #[test]
706 fn test_digest_unexpected_top_level_token() {
707 let t = SimpleTable::new("t", &["id"], &[0]);
708 let mut builder = make_builder(&[t]);
709 let err = builder.digest_sql("SELECT 1").unwrap_err();
710 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
711 }
712
713 #[test]
714 fn test_digest_expect_identifier_rejects_value_token() {
715 let t = SimpleTable::new("t", &["id"], &[0]);
716 let mut builder = make_builder(&[t]);
717 let err = builder.digest_sql("INSERT INTO 42 VALUES (1)").unwrap_err();
719 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
720 }
721}