@@ -1685,40 +1685,44 @@ TOKEN_MGR_DECLS : {
16851685 return -1;
16861686 }
16871687
1688- private static boolean endsWithDelimiter(Deque<Character> windowQueue, String delimiter) {
1689- if (windowQueue.size() != delimiter.length()) {
1690- return false;
1691- }
1692-
1693- int i = 0;
1694- for (char ch : windowQueue) {
1695- if (ch != delimiter.charAt(i++)) {
1696- return false;
1688+ /** Scans a literal in linear time without tokenizing or rebuilding its whitespace. */
1689+ public void consumeDollarQuotedString(String closingQuote) {
1690+ int[] prefix = new int[closingQuote.length()];
1691+ for (int i = 1, matched = 0; i < closingQuote.length(); i++) {
1692+ while (matched > 0 && closingQuote.charAt(i) != closingQuote.charAt(matched)) {
1693+ matched = prefix[matched - 1];
16971694 }
1695+ if (closingQuote.charAt(i) == closingQuote.charAt(matched)) {
1696+ matched++;
1697+ }
1698+ prefix[i] = matched;
16981699 }
1699- return true;
1700- }
1701-
1702- public void consumeDollarQuotedString(String closingQuote) {
1703- Deque<Character> windowQueue = new ArrayDeque<Character>();
1704- int delimiterLength = closingQuote.length();
1705-
17061700 try {
1707- while (true) {
1701+ int matched = 0;
1702+ while (matched < closingQuote.length()) {
17081703 char ch = input_stream.readChar();
1709- windowQueue.addLast(ch);
1710- if (windowQueue.size() > delimiterLength) {
1711- windowQueue.removeFirst();
1704+ while (matched > 0 && ch != closingQuote.charAt(matched)) {
1705+ matched = prefix[matched - 1];
17121706 }
1713- if (endsWithDelimiter(windowQueue, closingQuote)) {
1714- return ;
1707+ if (ch == closingQuote.charAt(matched )) {
1708+ matched++ ;
17151709 }
17161710 }
17171711 } catch (java.io.IOException e) {
17181712 reportError(Math.max(closingQuote.length(), input_stream.GetImage().length()));
17191713 }
17201714 }
17211715
1716+ /** Rewinds any identifier suffix consumed by longest-match lexing before scanning the body. */
1717+ private void consumeDollarQuotedToken(Token token, String delimiter) {
1718+ input_stream.backup(token.image.length() - delimiter.length());
1719+ consumeDollarQuotedString(delimiter);
1720+ token.image = input_stream.GetImage();
1721+ token.kind = charLiteralIndex;
1722+ token.endLine = input_stream.getEndLine();
1723+ token.endColumn = input_stream.getEndColumn();
1724+ }
1725+
17221726 /**
17231727 * Consumes the body of a block comment after the opening delimiter has been matched,
17241728 * honouring nesting, up to and including the outermost closing delimiter. Then backs
@@ -2428,9 +2432,7 @@ TOKEN:
24282432|
24292433<S_DOLLAR_QUOTED_STRING: "$$">
24302434 {
2431- consumeDollarQuotedString(matchedToken.image);
2432- matchedToken.image = input_stream.GetImage();
2433- matchedToken.kind = charLiteralIndex;
2435+ consumeDollarQuotedToken(matchedToken, matchedToken.image);
24342436 }
24352437|
24362438// Bare `#` as a binary operator (PostgreSQL bitwise XOR / geometric
@@ -2441,6 +2443,13 @@ TOKEN:
24412443|
24422444<S_IDENTIFIER: (<LETTER> (<PART_LETTER>)*) | "$" | ("$" <PART_LETTER_NO_DOLLAR> (<PART_LETTER>)*)>
24432445{
2446+ if (matchedToken.image.charAt(0) == '$'
2447+ && Boolean.TRUE.equals(configuration.getValue(Feature.allowDollarQuotedStringTags))) {
2448+ String delimiter = StringValue.getDollarQuoteDelimiter(matchedToken.image);
2449+ if (delimiter != null) {
2450+ consumeDollarQuotedToken(matchedToken, delimiter);
2451+ }
2452+ }
24442453 // MySQL `#` line comments (#2499): under the flag an unquoted identifier
24452454 // ends at its first `#`, the rest of the line becomes a comment via the
24462455 // stream-level substitution (real MySQL reads `42#24` as `42` plus
@@ -2449,7 +2458,7 @@ TOKEN:
24492458 // that never opted in (the stream is only wired through the feature
24502459 // consumers / withConfiguration); getValue avoids the String-based
24512460 // getAsBoolean roundtrip
2452- if (input_stream.featureConfiguration != null
2461+ if (matchedToken.kind == S_IDENTIFIER && input_stream.featureConfiguration != null
24532462 && Boolean.TRUE.equals(configuration.getValue(Feature.allowHashLineComments))) {
24542463 int hashIndex = matchedToken.image.indexOf('#');
24552464 if (hashIndex > 0) {
@@ -16822,7 +16831,7 @@ List<String> captureFunctionBody() {
1682216831 tokens.add(tok.image);
1682316832 }
1682416833 foundEnd |= (tok.kind == K_END)
16825- || ( tok.image.trim().startsWith("$$") && tok.image.trim().endsWith("$$")) ;
16834+ || (tok.kind == S_CHAR_LITERAL && StringValue.getDollarQuoteDelimiter( tok.image) != null) ;
1682616835
1682716836 tok = getNextToken();
1682816837 }
0 commit comments