@@ -20856,3 +20856,92 @@ fn parse_window_spec_keyword_base_window_name() {
2085620856 "SELECT count(*) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM t",
2085720857 );
2085820858}
20859+
20860+ #[test]
20861+ fn tokenize_identifier_prefixes_and_unicode_spans() {
20862+ let suffixes = [
20863+ String::new(),
20864+ "_name".to_owned(),
20865+ "alpha".to_owned(),
20866+ "é".to_owned(),
20867+ "漢字".to_owned(),
20868+ "λ".to_owned(),
20869+ "\u{0301}".to_owned(),
20870+ "a".repeat(128),
20871+ ];
20872+ for dialect in all_dialects().dialects {
20873+ for prefix in [
20874+ "a", "b", "B", "n", "N", "nq", "NQ", "nQ", "Nq", "q", "Q", "r", "R", "x", "X", "e",
20875+ "E", "u", "U", "é", "漢", "λ",
20876+ ] {
20877+ for suffix in &suffixes {
20878+ let word = format!("{prefix}{suffix}");
20879+ let mut chars = word.chars();
20880+ if !dialect.is_identifier_start(chars.next().unwrap())
20881+ || !chars.all(|ch| dialect.is_identifier_part(ch))
20882+ {
20883+ continue;
20884+ }
20885+ let sql = format!("\n{word}\t/*tail*/");
20886+ let tokens = Tokenizer::new(&*dialect, &sql)
20887+ .tokenize_with_location()
20888+ .unwrap();
20889+ assert_eq!(
20890+ tokens.iter().map(|t| t.token.clone()).collect::<Vec<_>>(),
20891+ vec![
20892+ Token::Whitespace(Whitespace::Newline),
20893+ Token::make_word(&word, None),
20894+ Token::Whitespace(Whitespace::Tab),
20895+ Token::Whitespace(Whitespace::MultiLineComment("tail".to_owned())),
20896+ ],
20897+ "{dialect:?}: {sql:?}"
20898+ );
20899+ assert_eq!(
20900+ tokens[1].span,
20901+ Span::new(
20902+ Location::new(2, 1),
20903+ Location::new(2, word.chars().count() as u64 + 1)
20904+ ),
20905+ "{dialect:?}: {sql:?}"
20906+ );
20907+ }
20908+ }
20909+ }
20910+ }
20911+
20912+ #[test]
20913+ fn tokenize_identifier_at_eof_and_before_error() {
20914+ for dialect in all_dialects().dialects {
20915+ let word = "nQalpha";
20916+ let tokens = Tokenizer::new(&*dialect, word)
20917+ .tokenize_with_location()
20918+ .unwrap();
20919+ assert_eq!(tokens.len(), 1, "{dialect:?}");
20920+ assert_eq!(tokens[0].token, Token::make_word(word, None));
20921+ assert_eq!(
20922+ tokens[0].span,
20923+ Span::new(Location::new(1, 1), Location::new(1, 8))
20924+ );
20925+
20926+ let mut buf = tokens.clone();
20927+ let error = Tokenizer::new(&*dialect, "nQalpha '")
20928+ .tokenize_with_location_into_buf(&mut buf)
20929+ .unwrap_err();
20930+ assert_eq!(error.message, "Unterminated string literal", "{dialect:?}");
20931+ // Quote error positions differ by dialect.
20932+ let quote_error = Tokenizer::new(&*dialect, "'").tokenize().unwrap_err();
20933+ assert_eq!(
20934+ error.location,
20935+ Location::new(1, quote_error.location.column + word.len() as u64 + 1),
20936+ "{dialect:?}"
20937+ );
20938+ assert_eq!(buf.len(), 3, "{dialect:?}");
20939+ assert_eq!(buf[0], tokens[0]);
20940+ assert_eq!(buf[1], tokens[0]);
20941+ assert_eq!(buf[2].token, Token::Whitespace(Whitespace::Space));
20942+ assert_eq!(
20943+ buf[2].span,
20944+ Span::new(Location::new(1, 8), Location::new(1, 9))
20945+ );
20946+ }
20947+ }
0 commit comments