Skip to main content

rupg_sql/
lexer.rs

1//! The lexer, a port of `scan.l`.
2//!
3//! The rules of `scan.l` are regular expressions, and flex takes the longest match, or the first rule when two matches have the same length. Here each kind of token is a function that starts at the first byte, and the comments say which rule or which tie it follows. The exclusive states of `scan.l` (`xb`, `xc`, `xd`, `xh`, `xq`, `xqs`, `xe`, `xdolq`, `xui`, `xus`, `xeu`) are loops in these functions.
4//!
5//! `standard_conforming_strings` is always on, as it is in every PostgreSQL release since 9.1, so a backslash in `'...'` is a plain character and only `E'...'` has escapes. The server encoding is always UTF-8.
6//!
7//! Lifted from `crates/rudb-pgparse/src/lexer.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).
8
9use std::borrow::Cow;
10use std::fmt;
11
12use rupg_common::SqlState;
13
14use crate::error::{Error, Notice, Severity};
15use crate::generated::keywords::{self, Keyword};
16use crate::generated::tables::token;
17use crate::parser::character;
18
19/// The longest name in bytes, `NAMEDATALEN - 1` in PostgreSQL.
20pub(crate) const NAME_LENGTH: usize = 63;
21
22/// The token that the grammar gives to a character that is not in it, `$undefined`.
23const UNDEFINED: u16 = 2;
24
25/// The value of a token.
26#[derive(Clone, Debug)]
27pub enum Value<'a> {
28    /// No value, for a character token, an operator token such as `::`, or the end.
29    None,
30    /// The value of `ICONST` and `PARAM`.
31    Integer(i32),
32    /// The text of `IDENT`, `UIDENT`, `SCONST`, `USCONST`, `FCONST`, `BCONST`, `XCONST` and `Op`. An identifier is in lower case and not longer than 63 bytes. A bit string starts with `b` and a hexadecimal string starts with `x`, as in PostgreSQL.
33    Text(Cow<'a, str>),
34    /// The keyword of a keyword token.
35    Keyword(&'static Keyword),
36}
37
38impl PartialEq for Value<'_> {
39    fn eq(&self, other: &Self) -> bool {
40        match (self, other) {
41            (Value::None, Value::None) => true,
42            (Value::Integer(a), Value::Integer(b)) => a == b,
43            (Value::Text(a), Value::Text(b)) => a == b,
44            (Value::Keyword(a), Value::Keyword(b)) => std::ptr::eq(*a, *b),
45            _ => false,
46        }
47    }
48}
49
50/// One token.
51#[derive(Clone, Debug, PartialEq)]
52pub struct Token<'a> {
53    /// The token number of the grammar. See [`crate::token`] and [`crate::character`]. The end of the text is 0.
54    pub kind: u16,
55    /// The byte offset where the token starts.
56    pub start: usize,
57    /// The byte offset after the token.
58    pub end: usize,
59    /// The value.
60    pub value: Value<'a>,
61}
62
63impl Token<'_> {
64    /// The text of the value, or `None` when the value is not text.
65    pub fn text(&self) -> Option<&str> {
66        match &self.value {
67            Value::Text(text) => Some(text),
68            _ => None,
69        }
70    }
71}
72
73/// The kind of a quoted string.
74#[derive(Clone, Copy, PartialEq, Eq)]
75enum Quote {
76    /// `B'...'`, the state `xb`.
77    Bit,
78    /// `X'...'`, the state `xh`.
79    Hex,
80    /// `'...'`, the state `xq`.
81    Plain,
82    /// `E'...'`, the state `xe`.
83    Escape,
84    /// `U&'...'`, the state `xus`.
85    Unicode,
86}
87
88/// The lexer. It gives one token for each call of [`Lexer::next_token`].
89pub struct Lexer<'a> {
90    text: &'a str,
91    bytes: &'a [u8],
92    at: usize,
93    notices: Vec<Notice>,
94}
95
96impl fmt::Debug for Lexer<'_> {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        f.debug_struct("Lexer").field("at", &self.at).finish_non_exhaustive()
99    }
100}
101
102/// The text of a literal. It points into the input until an escape, a doubled quote or a continuation makes it different from the input. Then it is a copy.
103struct Literal {
104    start: usize,
105    end: usize,
106    copy: Option<Vec<u8>>,
107}
108
109impl Literal {
110    fn new() -> Literal {
111        Literal { start: 0, end: 0, copy: None }
112    }
113
114    fn copy(&mut self, bytes: &[u8]) -> &mut Vec<u8> {
115        self.copy.get_or_insert_with(|| bytes[self.start..self.end].to_vec())
116    }
117
118    /// Adds `bytes[from..to]`.
119    fn push(&mut self, bytes: &[u8], from: usize, to: usize) {
120        if self.copy.is_none() {
121            if self.start == self.end {
122                (self.start, self.end) = (from, to);
123                return;
124            }
125            if self.end == from {
126                self.end = to;
127                return;
128            }
129        }
130        self.copy(bytes).extend_from_slice(&bytes[from..to]);
131    }
132
133    fn push_byte(&mut self, bytes: &[u8], b: u8) {
134        self.copy(bytes).push(b);
135    }
136
137    fn push_char(&mut self, bytes: &[u8], c: char) {
138        let mut buffer = [0; 4];
139        self.copy(bytes).extend_from_slice(c.encode_utf8(&mut buffer).as_bytes());
140    }
141
142    fn is_empty(&self) -> bool {
143        match &self.copy {
144            Some(copy) => copy.is_empty(),
145            None => self.start == self.end,
146        }
147    }
148
149    /// The text. `verify` is the `saw_non_ascii` of `scan.l`: an escape made a zero byte or a byte over 127, so the bytes must be checked as `pg_verifymbstr` does.
150    fn finish<'a>(self, text: &'a str, verify: bool) -> Result<Cow<'a, str>, Error> {
151        let Some(copy) = self.copy else {
152            return Ok(Cow::Borrowed(&text[self.start..self.end]));
153        };
154        if verify && let Some(zero) = copy.iter().position(|&b| b == 0) {
155            let valid = std::str::from_utf8(&copy).map_or_else(|e| e.valid_up_to(), str::len);
156            return Err(Error::encoding(&copy[zero.min(valid)..]));
157        }
158        String::from_utf8(copy).map(Cow::Owned).map_err(|e| {
159            let at = e.utf8_error().valid_up_to();
160            Error::encoding(&e.as_bytes()[at..])
161        })
162    }
163}
164
165fn is_space(b: u8) -> bool {
166    matches!(b, b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c)
167}
168
169/// `ident_start`: a letter, `_` or any byte over 127. `dolq_start` is the same.
170fn is_ident_start(b: u8) -> bool {
171    b.is_ascii_alphabetic() || b == b'_' || b >= 0x80
172}
173
174/// `dolq_cont`: `ident_start` or a digit.
175fn is_dolq_cont(b: u8) -> bool {
176    is_ident_start(b) || b.is_ascii_digit()
177}
178
179/// `ident_cont`: `dolq_cont` or `$`.
180fn is_ident_cont(b: u8) -> bool {
181    is_dolq_cont(b) || b == b'$'
182}
183
184/// `op_chars`.
185fn is_op_char(b: u8) -> bool {
186    matches!(
187        b,
188        b'~' | b'!'
189            | b'@'
190            | b'#'
191            | b'^'
192            | b'&'
193            | b'|'
194            | b'`'
195            | b'?'
196            | b'+'
197            | b'-'
198            | b'*'
199            | b'/'
200            | b'%'
201            | b'<'
202            | b'>'
203            | b'='
204    )
205}
206
207/// `self`: the characters that are a token by themselves.
208fn is_self(b: u8) -> bool {
209    matches!(
210        b,
211        b',' | b'('
212            | b')'
213            | b'['
214            | b']'
215            | b'.'
216            | b';'
217            | b':'
218            | b'+'
219            | b'-'
220            | b'*'
221            | b'/'
222            | b'%'
223            | b'^'
224            | b'<'
225            | b'>'
226            | b'='
227    )
228}
229
230fn is_first_surrogate(c: u32) -> bool {
231    (0xd800..=0xdbff).contains(&c)
232}
233
234fn is_second_surrogate(c: u32) -> bool {
235    (0xdc00..=0xdfff).contains(&c)
236}
237
238fn surrogate_pair(first: u32, second: u32) -> u32 {
239    ((first & 0x3ff) << 10) + (second & 0x3ff) + 0x10000
240}
241
242/// The value of hexadecimal digits that the caller has checked.
243fn hex_value(digits: &[u8]) -> u32 {
244    digits.iter().fold(0, |value, &d| (value << 4) | char::from(d).to_digit(16).unwrap_or(0))
245}
246
247/// The value of an integer literal as `pg_strtoint32_safe` reads it, with an optional `0x`, `0o` or `0b` prefix and `_` between digits, or `None` when it is too large for 32 bits.
248fn integer(text: &[u8]) -> Option<i32> {
249    let (radix, digits) = match text {
250        [b'0', b'x' | b'X', rest @ ..] => (16, rest),
251        [b'0', b'o' | b'O', rest @ ..] => (8, rest),
252        [b'0', b'b' | b'B', rest @ ..] => (2, rest),
253        _ => (10, text),
254    };
255    let mut value: i32 = 0;
256    for &d in digits {
257        if d == b'_' {
258            continue;
259        }
260        let digit = char::from(d).to_digit(radix)?;
261        value = value.checked_mul(radix as i32)?.checked_add(digit as i32)?;
262    }
263    Some(value)
264}
265
266/// An identifier in lower case, cut to [`NAME_LENGTH`] bytes, as `downcase_truncate_identifier` makes it. Only ASCII letters change case, because the encoding has more than one byte for a character.
267fn downcase(word: &str) -> Cow<'_, str> {
268    if word.bytes().any(|b| b.is_ascii_uppercase()) {
269        Cow::Owned(word.to_ascii_lowercase())
270    } else {
271        Cow::Borrowed(word)
272    }
273}
274
275/// Cuts a name to [`NAME_LENGTH`] bytes at a character boundary, as `truncate_identifier` does, and gives the notice of PostgreSQL.
276pub(crate) fn truncate<'a>(name: Cow<'a, str>, notices: &mut Vec<Notice>) -> Cow<'a, str> {
277    if name.len() <= NAME_LENGTH {
278        return name;
279    }
280    let mut length = NAME_LENGTH;
281    while !name.is_char_boundary(length) {
282        length -= 1;
283    }
284    notices.push(Notice {
285        severity: Severity::Notice,
286        code: SqlState::NAME_TOO_LONG,
287        message: format!("identifier \"{name}\" will be truncated to \"{}\"", &name[..length]),
288        location: None,
289    });
290    match name {
291        Cow::Borrowed(name) => Cow::Borrowed(&name[..length]),
292        Cow::Owned(mut name) => {
293            name.truncate(length);
294            Cow::Owned(name)
295        }
296    }
297}
298
299impl<'a> Lexer<'a> {
300    /// A lexer for `text`. The text ends at the first zero byte, as the C string that PostgreSQL gets does.
301    pub fn new(text: &'a str) -> Lexer<'a> {
302        let text = text.find('\0').map_or(text, |zero| &text[..zero]);
303        Lexer { text, bytes: text.as_bytes(), at: 0, notices: Vec::new() }
304    }
305
306    /// The text that the lexer reads.
307    pub fn text(&self) -> &'a str {
308        self.text
309    }
310
311    /// Takes the notices that the tokens until now gave.
312    pub fn take_notices(&mut self) -> Vec<Notice> {
313        std::mem::take(&mut self.notices)
314    }
315
316    pub(crate) fn notices(&mut self) -> &mut Vec<Notice> {
317        &mut self.notices
318    }
319
320    /// The byte at `i`, or 0 after the end, as the buffer of flex has it.
321    fn byte(&self, i: usize) -> u8 {
322        self.bytes.get(i).copied().unwrap_or(0)
323    }
324
325    fn syntax(&self, message: &str, start: usize, end: usize) -> Error {
326        Error::syntax(self.bytes, message, start, end)
327    }
328
329    fn emit(&mut self, kind: u16, start: usize, end: usize, value: Value<'a>) -> Token<'a> {
330        self.at = end;
331        Token { kind, start, end, value }
332    }
333
334    /// The next token. After the last token it gives the end, token 0, again and again.
335    pub fn next_token(&mut self) -> Result<Token<'a>, Error> {
336        self.skip()?;
337        let start = self.at;
338        let c = self.byte(start);
339        let n = self.byte(start + 1);
340        match c {
341            _ if start >= self.bytes.len() => Ok(self.emit(0, start, start, Value::None)),
342            b'\'' => self.quoted(Quote::Plain, start, start + 1),
343            b'"' => self.delimited(token::IDENT, start, start + 1),
344            b'$' => self.dollar(start),
345            b'0'..=b'9' => self.number(start),
346            b'.' if n.is_ascii_digit() => self.number(start),
347            b'.' if n == b'.' => Ok(self.emit(token::DOT_DOT, start, start + 2, Value::None)),
348            b':' if n == b':' => Ok(self.emit(token::TYPECAST, start, start + 2, Value::None)),
349            b':' if n == b'=' => Ok(self.emit(token::COLON_EQUALS, start, start + 2, Value::None)),
350            b'b' | b'B' if n == b'\'' => self.quoted(Quote::Bit, start, start + 2),
351            b'x' | b'X' if n == b'\'' => self.quoted(Quote::Hex, start, start + 2),
352            b'e' | b'E' if n == b'\'' => self.quoted(Quote::Escape, start, start + 2),
353            b'n' | b'N' if n == b'\'' => {
354                // `xnstart`: the lexer gives the keyword NCHAR for the `n`, and then the string.
355                let nchar = keywords::lookup(b"nchar");
356                let kind = nchar.map_or(token::NCHAR, |k| k.token);
357                let value = nchar.map_or(Value::None, Value::Keyword);
358                Ok(self.emit(kind, start, start + 1, value))
359            }
360            b'u' | b'U' if n == b'&' => match self.byte(start + 2) {
361                b'\'' => self.quoted(Quote::Unicode, start, start + 3),
362                b'"' => self.delimited(token::UIDENT, start, start + 3),
363                // `xufailed`: the `u` is an identifier, and not a keyword.
364                _ => {
365                    let value = Value::Text(downcase(&self.text[start..start + 1]));
366                    Ok(self.emit(token::IDENT, start, start + 1, value))
367                }
368            },
369            _ if is_ident_start(c) => Ok(self.identifier(start)),
370            _ if is_op_char(c) => self.operator(start),
371            // `self` and `other`.
372            _ => {
373                let kind = character(c).unwrap_or(UNDEFINED);
374                Ok(self.emit(kind, start, start + 1, Value::None))
375            }
376        }
377    }
378
379    /// Skips `whitespace`, which is spaces and `--` comments, and `/* */` comments, which can nest. `/*` always starts a comment, and `--` always starts a comment, because those rules come before `operator` and their match is never shorter.
380    fn skip(&mut self) -> Result<(), Error> {
381        loop {
382            match self.byte(self.at) {
383                b if is_space(b) && self.at < self.bytes.len() => self.at += 1,
384                b'-' if self.byte(self.at + 1) == b'-' => self.at = self.line_end(self.at),
385                b'/' if self.byte(self.at + 1) == b'*' => self.comment()?,
386                _ => return Ok(()),
387            }
388        }
389    }
390
391    /// The offset of the newline that ends the line of `from`, or the end of the text.
392    fn line_end(&self, from: usize) -> usize {
393        self.bytes[from..]
394            .iter()
395            .position(|&b| b == b'\n' || b == b'\r')
396            .map_or(self.bytes.len(), |at| from + at)
397    }
398
399    /// The state `xc`.
400    fn comment(&mut self) -> Result<(), Error> {
401        let start = self.at;
402        let mut depth = 0usize;
403        let mut i = start + 2;
404        loop {
405            if i >= self.bytes.len() {
406                return Err(self.syntax("unterminated /* comment", start, i));
407            }
408            match (self.bytes[i], self.byte(i + 1)) {
409                (b'/', b'*') => {
410                    depth += 1;
411                    i += 2;
412                }
413                (b'*', b'/') => {
414                    if depth == 0 {
415                        self.at = i + 2;
416                        return Ok(());
417                    }
418                    depth -= 1;
419                    i += 2;
420                }
421                _ => i += 1,
422            }
423        }
424    }
425
426    /// The state `xqs`: after the end quote of a string, the string goes on when spaces with a newline and another quote come next. Gives the offset after that quote.
427    fn continuation(&self, mut i: usize) -> Option<usize> {
428        let mut newline = false;
429        loop {
430            match self.byte(i) {
431                b' ' | b'\t' | 0x0b | 0x0c => i += 1,
432                b'\n' | b'\r' => {
433                    newline = true;
434                    i += 1;
435                }
436                b'-' if self.byte(i + 1) == b'-' => i = self.line_end(i),
437                b'\'' if newline => return Some(i + 1),
438                _ => return None,
439            }
440        }
441    }
442
443    /// The states `xb`, `xh`, `xq`, `xe` and `xus`. `open` is the offset after the first quote.
444    fn quoted(&mut self, quote: Quote, start: usize, open: usize) -> Result<Token<'a>, Error> {
445        let bytes = self.bytes;
446        let mut literal = Literal::new();
447        match quote {
448            Quote::Bit => literal.push_byte(bytes, b'b'),
449            Quote::Hex => literal.push_byte(bytes, b'x'),
450            _ => {}
451        }
452        let mut verify = false;
453        let mut i = open;
454        let end = loop {
455            if i >= bytes.len() {
456                let message = match quote {
457                    Quote::Bit => "unterminated bit string literal",
458                    Quote::Hex => "unterminated hexadecimal string literal",
459                    _ => "unterminated quoted string",
460                };
461                return Err(self.syntax(message, start, i));
462            }
463            match bytes[i] {
464                // `xqdouble`. It is not in `xb` and `xh`.
465                b'\'' if self.byte(i + 1) == b'\'' && !matches!(quote, Quote::Bit | Quote::Hex) => {
466                    literal.push(bytes, i, i + 1);
467                    i += 2;
468                }
469                b'\'' => match self.continuation(i + 1) {
470                    Some(next) => i = next,
471                    None => break i + 1,
472                },
473                b'\\' if quote == Quote::Escape => {
474                    i = self.escape(i, &mut literal, &mut verify)?;
475                }
476                _ => {
477                    let stop = |&b: &u8| b == b'\'' || (b == b'\\' && quote == Quote::Escape);
478                    let run = bytes[i..].iter().position(stop).map_or(bytes.len(), |n| i + n);
479                    literal.push(bytes, i, run);
480                    i = run;
481                }
482            }
483        };
484        let kind = match quote {
485            Quote::Bit => token::BCONST,
486            Quote::Hex => token::XCONST,
487            Quote::Plain | Quote::Escape => token::SCONST,
488            Quote::Unicode => token::USCONST,
489        };
490        let text = literal.finish(self.text, verify)?;
491        Ok(self.emit(kind, start, end, Value::Text(text)))
492    }
493
494    /// One escape of `E'...'` at the backslash at `i`: `xeescape`, `xeoctesc`, `xehexesc`, `xeunicode` and the state `xeu`. Gives the offset after it.
495    fn escape(&self, i: usize, literal: &mut Literal, verify: &mut bool) -> Result<usize, Error> {
496        let bytes = self.bytes;
497        let n = self.byte(i + 1);
498        if i + 1 >= bytes.len() {
499            // `<xe>.`, for a backslash just before the end.
500            literal.push_byte(bytes, b'\\');
501            return Ok(i + 1);
502        }
503        let mut byte = |b: u8, literal: &mut Literal| {
504            literal.push_byte(bytes, b);
505            if b == 0 || b >= 0x80 {
506                *verify = true;
507            }
508        };
509        match n {
510            b'0'..=b'7' => {
511                let digits =
512                    bytes[i + 1..].iter().take(3).take_while(|b| (b'0'..=b'7').contains(b));
513                let length = digits.clone().count();
514                // The value is an unsigned char in C, so `\777` is 0xff.
515                let value = digits.fold(0u32, |v, &d| (v << 3) | u32::from(d - b'0'));
516                byte((value & 0xff) as u8, literal);
517                Ok(i + 1 + length)
518            }
519            b'x' if self.byte(i + 2).is_ascii_hexdigit() => {
520                let length = if self.byte(i + 3).is_ascii_hexdigit() { 2 } else { 1 };
521                byte(hex_value(&bytes[i + 2..i + 2 + length]) as u8, literal);
522                Ok(i + 2 + length)
523            }
524            b'u' | b'U' => {
525                let (c, after) = self.unicode(i)?;
526                let c = if is_first_surrogate(c) {
527                    // The state `xeu`: the second half of the pair must come next.
528                    if self.byte(after) != b'\\' || !matches!(self.byte(after + 1), b'u' | b'U') {
529                        let end = (after + 1).min(bytes.len());
530                        return Err(self.syntax("invalid Unicode surrogate pair", after, end));
531                    }
532                    let (second, end) = self.unicode(after)?;
533                    if !is_second_surrogate(second) {
534                        return Err(self.syntax("invalid Unicode surrogate pair", after, end));
535                    }
536                    return self.push_unicode(surrogate_pair(c, second), after, end, literal);
537                } else if is_second_surrogate(c) {
538                    return Err(self.syntax("invalid Unicode surrogate pair", i, after));
539                } else {
540                    c
541                };
542                self.push_unicode(c, i, after, literal)
543            }
544            _ => {
545                // `xeescape`, one byte after the backslash, as `unescape_single_char` reads it.
546                let value = match n {
547                    b'b' => 0x08,
548                    b'f' => 0x0c,
549                    b'n' => b'\n',
550                    b'r' => b'\r',
551                    b't' => b'\t',
552                    b'v' => 0x0b,
553                    _ => n,
554                };
555                byte(value, literal);
556                Ok(i + 2)
557            }
558        }
559    }
560
561    /// `xeunicode` at the backslash at `i`: the code point and the offset after it. A `\u` with less than four digits, or a `\U` with less than eight, is `xeunicodefail`.
562    fn unicode(&self, i: usize) -> Result<(u32, usize), Error> {
563        let length = if self.byte(i + 1) == b'u' { 4 } else { 8 };
564        let digits = &self.bytes[i + 2..];
565        if digits.len() < length || !digits[..length].iter().all(u8::is_ascii_hexdigit) {
566            return Err(Error {
567                code: SqlState::INVALID_ESCAPE_SEQUENCE,
568                message: "invalid Unicode escape".to_owned(),
569                detail: None,
570                hint: Some("Unicode escapes must be \\uXXXX or \\UXXXXXXXX."),
571                location: Some(i),
572            });
573        }
574        Ok((hex_value(&digits[..length]), i + 2 + length))
575    }
576
577    /// `addunicode`.
578    fn push_unicode(
579        &self,
580        c: u32,
581        start: usize,
582        end: usize,
583        literal: &mut Literal,
584    ) -> Result<usize, Error> {
585        match char::from_u32(c).filter(|&c| c != '\0') {
586            Some(c) => {
587                literal.push_char(self.bytes, c);
588                Ok(end)
589            }
590            None => Err(self.syntax("invalid Unicode escape value", start, end)),
591        }
592    }
593
594    /// The states `xd` and `xui`. `open` is the offset after the first double quote.
595    fn delimited(&mut self, kind: u16, start: usize, open: usize) -> Result<Token<'a>, Error> {
596        let bytes = self.bytes;
597        let mut literal = Literal::new();
598        let mut i = open;
599        let end = loop {
600            if i >= bytes.len() {
601                return Err(self.syntax("unterminated quoted identifier", start, i));
602            }
603            if bytes[i] == b'"' {
604                if self.byte(i + 1) != b'"' {
605                    break i + 1;
606                }
607                literal.push(bytes, i, i + 1);
608                i += 2;
609            } else {
610                let run = bytes[i..].iter().position(|&b| b == b'"').map_or(bytes.len(), |n| i + n);
611                literal.push(bytes, i, run);
612                i = run;
613            }
614        };
615        if literal.is_empty() {
616            return Err(self.syntax("zero-length delimited identifier", start, end));
617        }
618        let mut name = literal.finish(self.text, false)?;
619        // A `U&"..."` name is cut after the escapes are made, in the filter.
620        if kind == token::IDENT {
621            name = truncate(name, &mut self.notices);
622        }
623        Ok(self.emit(kind, start, end, Value::Text(name)))
624    }
625
626    /// A `$`: `dolqdelim`, `dolqfailed`, `param`, `param_junk` or `other`.
627    fn dollar(&mut self, start: usize) -> Result<Token<'a>, Error> {
628        let n = self.byte(start + 1);
629        if n.is_ascii_digit() {
630            let digits = self.bytes[start + 1..].iter().take_while(|b| b.is_ascii_digit()).count();
631            let end = start + 1 + digits;
632            if is_ident_start(self.byte(end)) {
633                let end = self.identifier_end(end);
634                return Err(self.syntax("trailing junk after parameter", start, end));
635            }
636            return match integer(&self.bytes[start + 1..end]) {
637                Some(n) => Ok(self.emit(token::PARAM, start, end, Value::Integer(n))),
638                None => Err(self.syntax("parameter number too large", start, end)),
639            };
640        }
641        match self.delimiter(start) {
642            Some(open) => self.dollar_quoted(start, open),
643            // `dolqfailed` gives back all but the `$`, and the `$` is `other`.
644            None => {
645                Ok(self.emit(character(b'$').unwrap_or(UNDEFINED), start, start + 1, Value::None))
646            }
647        }
648    }
649
650    /// The offset after the `dolqdelim` at `i`, or `None` when there is none.
651    fn delimiter(&self, i: usize) -> Option<usize> {
652        let mut j = i + 1;
653        if is_ident_start(self.byte(j)) {
654            j += self.bytes[j..].iter().take_while(|&&b| is_dolq_cont(b)).count();
655        }
656        (self.byte(j) == b'$').then_some(j + 1)
657    }
658
659    /// The state `xdolq`. The text between the delimiters is the value, with no escapes.
660    fn dollar_quoted(&mut self, start: usize, open: usize) -> Result<Token<'a>, Error> {
661        let bytes = self.bytes;
662        let tag = &bytes[start..open];
663        let mut i = open;
664        loop {
665            let Some(at) = bytes[i..].iter().position(|&b| b == b'$') else {
666                return Err(self.syntax("unterminated dollar-quoted string", start, bytes.len()));
667            };
668            let at = i + at;
669            match self.delimiter(at) {
670                Some(end) if &bytes[at..end] == tag => {
671                    let value = Value::Text(Cow::Borrowed(&self.text[open..at]));
672                    return Ok(self.emit(token::SCONST, start, end, value));
673                }
674                // Another delimiter: its last `$` can start the delimiter that ends the string.
675                Some(end) => i = end - 1,
676                None => i = at + 1,
677            }
678        }
679    }
680
681    /// The offset after the `identifier` that starts at `i`.
682    fn identifier_end(&self, i: usize) -> usize {
683        i + self.bytes[i..].iter().take_while(|&&b| is_ident_cont(b)).count()
684    }
685
686    /// `identifier`: a keyword, or a name in lower case.
687    fn identifier(&mut self, start: usize) -> Token<'a> {
688        let end = self.identifier_end(start + 1);
689        let word = &self.text[start..end];
690        if let Some(keyword) = keywords::lookup(word.as_bytes()) {
691            return self.emit(keyword.token, start, end, Value::Keyword(keyword));
692        }
693        let name = truncate(downcase(word), &mut self.notices);
694        self.emit(token::IDENT, start, end, Value::Text(name))
695    }
696
697    /// The length of `decinteger` at `i`, or 0.
698    fn decimal(&self, i: usize) -> usize {
699        if !self.byte(i).is_ascii_digit() {
700            return 0;
701        }
702        let mut j = i + 1;
703        loop {
704            if self.byte(j).is_ascii_digit() {
705                j += 1;
706            } else if self.byte(j) == b'_' && self.byte(j + 1).is_ascii_digit() {
707                j += 2;
708            } else {
709                return j - i;
710            }
711        }
712    }
713
714    /// The length of `hexinteger`, `octinteger` or `bininteger` at `i`, with digits in `radix`.
715    fn prefixed(&self, i: usize, radix: u32) -> usize {
716        let digit = |b: u8| char::from(b).is_digit(radix);
717        let mut j = i + 2;
718        loop {
719            if digit(self.byte(j)) {
720                j += 1;
721            } else if self.byte(j) == b'_' && digit(self.byte(j + 1)) {
722                j += 2;
723            } else {
724                return if j == i + 2 { 0 } else { j - i };
725            }
726        }
727    }
728
729    /// The number rules, from `decinteger` to `real_junk`. Each rule gives the length of its match, and the longest match wins. When two have the same length, the first rule wins, as in flex.
730    fn number(&mut self, start: usize) -> Result<Token<'a>, Error> {
731        #[derive(Clone, Copy)]
732        enum Rule {
733            Integer,
734            Prefixed,
735            HexFail,
736            OctFail,
737            BinFail,
738            Numeric,
739            NumericFail,
740            Real,
741            RealFail,
742            Junk,
743        }
744        let mut best = (0, Rule::Integer);
745        let mut offer = |length: usize, rule: Rule| {
746            if length > best.0 {
747                best = (length, rule);
748            }
749        };
750        let whole = self.decimal(start);
751        offer(whole, Rule::Integer);
752        if self.byte(start) == b'0' {
753            let (radix, fail) = match self.byte(start + 1) {
754                b'x' | b'X' => (16, Rule::HexFail),
755                b'o' | b'O' => (8, Rule::OctFail),
756                b'b' | b'B' => (2, Rule::BinFail),
757                _ => (0, Rule::Integer),
758            };
759            if radix != 0 {
760                offer(self.prefixed(start, radix), Rule::Prefixed);
761                offer(if self.byte(start + 2) == b'_' { 3 } else { 2 }, fail);
762            }
763        }
764        // `numeric` is `1.`, `1.5` or `.5`. `numericfail` is `1..`, which gives back the `..`.
765        let numeric = if whole > 0 && self.byte(start + whole) == b'.' {
766            if self.byte(start + whole + 1) == b'.' {
767                offer(whole + 2, Rule::NumericFail);
768            }
769            whole + 1 + self.decimal(start + whole + 1)
770        } else if whole == 0 {
771            1 + self.decimal(start + 1)
772        } else {
773            0
774        };
775        offer(numeric, Rule::Numeric);
776        let mut real = 0;
777        for base in [whole, numeric] {
778            if base == 0 || !matches!(self.byte(start + base), b'e' | b'E') {
779                continue;
780            }
781            let mut j = start + base + 1;
782            let sign = matches!(self.byte(j), b'+' | b'-');
783            if sign {
784                j += 1;
785            }
786            let digits = self.decimal(j);
787            if digits > 0 {
788                real = real.max(j + digits - start);
789                offer(j + digits - start, Rule::Real);
790            } else if sign {
791                offer(j - start, Rule::RealFail);
792            }
793        }
794        // `integer_junk`, `numeric_junk` and `real_junk`: a number with an identifier after it. The identifier can also start at a `_` in the digits, because a `decinteger` can stop before any `_` and an identifier can start with `_`. So `1_000$1` is `1` and `_000$1`, and `1_0.5_0$` is `1_0.5` and `_0$`.
795        for base in [whole, numeric, real] {
796            if base > 0 && is_ident_start(self.byte(start + base)) {
797                offer(self.identifier_end(start + base) - start, Rule::Junk);
798            }
799        }
800        let digits = start..start + whole.max(numeric).max(real);
801        for underscore in digits.filter(|&i| self.bytes[i] == b'_') {
802            offer(self.identifier_end(underscore) - start, Rule::Junk);
803        }
804        let (length, rule) = best;
805        let end = start + length;
806        let text = &self.text[start..end];
807        let message = match rule {
808            Rule::Integer | Rule::Prefixed | Rule::NumericFail => {
809                let end = if matches!(rule, Rule::NumericFail) { end - 2 } else { end };
810                let text = &self.text[start..end];
811                // `process_integer_literal`: a value too large for 32 bits is FCONST.
812                return Ok(match integer(text.as_bytes()) {
813                    Some(n) => self.emit(token::ICONST, start, end, Value::Integer(n)),
814                    None => self.emit(token::FCONST, start, end, Value::Text(Cow::Borrowed(text))),
815                });
816            }
817            Rule::Numeric | Rule::Real => {
818                return Ok(self.emit(token::FCONST, start, end, Value::Text(Cow::Borrowed(text))));
819            }
820            Rule::HexFail => "invalid hexadecimal integer",
821            Rule::OctFail => "invalid octal integer",
822            Rule::BinFail => "invalid binary integer",
823            Rule::RealFail | Rule::Junk => "trailing junk after numeric literal",
824        };
825        Err(self.syntax(message, start, end))
826    }
827
828    /// `operator`, with the special operators and the characters of `self`.
829    fn operator(&mut self, start: usize) -> Result<Token<'a>, Error> {
830        let run = &self.bytes
831            [start..start + self.bytes[start..].iter().take_while(|&&b| is_op_char(b)).count()];
832        // A comment start in the operator ends it. It is never at the first character, because then the comment rule matched.
833        let mut length = run.windows(2).position(|w| w == b"/*" || w == b"--").unwrap_or(run.len());
834        // A multi-character operator cannot end in `+` or `-`, unless it has a character that is not in the operators of the SQL standard. So `=-` is two operators and `?-` is one.
835        if length > 1 && matches!(run[length - 1], b'+' | b'-') {
836            let qualifying = |b: &u8| {
837                matches!(b, b'~' | b'!' | b'@' | b'#' | b'^' | b'&' | b'|' | b'`' | b'?' | b'%')
838            };
839            if !run[..length - 1].iter().any(qualifying) {
840                while length > 1 && matches!(run[length - 1], b'+' | b'-') {
841                    length -= 1;
842                }
843            }
844        }
845        let end = start + length;
846        let kind = match &run[..length] {
847            [c] if is_self(*c) => character(*c),
848            b"=>" => Some(token::EQUALS_GREATER),
849            b"<=" => Some(token::LESS_EQUALS),
850            b">=" => Some(token::GREATER_EQUALS),
851            b"<>" | b"!=" => Some(token::NOT_EQUALS),
852            _ => None,
853        };
854        if let Some(kind) = kind {
855            return Ok(self.emit(kind, start, end, Value::None));
856        }
857        if length > NAME_LENGTH {
858            return Err(self.syntax("operator too long", start, end));
859        }
860        let value = Value::Text(Cow::Borrowed(&self.text[start..end]));
861        Ok(self.emit(token::OP, start, end, value))
862    }
863}
864
865#[cfg(test)]
866mod tests {
867    use super::*;
868    use crate::token::*;
869
870    /// The kinds and the values of the tokens of `text`, without the end.
871    fn lex(text: &str) -> Vec<(u16, Value<'_>)> {
872        let mut lexer = Lexer::new(text);
873        let mut tokens = Vec::new();
874        loop {
875            let token = lexer.next_token().unwrap();
876            if token.kind == 0 {
877                return tokens;
878            }
879            tokens.push((token.kind, token.value));
880        }
881    }
882
883    fn text(s: &str) -> Value<'_> {
884        Value::Text(Cow::Borrowed(s))
885    }
886
887    fn c(ch: u8) -> u16 {
888        character(ch).unwrap()
889    }
890
891    #[test]
892    fn names_and_keywords() {
893        let select = keywords::lookup(b"select").unwrap();
894        assert_eq!(
895            lex("SeLeCt Foo \"Foo\" é_$1"),
896            [
897                (SELECT, Value::Keyword(select)),
898                (IDENT, text("foo")),
899                (IDENT, text("Foo")),
900                (IDENT, text("é_$1")),
901            ]
902        );
903        // `xufailed`: `u&` with no quote after it is the name `u` and the operator `&`.
904        assert_eq!(lex("U&x"), [(IDENT, text("u")), (OP, text("&")), (IDENT, text("x"))]);
905        assert_eq!(
906            lex("n'a'"),
907            [(NCHAR, Value::Keyword(keywords::lookup(b"nchar").unwrap())), (SCONST, text("a"))]
908        );
909    }
910
911    #[test]
912    fn long_names() {
913        let long = "a".repeat(70);
914        let mut lexer = Lexer::new(&long);
915        assert_eq!(lexer.next_token().unwrap().value, text(&long[..63]));
916        let notices = lexer.take_notices();
917        assert_eq!(notices.len(), 1);
918        assert_eq!(notices[0].code.as_str(), "42622");
919        // A cut does not split a character: 31 characters of two bytes are 62 bytes.
920        let long = "é".repeat(40);
921        assert_eq!(lex(&long), [(IDENT, text(&long[..62]))]);
922    }
923
924    #[test]
925    fn strings() {
926        assert_eq!(lex("'it''s'"), [(SCONST, text("it's"))]);
927        assert_eq!(lex("'a\\b'"), [(SCONST, text("a\\b"))]);
928        assert_eq!(lex("'a'\n'b' 'c'"), [(SCONST, text("ab")), (SCONST, text("c"))]);
929        assert_eq!(lex("'a' -- x\n 'b'"), [(SCONST, text("ab"))]);
930        assert_eq!(lex("E'\\n\\t\\101\\x41\\'\\q'"), [(SCONST, text("\n\tAA'q"))]);
931        assert_eq!(lex("E'\\u00e9\\U0001F600\\ud83d\\ude00'"), [(SCONST, text("é😀😀"))]);
932        assert_eq!(lex("E'\\303\\251'"), [(SCONST, text("é"))]);
933        assert_eq!(lex("b'01' x'0F'"), [(BCONST, text("b01")), (XCONST, text("x0F"))]);
934        assert_eq!(lex("U&'\\0041'"), [(USCONST, text("\\0041"))]);
935        assert_eq!(lex("U&\"a\""), [(UIDENT, text("a"))]);
936        assert_eq!(lex("$$a'b$$ $x$a$y$b$x$"), [(SCONST, text("a'b")), (SCONST, text("a$y$b"))]);
937        // `dolqfailed`: `$a` with no `$` after it is the character `$`.
938        assert_eq!(lex("$a")[0].0, UNDEFINED);
939    }
940
941    #[test]
942    fn numbers() {
943        assert_eq!(
944            lex("1 1_000 0x1F 0o17 0b101"),
945            [
946                (ICONST, Value::Integer(1)),
947                (ICONST, Value::Integer(1000)),
948                (ICONST, Value::Integer(31)),
949                (ICONST, Value::Integer(15)),
950                (ICONST, Value::Integer(5)),
951            ]
952        );
953        assert_eq!(
954            lex("2147483648 1.5 .5 1e3 1.5E-3"),
955            [
956                (FCONST, text("2147483648")),
957                (FCONST, text("1.5")),
958                (FCONST, text(".5")),
959                (FCONST, text("1e3")),
960                (FCONST, text("1.5E-3")),
961            ]
962        );
963        // `numericfail`: `1..2` is `1`, `..` and `2`.
964        assert_eq!(
965            lex("1..2"),
966            [(ICONST, Value::Integer(1)), (DOT_DOT, Value::None), (ICONST, Value::Integer(2))]
967        );
968        assert_eq!(lex("$12"), [(PARAM, Value::Integer(12))]);
969    }
970
971    #[test]
972    fn operators() {
973        assert_eq!(
974            lex(":: := => <= >= <> != . ; :"),
975            [
976                (TYPECAST, Value::None),
977                (COLON_EQUALS, Value::None),
978                (EQUALS_GREATER, Value::None),
979                (LESS_EQUALS, Value::None),
980                (GREATER_EQUALS, Value::None),
981                (NOT_EQUALS, Value::None),
982                (NOT_EQUALS, Value::None),
983                (c(b'.'), Value::None),
984                (c(b';'), Value::None),
985                (c(b':'), Value::None),
986            ]
987        );
988        // A multi-character operator ends in `+` or `-` only with a character such as `@`.
989        assert_eq!(lex("=-"), [(c(b'='), Value::None), (c(b'-'), Value::None)]);
990        assert_eq!(lex("@-"), [(OP, text("@-"))]);
991        assert_eq!(lex("<=-"), [(LESS_EQUALS, Value::None), (c(b'-'), Value::None)]);
992        assert_eq!(lex("+/*x*/-"), [(c(b'+'), Value::None), (c(b'-'), Value::None)]);
993        assert_eq!(lex("~--x"), [(OP, text("~"))]);
994        assert_eq!(lex("/* a /* b */ c */ 1")[0].0, ICONST);
995    }
996
997    #[test]
998    fn locations() {
999        let mut lexer = Lexer::new("  é 'x'\n");
1000        let token = lexer.next_token().unwrap();
1001        assert_eq!((token.start, token.end), (2, 4));
1002        let token = lexer.next_token().unwrap();
1003        assert_eq!((token.start, token.end), (5, 8));
1004        let token = lexer.next_token().unwrap();
1005        assert_eq!((token.kind, token.start, token.end), (0, 9, 9));
1006        // The text ends at a zero byte, as the C string of PostgreSQL does.
1007        assert_eq!(lex("1\u{0}2"), [(ICONST, Value::Integer(1))]);
1008    }
1009}