1use crate::ast::ddl::CreateContinuousAggregate;
2use crate::ast::dml::{FromItem, Select, SelectItem, SetOperation, SetOperator};
3use crate::ast::expr::{Expr, ExprKind};
4use crate::ast::{Location, Span, Statement, StatementKind};
5use crate::error::{ParserError, Result};
6use crate::nim_ffi::{self, OwnedBuffer, ParseResultKind};
7use serde::Deserialize;
8
9const MAX_SQL_INPUT_BYTES: usize = 1_048_576;
10const MAX_MESSAGEPACK_PAYLOAD_BYTES: usize = 1_048_576;
11const MAX_MESSAGEPACK_DEPTH: usize = 128;
12const MAX_MESSAGEPACK_VALUES: usize = 65_536;
13const SELECT_WRAPPER_PREFIX: &str = "SELECT ";
14const PARSER_CONTRACT_DESCRIPTOR: &str = include_str!("../nim-sql-parser/PARSER_CONTRACT_VERSION");
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17enum InputPreflightError {
18 TooLarge,
19 LengthOverflow,
20 InteriorNul,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24enum MessagePackPreflightError {
25 TooLarge,
26 TooDeep,
27 TooManyValues,
28 Truncated,
29 ReservedMarker,
30 TrailingBytes,
31}
32
33#[derive(Deserialize)]
34#[serde(deny_unknown_fields)]
35struct StagedContinuousAggregateStatement {
36 kind: StagedContinuousAggregateKind,
37 span: Span,
38}
39
40#[derive(Deserialize)]
41#[serde(tag = "variant")]
42enum StagedContinuousAggregateKind {
43 CreateContinuousAggregate(CreateContinuousAggregate),
44}
45
46pub(crate) mod continuous_aggregate_select_wire {
53 use crate::ast::{
54 Expr, FromItem, OrderByExpr, Select, SelectItem, SetOperation, Span, WithClause,
55 };
56 use serde::de::Error as _;
57 use serde::{Deserialize, Deserializer, Serialize, Serializer};
58
59 #[derive(Serialize)]
60 struct SelectWireRef<'a> {
61 variant: &'static str,
62 distinct: bool,
63 projection: &'a [SelectItem],
64 from: &'a [FromItem],
65 selection: &'a Option<Expr>,
66 group_by: &'a Option<Vec<Expr>>,
67 having: &'a Option<Expr>,
68 set_operations: &'a [SetOperation],
69 order_by: &'a [OrderByExpr],
70 limit: &'a Option<Expr>,
71 offset: &'a Option<Expr>,
72 span: Span,
73 }
74
75 #[derive(Deserialize)]
76 #[serde(deny_unknown_fields)]
77 struct SelectWire {
78 variant: String,
79 #[serde(default)]
80 with: Option<WithClause>,
81 distinct: bool,
82 projection: Vec<SelectItem>,
83 from: Vec<FromItem>,
84 selection: Option<Expr>,
85 group_by: Option<Vec<Expr>>,
86 having: Option<Expr>,
87 #[serde(default)]
88 set_operations: Vec<SetOperation>,
89 order_by: Vec<OrderByExpr>,
90 limit: Option<Expr>,
91 offset: Option<Expr>,
92 span: Span,
93 }
94
95 pub(crate) fn serialize<S>(
96 select: &Select,
97 serializer: S,
98 ) -> std::result::Result<S::Ok, S::Error>
99 where
100 S: Serializer,
101 {
102 SelectWireRef {
103 variant: "Select",
104 distinct: select.distinct,
105 projection: &select.projection,
106 from: &select.from,
107 selection: &select.selection,
108 group_by: &select.group_by,
109 having: &select.having,
110 set_operations: &select.set_operations,
111 order_by: &select.order_by,
112 limit: &select.limit,
113 offset: &select.offset,
114 span: select.span,
115 }
116 .serialize(serializer)
117 }
118
119 pub(crate) fn deserialize<'de, D>(deserializer: D) -> std::result::Result<Select, D::Error>
120 where
121 D: Deserializer<'de>,
122 {
123 let wire = SelectWire::deserialize(deserializer)?;
124 if wire.variant != "Select" {
125 return Err(D::Error::custom(format!(
126 "expected nested query variant `Select`, found `{}`",
127 wire.variant
128 )));
129 }
130 Ok(Select {
131 with: wire.with,
132 distinct: wire.distinct,
133 projection: wire.projection,
134 from: wire.from,
135 selection: wire.selection,
136 group_by: wire.group_by,
137 having: wire.having,
138 set_operations: wire.set_operations,
139 order_by: wire.order_by,
140 limit: wire.limit,
141 offset: wire.offset,
142 span: wire.span,
143 })
144 }
145}
146
147impl CreateContinuousAggregate {
148 #[doc(hidden)]
154 pub fn decode_staged_messagepack(linked_parser_contract: &str, payload: &[u8]) -> Result<Self> {
155 ensure_linked_parser_contract(linked_parser_contract)?;
156 validate_bounded_messagepack(payload).map_err(messagepack_preflight_error)?;
157 let decoded = rmp_serde::from_slice::<StagedContinuousAggregateStatement>(payload)
158 .map_err(messagepack_decode_error)?;
159 let StagedContinuousAggregateKind::CreateContinuousAggregate(statement) = decoded.kind;
160 if decoded.span != statement.span {
161 return Err(ParserError::UnexpectedToken {
162 line: 0,
163 column: 0,
164 expected: "matching outer and kind spans in MessagePack AST".to_string(),
165 found: "continuous aggregate outer span differs from kind span".to_string(),
166 });
167 }
168 Ok(statement)
169 }
170}
171
172pub fn parser_contract_version() -> String {
174 nim_ffi::parser_contract_version()
175}
176
177pub fn parse_sql(sql: &str) -> Result<Vec<Statement>> {
178 preflight_input(sql, 0).map_err(parser_error_from_preflight)?;
179 parse_sql_preflighted(sql)
180}
181
182fn parse_sql_preflighted(sql: &str) -> Result<Vec<Statement>> {
183 let tokens = scan_top_level_tokens(sql);
184 let ranges = top_level_statement_ranges(sql, &tokens);
185 let contains_set_operation = ranges.iter().any(|(start, end)| {
186 tokens.iter().any(|token| {
187 token.start >= *start
188 && token.end <= *end
189 && matches!(token.kind, TopLevelTokenKind::Word)
190 && set_operator(&sql[token.start..token.end]).is_some()
191 })
192 });
193 if contains_set_operation
194 && let Ok(statements) = parse_sql_via_ffi(sql)
195 && statements.len() == ranges.len()
196 {
197 return Ok(statements);
198 }
199 if let Some(statements) = parse_set_operation_batch(sql)? {
200 return Ok(statements);
201 }
202 parse_sql_via_ffi(sql)
203}
204
205fn parse_sql_via_ffi(sql: &str) -> Result<Vec<Statement>> {
206 ensure_linked_parser_contract(&nim_ffi::parser_contract_version())?;
207 let natural_join_markers = natural_join_markers(sql);
208 let normalized_sql = normalize_quoted_identifiers(sql);
213 let result = nim_ffi::parse_sql(&normalized_sql).map_err(parser_error_from_ffi_input)?;
214 match result.kind {
215 ParseResultKind::Ok => {
216 let buffer = OwnedBuffer::new(result.buffer_ptr, result.buffer_len);
217 if buffer.as_slice().is_empty() {
222 return Err(ParserError::UnexpectedToken {
223 line: 0,
224 column: 0,
225 expected: "MessagePack AST matching docs/ffi-ast-contract.md".to_string(),
226 found: "empty payload from Nim parser (leaked exception at FFI boundary; \
227 see issue #40)"
228 .to_string(),
229 });
230 }
231 validate_bounded_messagepack(buffer.as_slice()).map_err(messagepack_preflight_error)?;
232 let mut statements = rmp_serde::from_slice::<Vec<Statement>>(buffer.as_slice())
233 .map_err(messagepack_decode_error)?;
234 annotate_natural_joins(&mut statements, natural_join_markers)?;
235 Ok(statements)
236 }
237 ParseResultKind::Error => {
238 let buffer = OwnedBuffer::new(result.error_ptr.cast(), result.error_len);
239 Err(parser_error_from_nim(
240 String::from_utf8_lossy(buffer.as_slice()).as_ref(),
241 ))
242 }
243 }
244}
245
246#[derive(Debug, Clone, Copy)]
247enum TopLevelTokenKind {
248 Word,
249 Semicolon,
250}
251
252#[derive(Debug, Clone, Copy)]
253struct TopLevelToken {
254 kind: TopLevelTokenKind,
255 start: usize,
256 end: usize,
257}
258
259#[derive(Debug, Clone, Copy)]
260struct SetOperationSpec {
261 operator: SetOperator,
262 all: bool,
263 span: Span,
264}
265
266fn parse_set_operation_batch(sql: &str) -> Result<Option<Vec<Statement>>> {
267 let tokens = scan_top_level_tokens(sql);
268 let ranges = top_level_statement_ranges(sql, &tokens);
269
270 let contains_set_operation = ranges.iter().any(|(start, end)| {
271 tokens.iter().any(|token| {
272 token.start >= *start
273 && token.end <= *end
274 && matches!(token.kind, TopLevelTokenKind::Word)
275 && set_operator(&sql[token.start..token.end]).is_some()
276 })
277 });
278 if !contains_set_operation {
279 return Ok(None);
280 }
281
282 let mut statements = Vec::new();
283 for (start, end) in ranges {
284 let words = tokens
285 .iter()
286 .copied()
287 .filter(|token| {
288 token.start >= start
289 && token.end <= end
290 && matches!(token.kind, TopLevelTokenKind::Word)
291 })
292 .collect::<Vec<_>>();
293 if words
294 .iter()
295 .any(|word| set_operator(&sql[word.start..word.end]).is_some())
296 {
297 statements.push(parse_set_operation_statement(sql, start, end, &words)?);
298 } else {
299 statements.extend(parse_sql_via_ffi(&sql[start..end])?);
300 }
301 }
302 Ok(Some(statements))
303}
304
305fn top_level_statement_ranges(sql: &str, tokens: &[TopLevelToken]) -> Vec<(usize, usize)> {
306 let mut ranges = Vec::new();
307 let mut start = 0;
308 for token in tokens {
309 if matches!(token.kind, TopLevelTokenKind::Semicolon) {
310 if !sql[start..token.start].trim().is_empty() {
311 ranges.push((start, token.start));
312 }
313 start = token.end;
314 }
315 }
316 if !sql[start..].trim().is_empty() {
317 ranges.push((start, sql.len()));
318 }
319 ranges
320}
321
322fn parse_set_operation_statement(
323 sql: &str,
324 statement_start: usize,
325 statement_end: usize,
326 words: &[TopLevelToken],
327) -> Result<Statement> {
328 let mut branch_ranges = Vec::new();
329 let mut operations = Vec::new();
330 let mut branch_start = statement_start;
331
332 for (index, word) in words.iter().enumerate() {
333 let Some(operator) = set_operator(&sql[word.start..word.end]) else {
334 continue;
335 };
336 branch_ranges.push((branch_start, word.start));
337 let all_word = words
338 .get(index + 1)
339 .filter(|next| sql[next.start..next.end].eq_ignore_ascii_case("all"));
340 let all = all_word.is_some();
341 branch_start = all_word.map_or(word.end, |token| token.end);
342 operations.push(SetOperationSpec {
343 operator,
344 all,
345 span: span_for_offsets(sql, word.start, word.end),
346 });
347 }
348 branch_ranges.push((branch_start, statement_end));
349
350 if branch_ranges.len() != operations.len() + 1 {
351 return Err(set_operation_parser_error(
352 "a SELECT query on both sides of every set operator",
353 "malformed set-operation chain",
354 ));
355 }
356
357 let mut branches = branch_ranges
358 .into_iter()
359 .map(|(start, end)| parse_select_fragment(&sql[start..end]))
360 .collect::<Result<Vec<_>>>()?;
361 let final_branch = branches.last_mut().ok_or_else(|| {
362 set_operation_parser_error("at least one SELECT query", "empty set-operation chain")
363 })?;
364 let order_by = std::mem::take(&mut final_branch.order_by);
365 let limit = final_branch.limit.take();
366 let offset = final_branch.offset.take();
367
368 let mut terms = Vec::new();
369 let mut outer_operations = Vec::new();
370 let mut current = branches.remove(0);
371 for (operation, right) in operations.into_iter().zip(branches) {
372 if operation.operator == SetOperator::Intersect {
373 current.set_operations.push(SetOperation {
374 operator: operation.operator,
375 all: operation.all,
376 right: Box::new(right),
377 span: operation.span,
378 });
379 } else {
380 terms.push(current);
381 outer_operations.push(operation);
382 current = right;
383 }
384 }
385 terms.push(current);
386
387 let mut root = terms.remove(0);
388 for (operation, right) in outer_operations.into_iter().zip(terms) {
389 root.set_operations.push(SetOperation {
390 operator: operation.operator,
391 all: operation.all,
392 right: Box::new(right),
393 span: operation.span,
394 });
395 }
396 root.order_by = order_by;
397 root.limit = limit;
398 root.offset = offset;
399 let span = root.span;
400 Ok(Statement {
401 kind: StatementKind::Select(root),
402 span,
403 })
404}
405
406fn parse_select_fragment(sql: &str) -> Result<Select> {
407 let mut statements = parse_sql_via_ffi(sql.trim())?;
408 if statements.len() != 1 {
409 return Err(set_operation_parser_error(
410 "exactly one SELECT query per set-operation input",
411 format!("{} statements", statements.len()),
412 ));
413 }
414 match statements.remove(0).kind {
415 StatementKind::Select(select) => Ok(select),
416 _ => Err(set_operation_parser_error(
417 "SELECT query in set operation",
418 "non-SELECT statement",
419 )),
420 }
421}
422
423fn set_operator(operator: &str) -> Option<SetOperator> {
424 if operator.eq_ignore_ascii_case("union") {
425 Some(SetOperator::Union)
426 } else if operator.eq_ignore_ascii_case("intersect") {
427 Some(SetOperator::Intersect)
428 } else if operator.eq_ignore_ascii_case("except") {
429 Some(SetOperator::Except)
430 } else {
431 None
432 }
433}
434
435fn set_operation_parser_error(
436 expected: impl Into<String>,
437 found: impl Into<String>,
438) -> ParserError {
439 ParserError::UnexpectedToken {
440 line: 0,
441 column: 0,
442 expected: expected.into(),
443 found: found.into(),
444 }
445}
446
447fn span_for_offsets(sql: &str, start: usize, end: usize) -> Span {
448 fn location(sql: &str, offset: usize) -> Location {
449 let prefix = &sql[..offset.min(sql.len())];
450 let line = prefix.bytes().filter(|byte| *byte == b'\n').count() as u64 + 1;
451 let column = prefix
452 .rsplit_once('\n')
453 .map_or(prefix.len(), |(_, tail)| tail.len()) as u64
454 + 1;
455 Location::new(line, column)
456 }
457 Span::new(location(sql, start), location(sql, end.saturating_sub(1)))
458}
459
460fn scan_top_level_tokens(sql: &str) -> Vec<TopLevelToken> {
461 let bytes = sql.as_bytes();
462 let mut tokens = Vec::new();
463 let mut index = 0;
464 let mut depth = 0_u32;
465
466 while index < bytes.len() {
467 match bytes[index] {
468 b'\'' | b'"' => {
469 let quote = bytes[index];
470 index += 1;
471 while index < bytes.len() {
472 if bytes[index] == quote {
473 if bytes.get(index + 1) == Some("e) {
474 index += 2;
475 } else {
476 index += 1;
477 break;
478 }
479 } else {
480 index += 1;
481 }
482 }
483 }
484 b'-' if bytes.get(index + 1) == Some(&b'-') => {
485 index += 2;
486 while index < bytes.len() && bytes[index] != b'\n' {
487 index += 1;
488 }
489 }
490 b'/' if bytes.get(index + 1) == Some(&b'*') => {
491 index += 2;
492 while index + 1 < bytes.len() && !(bytes[index] == b'*' && bytes[index + 1] == b'/')
493 {
494 index += 1;
495 }
496 index = (index + 2).min(bytes.len());
497 }
498 b'(' => {
499 depth += 1;
500 index += 1;
501 }
502 b')' => {
503 depth = depth.saturating_sub(1);
504 index += 1;
505 }
506 b';' if depth == 0 => {
507 tokens.push(TopLevelToken {
508 kind: TopLevelTokenKind::Semicolon,
509 start: index,
510 end: index + 1,
511 });
512 index += 1;
513 }
514 byte if depth == 0 && (byte.is_ascii_alphabetic() || byte == b'_') => {
515 let start = index;
516 index += 1;
517 while index < bytes.len()
518 && (bytes[index].is_ascii_alphanumeric() || bytes[index] == b'_')
519 {
520 index += 1;
521 }
522 tokens.push(TopLevelToken {
523 kind: TopLevelTokenKind::Word,
524 start,
525 end: index,
526 });
527 }
528 _ => index += 1,
529 }
530 }
531 tokens
532}
533
534fn expected_parser_contract() -> &'static str {
535 PARSER_CONTRACT_DESCRIPTOR.trim()
536}
537
538fn ensure_linked_parser_contract(linked_parser_contract: &str) -> Result<()> {
539 let expected = expected_parser_contract();
540 if linked_parser_contract == expected {
541 return Ok(());
542 }
543 Err(ParserError::UnexpectedToken {
544 line: 0,
545 column: 0,
546 expected: format!("linked Nim parser contract {expected}"),
547 found: format!("linked Nim parser contract {linked_parser_contract}"),
548 })
549}
550
551fn messagepack_decode_error(error: rmp_serde::decode::Error) -> ParserError {
552 ParserError::UnexpectedToken {
553 line: 0,
554 column: 0,
555 expected: "bounded MessagePack AST matching docs/ffi-ast-contract.md".to_string(),
556 found: error.to_string(),
557 }
558}
559
560fn messagepack_preflight_error(error: MessagePackPreflightError) -> ParserError {
561 let found = match error {
562 MessagePackPreflightError::TooLarge => {
563 format!("MessagePack payload exceeds {MAX_MESSAGEPACK_PAYLOAD_BYTES} bytes")
564 }
565 MessagePackPreflightError::TooDeep => {
566 format!("MessagePack nesting exceeds {MAX_MESSAGEPACK_DEPTH} levels")
567 }
568 MessagePackPreflightError::TooManyValues => {
569 format!("MessagePack collection limit of {MAX_MESSAGEPACK_VALUES} values exceeded")
570 }
571 MessagePackPreflightError::Truncated => "truncated MessagePack payload".to_string(),
572 MessagePackPreflightError::ReservedMarker => "reserved MessagePack marker 0xc1".to_string(),
573 MessagePackPreflightError::TrailingBytes => {
574 "trailing bytes after MessagePack payload".to_string()
575 }
576 };
577 ParserError::UnexpectedToken {
578 line: 0,
579 column: 0,
580 expected: "bounded MessagePack AST matching docs/ffi-ast-contract.md".to_string(),
581 found,
582 }
583}
584
585fn validate_bounded_messagepack(
586 payload: &[u8],
587) -> std::result::Result<(), MessagePackPreflightError> {
588 if payload.len() > MAX_MESSAGEPACK_PAYLOAD_BYTES {
589 return Err(MessagePackPreflightError::TooLarge);
590 }
591 let mut scanner = MessagePackScanner {
592 payload,
593 position: 0,
594 values: 0,
595 };
596 scanner.scan_value(1)?;
597 if scanner.position != payload.len() {
598 return Err(MessagePackPreflightError::TrailingBytes);
599 }
600 Ok(())
601}
602
603struct MessagePackScanner<'a> {
604 payload: &'a [u8],
605 position: usize,
606 values: usize,
607}
608
609impl MessagePackScanner<'_> {
610 fn scan_value(&mut self, depth: usize) -> std::result::Result<(), MessagePackPreflightError> {
611 if depth > MAX_MESSAGEPACK_DEPTH {
612 return Err(MessagePackPreflightError::TooDeep);
613 }
614 self.values = self
615 .values
616 .checked_add(1)
617 .ok_or(MessagePackPreflightError::TooManyValues)?;
618 if self.values > MAX_MESSAGEPACK_VALUES {
619 return Err(MessagePackPreflightError::TooManyValues);
620 }
621
622 let marker = self.read_byte()?;
623 match marker {
624 0x00..=0x7f | 0xc0 | 0xc2 | 0xc3 | 0xe0..=0xff => Ok(()),
625 0x80..=0x8f => self.scan_map(usize::from(marker & 0x0f), depth),
626 0x90..=0x9f => self.scan_children(usize::from(marker & 0x0f), depth),
627 0xa0..=0xbf => self.skip(usize::from(marker & 0x1f)),
628 0xc1 => Err(MessagePackPreflightError::ReservedMarker),
629 0xc4 | 0xd9 => {
630 let length = usize::from(self.read_byte()?);
631 self.skip(length)
632 }
633 0xc5 | 0xda => {
634 let length = usize::from(self.read_u16()?);
635 self.skip(length)
636 }
637 0xc6 | 0xdb => {
638 let length = usize::try_from(self.read_u32()?)
639 .map_err(|_| MessagePackPreflightError::TooLarge)?;
640 self.skip(length)
641 }
642 0xc7 => {
643 let length = usize::from(self.read_byte()?);
644 self.skip_ext(length)
645 }
646 0xc8 => {
647 let length = usize::from(self.read_u16()?);
648 self.skip_ext(length)
649 }
650 0xc9 => {
651 let length = usize::try_from(self.read_u32()?)
652 .map_err(|_| MessagePackPreflightError::TooLarge)?;
653 self.skip_ext(length)
654 }
655 0xca => self.skip(4),
656 0xcb => self.skip(8),
657 0xcc | 0xd0 => self.skip(1),
658 0xcd | 0xd1 => self.skip(2),
659 0xce | 0xd2 => self.skip(4),
660 0xcf | 0xd3 => self.skip(8),
661 0xd4 => self.skip_ext(1),
662 0xd5 => self.skip_ext(2),
663 0xd6 => self.skip_ext(4),
664 0xd7 => self.skip_ext(8),
665 0xd8 => self.skip_ext(16),
666 0xdc => {
667 let count = usize::from(self.read_u16()?);
668 self.scan_children(count, depth)
669 }
670 0xdd => {
671 let count = usize::try_from(self.read_u32()?)
672 .map_err(|_| MessagePackPreflightError::TooManyValues)?;
673 self.scan_children(count, depth)
674 }
675 0xde => {
676 let count = usize::from(self.read_u16()?);
677 self.scan_map(count, depth)
678 }
679 0xdf => {
680 let count = usize::try_from(self.read_u32()?)
681 .map_err(|_| MessagePackPreflightError::TooManyValues)?;
682 self.scan_map(count, depth)
683 }
684 }
685 }
686
687 fn scan_map(
688 &mut self,
689 entries: usize,
690 depth: usize,
691 ) -> std::result::Result<(), MessagePackPreflightError> {
692 let children = entries
693 .checked_mul(2)
694 .ok_or(MessagePackPreflightError::TooManyValues)?;
695 self.scan_children(children, depth)
696 }
697
698 fn scan_children(
699 &mut self,
700 children: usize,
701 depth: usize,
702 ) -> std::result::Result<(), MessagePackPreflightError> {
703 if children > MAX_MESSAGEPACK_VALUES {
704 return Err(MessagePackPreflightError::TooManyValues);
705 }
706 for _ in 0..children {
707 self.scan_value(depth + 1)?;
708 }
709 Ok(())
710 }
711
712 fn skip_ext(
713 &mut self,
714 payload_length: usize,
715 ) -> std::result::Result<(), MessagePackPreflightError> {
716 let total = payload_length
717 .checked_add(1)
718 .ok_or(MessagePackPreflightError::TooLarge)?;
719 self.skip(total)
720 }
721
722 fn read_byte(&mut self) -> std::result::Result<u8, MessagePackPreflightError> {
723 let byte = *self
724 .payload
725 .get(self.position)
726 .ok_or(MessagePackPreflightError::Truncated)?;
727 self.position += 1;
728 Ok(byte)
729 }
730
731 fn read_u16(&mut self) -> std::result::Result<u16, MessagePackPreflightError> {
732 let bytes = self.take(2)?;
733 Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
734 }
735
736 fn read_u32(&mut self) -> std::result::Result<u32, MessagePackPreflightError> {
737 let bytes = self.take(4)?;
738 Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
739 }
740
741 fn skip(&mut self, length: usize) -> std::result::Result<(), MessagePackPreflightError> {
742 self.take(length).map(|_| ())
743 }
744
745 fn take(&mut self, length: usize) -> std::result::Result<&[u8], MessagePackPreflightError> {
746 let end = self
747 .position
748 .checked_add(length)
749 .ok_or(MessagePackPreflightError::Truncated)?;
750 let bytes = self
751 .payload
752 .get(self.position..end)
753 .ok_or(MessagePackPreflightError::Truncated)?;
754 self.position = end;
755 Ok(bytes)
756 }
757}
758
759fn normalize_quoted_identifiers(sql: &str) -> String {
760 let mut normalized = String::with_capacity(sql.len());
761 let mut chars = sql.chars().peekable();
762 while let Some(ch) = chars.next() {
763 match ch {
764 '\'' => {
765 normalized.push(ch);
766 while let Some(string_ch) = chars.next() {
767 normalized.push(string_ch);
768 if string_ch == '\'' {
769 if chars.peek() == Some(&'\'') {
770 normalized.push(chars.next().expect("peeked quote"));
771 } else {
772 break;
773 }
774 }
775 }
776 }
777 '"' => {
778 normalized.push(' ');
782 while let Some(identifier_ch) = chars.next() {
783 if identifier_ch == '"' {
784 if chars.peek() == Some(&'"') {
785 normalized.push(chars.next().expect("peeked quote"));
788 normalized.push(' ');
789 } else {
790 normalized.push(' ');
791 break;
792 }
793 } else {
794 normalized.push(identifier_ch);
795 }
796 }
797 }
798 '-' if chars.peek() == Some(&'-') => {
799 normalized.push(ch);
800 normalized.push(chars.next().expect("peeked comment dash"));
801 for comment_ch in chars.by_ref() {
802 normalized.push(comment_ch);
803 if comment_ch == '\n' {
804 break;
805 }
806 }
807 }
808 '/' if chars.peek() == Some(&'*') => {
809 normalized.push(ch);
810 normalized.push(chars.next().expect("peeked comment star"));
811 let mut previous = '\0';
812 for comment_ch in chars.by_ref() {
813 normalized.push(comment_ch);
814 if previous == '*' && comment_ch == '/' {
815 break;
816 }
817 previous = comment_ch;
818 }
819 }
820 ch if ch.is_ascii_alphabetic() || ch == '_' => {
821 let mut identifier = String::from(ch);
822 while chars
823 .peek()
824 .is_some_and(|next| next.is_ascii_alphanumeric() || *next == '_')
825 {
826 identifier.push(chars.next().expect("peeked identifier character"));
827 }
828 normalized.push_str(&identifier.to_ascii_lowercase());
832 }
833 _ => normalized.push(ch),
834 }
835 }
836 normalized
837}
838
839fn natural_join_markers(sql: &str) -> Vec<bool> {
840 let mut markers = Vec::new();
841 let mut saw_natural = false;
842 let mut chars = sql.chars().peekable();
843 while let Some(ch) = chars.next() {
844 match ch {
845 '\'' | '"' => skip_quoted(&mut chars, ch),
846 '-' if chars.peek() == Some(&'-') => {
847 chars.next();
848 for comment_ch in chars.by_ref() {
849 if comment_ch == '\n' {
850 break;
851 }
852 }
853 }
854 '/' if chars.peek() == Some(&'*') => {
855 chars.next();
856 let mut previous = '\0';
857 for comment_ch in chars.by_ref() {
858 if previous == '*' && comment_ch == '/' {
859 break;
860 }
861 previous = comment_ch;
862 }
863 }
864 ';' => saw_natural = false,
865 c if c.is_ascii_alphabetic() || c == '_' => {
866 let mut word = String::from(c);
867 while chars
868 .peek()
869 .is_some_and(|next| next.is_ascii_alphanumeric() || *next == '_')
870 {
871 word.push(chars.next().expect("peeked identifier character"));
872 }
873 match word.to_ascii_lowercase().as_str() {
874 "natural" => saw_natural = true,
875 "join" => {
876 markers.push(saw_natural);
877 saw_natural = false;
878 }
879 _ => {}
880 }
881 }
882 _ => {}
883 }
884 }
885 markers
886}
887
888fn skip_quoted(chars: &mut std::iter::Peekable<std::str::Chars<'_>>, quote: char) {
889 while let Some(ch) = chars.next() {
890 if ch == quote {
891 if chars.peek() == Some("e) {
892 chars.next();
893 } else {
894 break;
895 }
896 }
897 }
898}
899
900fn annotate_natural_joins(statements: &mut [Statement], natural_markers: Vec<bool>) -> Result<()> {
907 let supplied = natural_markers.len();
908 let mut natural_markers = natural_markers.into_iter();
909 let mut consumed = 0usize;
910 for statement in statements {
911 if let StatementKind::Select(select) = &mut statement.kind {
912 annotate_select_natural_joins(select, &mut natural_markers, &mut consumed);
913 }
914 }
915
916 if consumed != supplied {
917 return Err(ParserError::UnexpectedToken {
918 line: 0,
919 column: 0,
920 expected: format!("{supplied} NATURAL join markers, one per join"),
921 found: format!("{consumed} joins in the AST"),
922 });
923 }
924 Ok(())
925}
926
927fn annotate_select_natural_joins(
928 select: &mut Select,
929 natural_markers: &mut impl Iterator<Item = bool>,
930 consumed: &mut usize,
931) {
932 for item in &mut select.projection {
933 if let SelectItem::Expr { expr, .. } = item {
934 annotate_expr_natural_joins(expr, natural_markers, consumed);
935 }
936 }
937 for from in &mut select.from {
938 annotate_from_natural_joins(from, natural_markers, consumed);
939 }
940 if let Some(selection) = &mut select.selection {
941 annotate_expr_natural_joins(selection, natural_markers, consumed);
942 }
943 if let Some(group_by) = &mut select.group_by {
944 for expression in group_by {
945 annotate_expr_natural_joins(expression, natural_markers, consumed);
946 }
947 }
948 if let Some(having) = &mut select.having {
949 annotate_expr_natural_joins(having, natural_markers, consumed);
950 }
951 for order_by in &mut select.order_by {
952 annotate_expr_natural_joins(&mut order_by.expr, natural_markers, consumed);
953 }
954 if let Some(limit) = &mut select.limit {
955 annotate_expr_natural_joins(limit, natural_markers, consumed);
956 }
957 if let Some(offset) = &mut select.offset {
958 annotate_expr_natural_joins(offset, natural_markers, consumed);
959 }
960}
961
962fn annotate_from_natural_joins(
963 from: &mut FromItem,
964 natural_markers: &mut impl Iterator<Item = bool>,
965 consumed: &mut usize,
966) {
967 match from {
968 FromItem::Join {
969 left,
970 right,
971 natural,
972 ..
973 } => {
974 annotate_from_natural_joins(left, natural_markers, consumed);
975 if let Some(marker) = natural_markers.next() {
976 *natural |= marker;
977 *consumed += 1;
978 }
979 annotate_from_natural_joins(right, natural_markers, consumed);
980 }
981 FromItem::Derived { subquery, .. } => {
982 if let StatementKind::Select(select) = &mut subquery.kind {
983 annotate_select_natural_joins(select, natural_markers, consumed);
984 }
985 }
986 FromItem::Table { .. } => {}
987 }
988}
989
990fn annotate_expr_natural_joins(
991 expr: &mut Expr,
992 natural_markers: &mut impl Iterator<Item = bool>,
993 consumed: &mut usize,
994) {
995 match &mut expr.kind {
996 ExprKind::ScalarSubquery { subquery } | ExprKind::Exists { subquery, .. } => {
997 if let StatementKind::Select(select) = &mut subquery.kind {
998 annotate_select_natural_joins(select, natural_markers, consumed);
999 }
1000 }
1001 ExprKind::InSubquery { expr, subquery, .. }
1002 | ExprKind::Quantified { expr, subquery, .. } => {
1003 annotate_expr_natural_joins(expr, natural_markers, consumed);
1004 if let StatementKind::Select(select) = &mut subquery.kind {
1005 annotate_select_natural_joins(select, natural_markers, consumed);
1006 }
1007 }
1008 ExprKind::BinaryOp { left, right, .. } => {
1009 annotate_expr_natural_joins(left, natural_markers, consumed);
1010 annotate_expr_natural_joins(right, natural_markers, consumed);
1011 }
1012 ExprKind::UnaryOp { operand, .. } | ExprKind::IsNull { expr: operand, .. } => {
1013 annotate_expr_natural_joins(operand, natural_markers, consumed);
1014 }
1015 ExprKind::Case {
1016 operand,
1017 branches,
1018 else_expr,
1019 } => {
1020 if let Some(operand) = operand {
1021 annotate_expr_natural_joins(operand, natural_markers, consumed);
1022 }
1023 for branch in branches {
1024 annotate_expr_natural_joins(&mut branch.when, natural_markers, consumed);
1025 annotate_expr_natural_joins(&mut branch.then, natural_markers, consumed);
1026 }
1027 if let Some(else_expr) = else_expr {
1028 annotate_expr_natural_joins(else_expr, natural_markers, consumed);
1029 }
1030 }
1031 ExprKind::FunctionCall { args, .. } => {
1032 for argument in args {
1033 annotate_expr_natural_joins(argument, natural_markers, consumed);
1034 }
1035 }
1036 ExprKind::Between {
1037 expr, low, high, ..
1038 } => {
1039 annotate_expr_natural_joins(expr, natural_markers, consumed);
1040 annotate_expr_natural_joins(low, natural_markers, consumed);
1041 annotate_expr_natural_joins(high, natural_markers, consumed);
1042 }
1043 ExprKind::Like {
1044 expr,
1045 pattern,
1046 escape,
1047 ..
1048 } => {
1049 annotate_expr_natural_joins(expr, natural_markers, consumed);
1050 annotate_expr_natural_joins(pattern, natural_markers, consumed);
1051 if let Some(escape) = escape {
1052 annotate_expr_natural_joins(escape, natural_markers, consumed);
1053 }
1054 }
1055 ExprKind::InList { expr, list, .. } => {
1056 annotate_expr_natural_joins(expr, natural_markers, consumed);
1057 for item in list {
1058 annotate_expr_natural_joins(item, natural_markers, consumed);
1059 }
1060 }
1061 ExprKind::Cast { expr, .. } => {
1062 annotate_expr_natural_joins(expr, natural_markers, consumed);
1063 }
1064 ExprKind::Literal { .. } | ExprKind::ColumnRef { .. } | ExprKind::VectorLiteral { .. } => {}
1065 }
1066}
1067
1068pub fn parse_expression_sql(sql: &str) -> Result<crate::ast::Expr> {
1069 let wrapped_len =
1070 preflight_input(sql, SELECT_WRAPPER_PREFIX.len()).map_err(parser_error_from_preflight)?;
1071 let mut wrapped = String::with_capacity(wrapped_len);
1072 wrapped.push_str(SELECT_WRAPPER_PREFIX);
1073 wrapped.push_str(sql);
1074 let statements = parse_sql_preflighted(&wrapped)?;
1075 let Some(statement) = statements.into_iter().next() else {
1076 return Err(empty_expression_error());
1077 };
1078 let StatementKind::Select(select) = statement.kind else {
1079 return Err(empty_expression_error());
1080 };
1081 let Some(crate::ast::SelectItem::Expr { expr, .. }) = select.projection.into_iter().next()
1082 else {
1083 return Err(empty_expression_error());
1084 };
1085 Ok(expr)
1086}
1087
1088fn empty_expression_error() -> ParserError {
1089 ParserError::UnexpectedToken {
1090 line: 0,
1091 column: 0,
1092 expected: "expression".to_string(),
1093 found: "empty parser result".to_string(),
1094 }
1095}
1096
1097fn checked_total_input_len(
1098 input_len: usize,
1099 wrapper_len: usize,
1100) -> std::result::Result<usize, InputPreflightError> {
1101 let total_len = input_len
1102 .checked_add(wrapper_len)
1103 .ok_or(InputPreflightError::LengthOverflow)?;
1104 if total_len > MAX_SQL_INPUT_BYTES {
1105 return Err(InputPreflightError::TooLarge);
1106 }
1107 Ok(total_len)
1108}
1109
1110fn preflight_input(
1111 sql: &str,
1112 wrapper_len: usize,
1113) -> std::result::Result<usize, InputPreflightError> {
1114 let total_len = checked_total_input_len(sql.len(), wrapper_len)?;
1115 if sql.as_bytes().contains(&0) {
1116 return Err(InputPreflightError::InteriorNul);
1117 }
1118 Ok(total_len)
1119}
1120
1121fn parser_error_from_preflight(error: InputPreflightError) -> ParserError {
1122 match error {
1123 InputPreflightError::TooLarge | InputPreflightError::LengthOverflow => {
1124 input_too_large_error()
1125 }
1126 InputPreflightError::InteriorNul => interior_nul_error(),
1127 }
1128}
1129
1130fn parser_error_from_ffi_input(error: nim_ffi::ParseInputError) -> ParserError {
1131 match error {
1132 nim_ffi::ParseInputError::LengthOutOfRange => input_too_large_error(),
1133 nim_ffi::ParseInputError::InteriorNul => interior_nul_error(),
1134 }
1135}
1136
1137fn input_too_large_error() -> ParserError {
1138 ParserError::UnexpectedToken {
1139 line: 0,
1140 column: 0,
1141 expected: "SQL input at most 1048576 UTF-8 bytes".to_string(),
1142 found: "SQL input exceeds byte limit".to_string(),
1143 }
1144}
1145
1146fn interior_nul_error() -> ParserError {
1147 ParserError::UnexpectedToken {
1148 line: 0,
1149 column: 0,
1150 expected: "valid SQL without interior NUL bytes".to_string(),
1151 found: "interior NUL byte".to_string(),
1152 }
1153}
1154
1155const INTERNAL_DEFECT_PREFIX: &str =
1161 "internal parser defect (this is a parser bug, not invalid SQL): ";
1162
1163fn parser_error_from_nim(message: &str) -> ParserError {
1164 if let Some(defect_message) = message.strip_prefix(INTERNAL_DEFECT_PREFIX) {
1165 return ParserError::InternalParserDefect {
1166 message: defect_message.to_string(),
1167 };
1168 }
1169 let (line, column) = parse_nim_line_col(message).unwrap_or((0, 0));
1170 ParserError::UnexpectedToken {
1171 line,
1172 column,
1173 expected: "valid SQL".to_string(),
1174 found: message.to_string(),
1175 }
1176}
1177
1178fn parse_nim_line_col(message: &str) -> Option<(u64, u64)> {
1179 let after_line = message.strip_prefix("Parse error at line ")?;
1180 let (line, rest) = after_line.split_once(", col ")?;
1181 let (col, _) = rest.split_once(':')?;
1182 Some((line.parse().ok()?, col.parse().ok()?))
1183}
1184
1185#[cfg(test)]
1186mod input_preflight_tests {
1187 use super::*;
1188
1189 #[test]
1190 fn raw_sql_guard_accepts_boundary_minus_and_exact_but_rejects_plus() {
1191 assert_eq!(
1192 checked_total_input_len(MAX_SQL_INPUT_BYTES - 1, 0),
1193 Ok(MAX_SQL_INPUT_BYTES - 1)
1194 );
1195 assert_eq!(
1196 checked_total_input_len(MAX_SQL_INPUT_BYTES, 0),
1197 Ok(MAX_SQL_INPUT_BYTES)
1198 );
1199 assert_eq!(
1200 checked_total_input_len(MAX_SQL_INPUT_BYTES + 1, 0),
1201 Err(InputPreflightError::TooLarge)
1202 );
1203 }
1204
1205 #[test]
1206 fn guard_counts_utf8_bytes_instead_of_characters() {
1207 let exact = "é".repeat(MAX_SQL_INPUT_BYTES / "é".len());
1208 assert_eq!(exact.chars().count(), MAX_SQL_INPUT_BYTES / 2);
1209 assert_eq!(exact.len(), MAX_SQL_INPUT_BYTES);
1210 assert_eq!(preflight_input(&exact, 0), Ok(MAX_SQL_INPUT_BYTES));
1211
1212 let plus = format!("{exact}é");
1213 assert_eq!(
1214 preflight_input(&plus, 0),
1215 Err(InputPreflightError::TooLarge)
1216 );
1217 }
1218
1219 #[test]
1220 fn expression_guard_includes_wrapper_and_detects_length_overflow() {
1221 assert_eq!(
1222 checked_total_input_len(
1223 MAX_SQL_INPUT_BYTES - SELECT_WRAPPER_PREFIX.len(),
1224 SELECT_WRAPPER_PREFIX.len(),
1225 ),
1226 Ok(MAX_SQL_INPUT_BYTES)
1227 );
1228 assert_eq!(
1229 checked_total_input_len(
1230 MAX_SQL_INPUT_BYTES - SELECT_WRAPPER_PREFIX.len() + 1,
1231 SELECT_WRAPPER_PREFIX.len(),
1232 ),
1233 Err(InputPreflightError::TooLarge)
1234 );
1235 assert_eq!(
1236 checked_total_input_len(usize::MAX, SELECT_WRAPPER_PREFIX.len()),
1237 Err(InputPreflightError::LengthOverflow)
1238 );
1239 }
1240
1241 #[test]
1242 fn preflight_rejects_nul_before_any_ffi_work() {
1243 assert_eq!(
1244 preflight_input("SELECT \0 1", 0),
1245 Err(InputPreflightError::InteriorNul)
1246 );
1247 assert_eq!(
1248 preflight_input("1 \0 2", SELECT_WRAPPER_PREFIX.len()),
1249 Err(InputPreflightError::InteriorNul)
1250 );
1251 }
1252}