@@ -595,6 +595,7 @@ private boolean statementStartsWith(String sql, Iterable<String> checkStatements
595595 static final char CLOSE_PARENTHESIS = ')' ;
596596 static final char COMMA = ',' ;
597597 static final char UNDERSCORE = '_' ;
598+ static final char BACKSLASH = '\\' ;
598599
599600 /**
600601 * Removes comments from and trims the given sql statement using the dialect of this parser.
@@ -698,6 +699,62 @@ public boolean checkReturningClause(String sql) {
698699 return checkReturningClauseInternal (sql );
699700 }
700701
702+ /**
703+ * <<<<<<< HEAD Returns true if this dialect supports nested comments.
704+ *
705+ * <ul>
706+ * <li>This method should return false for dialects that consider this to be a valid comment:
707+ * <code>/* A comment /* still a comment */</code>.
708+ * <li>This method should return true for dialects that require all comment start sequences to
709+ * be balanced with a comment end sequence: <code>
710+ * /* A comment /* still a comment */ Also still a comment */</code>.
711+ * </ul>
712+ */
713+ abstract boolean supportsNestedComments ();
714+
715+ /**
716+ * Returns true for dialects that support dollar-quoted string literals.
717+ *
718+ * <p>Example: <code>$tag$This is a string$tag$</code>.
719+ */
720+ abstract boolean supportsDollarQuotedStrings ();
721+
722+ /**
723+ * Returns true for dialects that support backticks as a quoting character, either for string
724+ * literals or identifiers.
725+ */
726+ abstract boolean supportsBacktickQuote ();
727+
728+ /**
729+ * Returns true for dialects that support triple-quoted string literals and identifiers.
730+ *
731+ * <p>Example: ```This is a triple-quoted string```
732+ */
733+ abstract boolean supportsTripleQuotedStrings ();
734+
735+ /**
736+ * Returns true if the dialect supports escaping a quote character within a literal with the same
737+ * quote as the literal is using. That is: 'foo''bar' means "foo'bar".
738+ */
739+ abstract boolean supportsEscapeQuoteWithQuote ();
740+
741+ /** Returns true if the dialect supports starting an escape sequence with a backslash. */
742+ abstract boolean supportsBackslashEscape ();
743+
744+ /**
745+ * Returns true if the dialect supports single-line comments that start with a dash.
746+ *
747+ * <p>Example: # This is a comment
748+ */
749+ abstract boolean supportsHashSingleLineComments ();
750+
751+ /**
752+ * Returns true for dialects that allow line-feeds in quoted strings. Note that the return value
753+ * of this is not used for triple-quoted strings. Triple-quoted strings are assumed to always
754+ * support line-feeds.
755+ */
756+ abstract boolean supportsLineFeedInQuotedString ();
757+
701758 /**
702759 * Returns true for characters that can be used as the first character in unquoted identifiers.
703760 */
@@ -733,11 +790,17 @@ String parseDollarQuotedString(String sql, int index) {
733790 * given index. The skipped characters are added to result if it is not null.
734791 */
735792 int skip (String sql , int currentIndex , @ Nullable StringBuilder result ) {
793+ if (currentIndex >= sql .length ()) {
794+ return currentIndex ;
795+ }
736796 char currentChar = sql .charAt (currentIndex );
737- if (currentChar == SINGLE_QUOTE || currentChar == DOUBLE_QUOTE ) {
797+
798+ if (currentChar == SINGLE_QUOTE
799+ || currentChar == DOUBLE_QUOTE
800+ || (supportsBacktickQuote () && currentChar == BACKTICK_QUOTE )) {
738801 appendIfNotNull (result , currentChar );
739802 return skipQuoted (sql , currentIndex , currentChar , result );
740- } else if (currentChar == DOLLAR ) {
803+ } else if (supportsDollarQuotedStrings () && currentChar == DOLLAR ) {
741804 String dollarTag = parseDollarQuotedString (sql , currentIndex + 1 );
742805 if (dollarTag != null ) {
743806 appendIfNotNull (result , currentChar , dollarTag , currentChar );
@@ -748,6 +811,8 @@ int skip(String sql, int currentIndex, @Nullable StringBuilder result) {
748811 && sql .length () > (currentIndex + 1 )
749812 && sql .charAt (currentIndex + 1 ) == HYPHEN ) {
750813 return skipSingleLineComment (sql , currentIndex , result );
814+ } else if (currentChar == DASH && supportsHashSingleLineComments ()) {
815+ return skipSingleLineComment (sql , currentIndex , result );
751816 } else if (currentChar == SLASH
752817 && sql .length () > (currentIndex + 1 )
753818 && sql .charAt (currentIndex + 1 ) == ASTERISK ) {
@@ -772,14 +837,17 @@ static int skipSingleLineComment(String sql, int startIndex, @Nullable StringBui
772837 }
773838
774839 /** Skips a multi-line comment from startIndex and adds it to result if result is not null. */
775- static int skipMultiLineComment (String sql , int startIndex , @ Nullable StringBuilder result ) {
840+ int skipMultiLineComment (String sql , int startIndex , @ Nullable StringBuilder result ) {
776841 // Current position is start + '/*'.length().
777842 int pos = startIndex + 2 ;
778843 // PostgreSQL allows comments to be nested. That is, the following is allowed:
779844 // '/* test /* inner comment */ still a comment */'
780845 int level = 1 ;
781846 while (pos < sql .length ()) {
782- if (sql .charAt (pos ) == SLASH && sql .length () > (pos + 1 ) && sql .charAt (pos + 1 ) == ASTERISK ) {
847+ if (supportsNestedComments ()
848+ && sql .charAt (pos ) == SLASH
849+ && sql .length () > (pos + 1 )
850+ && sql .charAt (pos + 1 ) == ASTERISK ) {
783851 level ++;
784852 }
785853 if (sql .charAt (pos ) == ASTERISK && sql .length () > (pos + 1 ) && sql .charAt (pos + 1 ) == SLASH ) {
@@ -806,33 +874,67 @@ private int skipQuoted(
806874 * Skips a quoted string from startIndex. The quote character is assumed to be $ if dollarTag is
807875 * not null.
808876 */
809- private int skipQuoted (
877+ int skipQuoted (
810878 String sql ,
811879 int startIndex ,
812880 char startQuote ,
813- String dollarTag ,
881+ @ Nullable String dollarTag ,
814882 @ Nullable StringBuilder result ) {
815- int currentIndex = startIndex + 1 ;
883+ boolean isTripleQuoted =
884+ supportsTripleQuotedStrings ()
885+ && sql .length () > startIndex + 2
886+ && sql .charAt (startIndex + 1 ) == startQuote
887+ && sql .charAt (startIndex + 2 ) == startQuote ;
888+ int currentIndex = startIndex + (isTripleQuoted ? 3 : 1 );
889+ if (isTripleQuoted ) {
890+ appendIfNotNull (result , startQuote );
891+ appendIfNotNull (result , startQuote );
892+ }
816893 while (currentIndex < sql .length ()) {
817894 char currentChar = sql .charAt (currentIndex );
818895 if (currentChar == startQuote ) {
819- if (currentChar == DOLLAR ) {
896+ if (supportsDollarQuotedStrings () && currentChar == DOLLAR ) {
820897 // Check if this is the end of the current dollar quoted string.
821898 String tag = parseDollarQuotedString (sql , currentIndex + 1 );
822899 if (tag != null && tag .equals (dollarTag )) {
823900 appendIfNotNull (result , currentChar , dollarTag , currentChar );
824901 return currentIndex + tag .length () + 2 ;
825902 }
826- } else if (sql .length () > currentIndex + 1 && sql .charAt (currentIndex + 1 ) == startQuote ) {
903+ } else if (supportsEscapeQuoteWithQuote ()
904+ && sql .length () > currentIndex + 1
905+ && sql .charAt (currentIndex + 1 ) == startQuote ) {
827906 // This is an escaped quote (e.g. 'foo''bar')
828907 appendIfNotNull (result , currentChar );
829908 appendIfNotNull (result , currentChar );
830909 currentIndex += 2 ;
831910 continue ;
911+ } else if (isTripleQuoted ) {
912+ // Check if this is the end of the triple-quoted string.
913+ if (sql .length () > currentIndex + 2
914+ && sql .charAt (currentIndex + 1 ) == startQuote
915+ && sql .charAt (currentIndex + 2 ) == startQuote ) {
916+ appendIfNotNull (result , currentChar );
917+ appendIfNotNull (result , currentChar );
918+ appendIfNotNull (result , currentChar );
919+ return currentIndex + 3 ;
920+ }
832921 } else {
833922 appendIfNotNull (result , currentChar );
834923 return currentIndex + 1 ;
835924 }
925+ } else if (supportsBackslashEscape ()
926+ && currentChar == BACKSLASH
927+ && sql .length () > currentIndex + 1
928+ && sql .charAt (currentIndex + 1 ) == startQuote ) {
929+ // This is an escaped quote (e.g. 'foo\'bar').
930+ // Note that in raw strings, the \ officially does not start an escape sequence, but the
931+ // result is still the same, as in a raw string 'both characters are preserved'.
932+ appendIfNotNull (result , currentChar );
933+ appendIfNotNull (result , sql .charAt (currentIndex + 1 ));
934+ currentIndex += 2 ;
935+ continue ;
936+ } else if (currentChar == '\n' && !isTripleQuoted && !supportsLineFeedInQuotedString ()) {
937+ break ;
836938 }
837939 currentIndex ++;
838940 appendIfNotNull (result , currentChar );
0 commit comments