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#[derive(Debug, Clone, PartialEq, thiserror::Error)]
18pub enum ParseError<'a> {
19 #[error("Lexer error: {0}")]
21 Lexer(#[from] LexerError),
22 #[error("Unexpected token {found:?} at position {pos}, expected {expected}")]
24 UnexpectedToken {
25 expected: &'static str,
27 found: TokenKind<'a>,
29 pos: usize,
31 },
32 #[error("Unexpected end of input, expected {expected}")]
34 UnexpectedEof {
35 expected: &'static str,
37 },
38 #[error("Empty column list in CREATE TABLE")]
40 EmptyColumnList,
41 #[error("Duplicate column name: {0}")]
43 DuplicateColumn(String),
44 #[error("Unknown column '{column}' in PRIMARY KEY constraint")]
46 UnknownPKColumn {
47 column: String,
49 },
50 #[error("Unknown table name: {0}")]
52 UnknownTable(&'a str),
53 #[error("Unknown column name: {0}")]
55 UnknownColumn(&'a str),
56 #[error("Missing WHERE clause in {statement}")]
58 MissingWhere {
59 statement: &'static str,
61 },
62 #[error("WHERE clause on non-primary key column '{column}'")]
64 WhereNonPKColumn {
65 column: &'a str,
67 },
68}
69
70pub(crate) struct Parser<'input, 'builder, T: SchemaWithPK, S> {
72 lexer: Lexer<'input>,
73 builder: &'builder mut DiffSetBuilder<PatchsetFormat, T, S, Vec<u8>>,
74}
75
76impl<'input, 'builder, T: NamedColumns, S: Clone + Hash + Eq + AsRef<str> + for<'a> From<&'a str>>
77 Parser<'input, 'builder, T, S>
78{
79 #[must_use]
81 pub(crate) fn new(
82 input: &'input str,
83 builder: &'builder mut DiffSetBuilder<PatchsetFormat, T, S, Vec<u8>>,
84 ) -> Self {
85 Self {
86 lexer: Lexer::new(input),
87 builder,
88 }
89 }
90
91 pub(crate) fn digest_all(&mut self) -> Result<(), ParseError<'input>> {
97 loop {
98 while self.lexer.peek()?.kind == TokenKind::Semicolon {
100 self.lexer.next()?;
101 }
102
103 if self.lexer.peek()?.kind == TokenKind::Eof {
104 break;
105 }
106
107 self.digest_statement()?;
108 }
109
110 Ok(())
111 }
112
113 fn digest_statement(&mut self) -> Result<(), ParseError<'input>> {
115 let token = self.lexer.peek()?;
116 match &token.kind {
117 TokenKind::Insert => self.digest_insert(),
118 TokenKind::Update => self.digest_update(),
119 TokenKind::Delete => self.digest_delete(),
120 other => Err(ParseError::UnexpectedToken {
121 expected: "INSERT, UPDATE, or DELETE",
122 found: other.clone(),
123 pos: token.pos,
124 }),
125 }
126 }
127
128 fn digest_insert(&mut self) -> Result<(), ParseError<'input>> {
130 self.expect(&TokenKind::Insert)?;
131 self.expect(&TokenKind::Into)?;
132
133 let table = self.expect_table()?;
134
135 let mut column_identifiers: Vec<u16> = Vec::new();
141 if self.lexer.peek()?.kind == TokenKind::LParen {
143 self.lexer.next()?;
144
145 loop {
146 column_identifiers.push(self.expect_column(&table)?.0);
147 if self.lexer.peek()?.kind != TokenKind::Comma {
148 break;
149 }
150 self.lexer.next()?;
151 }
152
153 self.expect(&TokenKind::RParen)?;
154 }
155
156 self.expect(&TokenKind::Values)?;
157 self.expect(&TokenKind::LParen)?;
158
159 let mut values = vec![Value::Null; table.number_of_columns()];
160 let mut pks = vec![Value::Null; table.number_of_primary_keys()];
161
162 if column_identifiers.is_empty() {
163 for (col_idx, value_ref) in values.iter_mut().enumerate() {
165 if col_idx > 0 {
166 self.expect(&TokenKind::Comma)?;
167 }
168 *value_ref = self.parse_value()?;
169 if let Some(pk_idx) = table.primary_key_index(col_idx) {
170 pks[pk_idx] = (*value_ref).clone();
171 }
172 }
173 } else {
174 for column_index in column_identifiers {
175 values[usize::from(column_index)] = self.parse_value()?;
176 if let Some(primary_key_index) = table.primary_key_index(usize::from(column_index))
177 {
178 pks[primary_key_index] = values[usize::from(column_index)].clone();
179 }
180 if self.lexer.peek()?.kind != TokenKind::Comma {
181 break;
182 }
183 self.lexer.next()?;
184 }
185 }
186
187 self.expect(&TokenKind::RParen)?;
188
189 self.builder.add_operation(
190 &table,
191 pks,
192 Operation::Insert {
193 values,
194 indirect: false,
195 },
196 );
197
198 Ok(())
199 }
200
201 fn digest_update(&mut self) -> Result<(), ParseError<'input>> {
203 self.expect(&TokenKind::Update)?;
204
205 let table = self.expect_table()?;
206 self.expect(&TokenKind::Set)?;
207
208 let mut new_values = vec![((), None); table.number_of_columns()];
209
210 loop {
212 let (col_idx, _) = self.expect_column(&table)?;
213 self.expect(&TokenKind::Equals)?;
214 let val = self.parse_value()?;
215 new_values[usize::from(col_idx)] = ((), Some(val));
216
217 if self.lexer.peek()?.kind != TokenKind::Comma {
218 break;
219 }
220 self.lexer.next()?;
221 }
222
223 if self.lexer.peek()?.kind != TokenKind::Where {
225 return Err(ParseError::MissingWhere {
226 statement: "UPDATE",
227 });
228 }
229
230 let mut pk = vec![Value::Null; table.number_of_primary_keys()];
231
232 self.digest_where(&table, |col_idx, col_name, val| {
233 if let Some(primary_key_index) = table.primary_key_index(usize::from(col_idx)) {
234 pk[primary_key_index] = val.clone();
235 Ok(())
236 } else {
237 Err(ParseError::WhereNonPKColumn { column: col_name })
238 }
239 })?;
240
241 self.builder.add_operation(
242 &table,
243 pk,
244 Operation::Update {
245 values: new_values,
246 indirect: false,
247 },
248 );
249
250 Ok(())
251 }
252
253 fn digest_delete(&mut self) -> Result<(), ParseError<'input>> {
255 self.expect(&TokenKind::Delete)?;
256 self.expect(&TokenKind::From)?;
257
258 let table = self.expect_table()?;
259 let mut pks = vec![Value::Null; table.number_of_primary_keys()];
260
261 if self.lexer.peek()?.kind != TokenKind::Where {
263 return Err(ParseError::MissingWhere {
264 statement: "DELETE",
265 });
266 }
267 self.digest_where(&table, |col_idx, col_name, val| {
268 if let Some(primary_key_index) = table.primary_key_index(usize::from(col_idx)) {
269 pks[primary_key_index] = val.clone();
270 Ok(())
271 } else {
272 Err(ParseError::WhereNonPKColumn { column: col_name })
273 }
274 })?;
275
276 self.builder.add_operation(
277 &table,
278 pks,
279 Operation::Delete {
280 data: (),
281 indirect: false,
282 },
283 );
284
285 Ok(())
286 }
287
288 fn digest_where<D>(&mut self, table: &T, mut digestor: D) -> Result<(), ParseError<'input>>
290 where
291 D: FnMut(u16, &'input str, Value<S, Vec<u8>>) -> Result<(), ParseError<'input>>,
292 {
293 self.expect(&TokenKind::Where)?;
294
295 loop {
296 let (col_idx, col_name) = self.expect_column(table)?;
297 self.expect(&TokenKind::Equals)?;
298 let val = self.parse_value()?;
299 digestor(col_idx, col_name, val)?;
300
301 if self.lexer.peek()?.kind != TokenKind::And {
302 break;
303 }
304 self.lexer.next()?;
305 }
306
307 Ok(())
308 }
309
310 fn parse_value(&mut self) -> Result<Value<S, Vec<u8>>, ParseError<'input>> {
312 let token = self.lexer.next()?;
313 match token.kind {
314 TokenKind::Null => Ok(Value::Null),
315 TokenKind::IntegerLiteral(v) => Ok(Value::Integer(v)),
316 TokenKind::RealLiteral(v) => Ok(Value::Real(v)),
317 TokenKind::StringLiteral(s) => {
318 let text: S = match s {
319 Cow::Borrowed(b) => S::from(b),
320 Cow::Owned(o) => S::from(o.as_str()),
321 };
322 Ok(Value::Text(text))
323 }
324 TokenKind::BlobLiteral(b) => Ok(Value::Blob(b)),
325 TokenKind::Minus => {
326 let next = self.lexer.next()?;
328 match next.kind {
329 TokenKind::IntegerLiteral(v) => Ok(Value::Integer(-v)),
330 TokenKind::RealLiteral(v) => {
331 let neg = -v;
335 #[allow(
336 clippy::cast_precision_loss,
337 clippy::float_cmp,
338 clippy::cast_possible_truncation
339 )]
340 if neg >= i64::MIN as f64
341 && neg <= i64::MAX as f64
342 && neg == (neg as i64 as f64)
343 {
344 Ok(Value::Integer(neg as i64))
345 } else {
346 Ok(Value::Real(neg))
347 }
348 }
349 other => Err(ParseError::UnexpectedToken {
350 expected: "number after minus",
351 found: other,
352 pos: next.pos,
353 }),
354 }
355 }
356 other => Err(ParseError::UnexpectedToken {
357 expected: "value (NULL, number, string, or blob)",
358 found: other,
359 pos: token.pos,
360 }),
361 }
362 }
363
364 fn expect(
366 &mut self,
367 expected: &TokenKind<'input>,
368 ) -> Result<Token<'input>, ParseError<'input>> {
369 let token = self.lexer.next()?;
370 if core::mem::discriminant(&token.kind) == core::mem::discriminant(expected) {
371 Ok(token)
372 } else {
373 Err(ParseError::UnexpectedToken {
374 expected: expected.static_name(),
375 found: token.kind,
376 pos: token.pos,
377 })
378 }
379 }
380
381 fn expect_column(&mut self, table: &T) -> Result<(u16, &'input str), ParseError<'input>> {
383 let column_name = self.expect_identifier()?;
384 #[allow(clippy::cast_possible_truncation)]
385 table
386 .column_index(column_name)
387 .map(|idx| (idx as u16, column_name))
388 .ok_or(ParseError::UnknownColumn(column_name))
389 }
390
391 fn expect_table(&mut self) -> Result<T, ParseError<'input>> {
393 let table_name = self.expect_identifier()?;
394 self.builder
395 .table(table_name)
396 .cloned()
397 .ok_or(ParseError::UnknownTable(table_name))
398 }
399
400 fn expect_identifier(&mut self) -> Result<&'input str, ParseError<'input>> {
402 let token = self.lexer.next()?;
403 match token.kind {
404 TokenKind::Identifier(name) => Ok(name),
405 TokenKind::Insert => Ok("INSERT"),
407 TokenKind::Into => Ok("INTO"),
408 TokenKind::Values => Ok("VALUES"),
409 TokenKind::Update => Ok("UPDATE"),
410 TokenKind::Set => Ok("SET"),
411 TokenKind::Delete => Ok("DELETE"),
412 TokenKind::From => Ok("FROM"),
413 TokenKind::Where => Ok("WHERE"),
414 TokenKind::And => Ok("AND"),
415 TokenKind::Primary => Ok("PRIMARY"),
416 TokenKind::Key => Ok("KEY"),
417 TokenKind::Null => Ok("NULL"),
418 TokenKind::Integer => Ok("INTEGER"),
419 TokenKind::Int => Ok("INT"),
420 TokenKind::Real => Ok("REAL"),
421 TokenKind::Text => Ok("TEXT"),
422 TokenKind::Blob => Ok("BLOB"),
423 TokenKind::Not => Ok("NOT"),
424 other => Err(ParseError::UnexpectedToken {
425 expected: "identifier",
426 found: other,
427 pos: token.pos,
428 }),
429 }
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use alloc::string::String;
436 use alloc::vec::Vec;
437
438 use crate::schema::SimpleTable;
439 use crate::{DiffSetBuilder, PatchsetFormat};
440
441 fn make_builder(
442 tables: &[SimpleTable],
443 ) -> DiffSetBuilder<PatchsetFormat, SimpleTable, String, Vec<u8>> {
444 let mut builder = DiffSetBuilder::default();
445 for t in tables {
446 builder.add_table(t);
447 }
448 builder
449 }
450
451 #[test]
452 fn test_digest_insert() {
453 let users = SimpleTable::new("users", &["id", "name"], &[0]);
454 let mut builder = make_builder(&[users]);
455 builder
456 .digest_sql("INSERT INTO users (id, name) VALUES (1, 'Alice')")
457 .unwrap();
458 assert_eq!(builder.len(), 1);
459 assert!(!builder.build().is_empty());
460 }
461
462 #[test]
463 fn test_digest_insert_positional() {
464 let users = SimpleTable::new("users", &["id", "name"], &[0]);
465 let mut builder = make_builder(&[users]);
466 builder
467 .digest_sql("INSERT INTO users VALUES (1, 'Alice')")
468 .unwrap();
469 assert_eq!(builder.len(), 1);
470 }
471
472 #[test]
473 fn test_digest_update() {
474 let users = SimpleTable::new("users", &["id", "name"], &[0]);
475 let mut builder = make_builder(&[users]);
476 builder
477 .digest_sql("UPDATE users SET name = 'Bob' WHERE id = 1")
478 .unwrap();
479 assert_eq!(builder.len(), 1);
480 assert!(!builder.build().is_empty());
481 }
482
483 #[test]
484 fn test_digest_delete() {
485 let users = SimpleTable::new("users", &["id", "name"], &[0]);
486 let mut builder = make_builder(&[users]);
487 builder
488 .digest_sql("DELETE FROM users WHERE id = 1")
489 .unwrap();
490 assert_eq!(builder.len(), 1);
491 assert!(!builder.build().is_empty());
492 }
493
494 #[test]
495 fn test_digest_delete_rejects_non_pk_in_where() {
496 let users = SimpleTable::new("users", &["id", "name", "status"], &[0]);
497 let mut builder = make_builder(&[users]);
498 let result = builder.digest_sql("DELETE FROM users WHERE id = 1 AND status = 'active'");
499 assert!(result.is_err());
500 }
501
502 #[test]
503 fn test_digest_multiple_dml() {
504 let users = SimpleTable::new("users", &["id", "name"], &[0]);
505 let mut builder = make_builder(&[users]);
506 builder
507 .digest_sql(
508 "INSERT INTO users (id, name) VALUES (1, 'Alice');\
509 INSERT INTO users (id, name) VALUES (2, 'Bob');\
510 DELETE FROM users WHERE id = 1;",
511 )
512 .unwrap();
513 assert_eq!(builder.len(), 1);
515 assert!(!builder.build().is_empty());
516 }
517
518 #[test]
519 fn test_digest_create_table_rejected() {
520 let mut builder: DiffSetBuilder<PatchsetFormat, SimpleTable, String, Vec<u8>> =
521 DiffSetBuilder::default();
522 let result = builder.digest_sql("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
523 assert!(result.is_err());
524 }
525
526 #[test]
527 fn test_digest_blob_value() {
528 let t = SimpleTable::new("t", &["data"], &[0]);
529 let mut builder = make_builder(&[t]);
530 builder
531 .digest_sql("INSERT INTO t (data) VALUES (X'DEADBEEF')")
532 .unwrap();
533 assert_eq!(builder.len(), 1);
534 }
535
536 #[test]
537 fn test_digest_null_value() {
538 let t = SimpleTable::new("t", &["id", "v"], &[0]);
539 let mut builder = make_builder(&[t]);
540 builder
541 .digest_sql("INSERT INTO t (id, v) VALUES (1, NULL)")
542 .unwrap();
543 assert_eq!(builder.len(), 1);
544 }
545
546 #[test]
547 fn test_digest_negative_numbers() {
548 let t = SimpleTable::new("t", &["a", "b"], &[0]);
549 let mut builder = make_builder(&[t]);
550 builder
551 .digest_sql("INSERT INTO t (a, b) VALUES (-42, -3.14)")
552 .unwrap();
553 assert_eq!(builder.len(), 1);
554 }
555
556 #[test]
557 fn test_digest_keyword_column_names() {
558 let cols = [
562 "INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE", "FROM", "WHERE", "AND",
563 "PRIMARY", "KEY", "NULL", "INTEGER", "INT", "REAL", "TEXT", "BLOB", "NOT",
564 ];
565 let t = SimpleTable::new("kwords", &cols, &[0]);
566 let mut builder = make_builder(&[t]);
567 builder
568 .digest_sql(
569 "INSERT INTO kwords (insert, into, values, update, set, delete, from, where, and, primary, key, null, integer, int, real, text, blob, not) \
570 VALUES (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)",
571 )
572 .unwrap();
573 assert_eq!(builder.len(), 1);
574 }
575
576 use crate::builders::sql::ParseError;
579
580 #[test]
581 fn test_digest_insert_missing_into() {
582 let t = SimpleTable::new("t", &["id"], &[0]);
583 let mut builder = make_builder(&[t]);
584 let err = builder.digest_sql("INSERT FROM t").unwrap_err();
585 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
586 }
587
588 #[test]
589 fn test_digest_insert_unknown_table() {
590 let t = SimpleTable::new("t", &["id"], &[0]);
591 let mut builder = make_builder(&[t]);
592 let err = builder
593 .digest_sql("INSERT INTO unknown_table VALUES (1)")
594 .unwrap_err();
595 assert!(matches!(err, ParseError::UnknownTable("unknown_table")));
596 }
597
598 #[test]
599 fn test_digest_update_missing_where() {
600 let t = SimpleTable::new("t", &["id", "v"], &[0]);
601 let mut builder = make_builder(&[t]);
602 let err = builder.digest_sql("UPDATE t SET v = 1").unwrap_err();
603 assert!(matches!(
604 err,
605 ParseError::MissingWhere {
606 statement: "UPDATE"
607 }
608 ));
609 }
610
611 #[test]
612 fn test_digest_delete_missing_where() {
613 let t = SimpleTable::new("t", &["id"], &[0]);
614 let mut builder = make_builder(&[t]);
615 let err = builder.digest_sql("DELETE FROM t").unwrap_err();
616 assert!(matches!(
617 err,
618 ParseError::MissingWhere {
619 statement: "DELETE"
620 }
621 ));
622 }
623
624 #[test]
625 fn test_digest_update_where_non_pk_column() {
626 let t = SimpleTable::new("t", &["id", "v"], &[0]);
627 let mut builder = make_builder(&[t]);
628 let err = builder
629 .digest_sql("UPDATE t SET v = 2 WHERE v = 1")
630 .unwrap_err();
631 assert!(matches!(err, ParseError::WhereNonPKColumn { column: "v" }));
632 }
633
634 #[test]
635 fn test_digest_unexpected_top_level_token() {
636 let t = SimpleTable::new("t", &["id"], &[0]);
637 let mut builder = make_builder(&[t]);
638 let err = builder.digest_sql("SELECT 1").unwrap_err();
639 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
640 }
641
642 #[test]
643 fn test_digest_expect_identifier_rejects_value_token() {
644 let t = SimpleTable::new("t", &["id"], &[0]);
645 let mut builder = make_builder(&[t]);
646 let err = builder.digest_sql("INSERT INTO 42 VALUES (1)").unwrap_err();
648 assert!(matches!(err, ParseError::UnexpectedToken { .. }));
649 }
650}