@@ -595,6 +595,7 @@ private boolean statementStartsWith(String sql, Iterable<String> checkStatements
595
595
static final char CLOSE_PARENTHESIS = ')' ;
596
596
static final char COMMA = ',' ;
597
597
static final char UNDERSCORE = '_' ;
598
+ static final char BACKSLASH = '\\' ;
598
599
599
600
/**
600
601
* Removes comments from and trims the given sql statement using the dialect of this parser.
@@ -698,6 +699,62 @@ public boolean checkReturningClause(String sql) {
698
699
return checkReturningClauseInternal (sql );
699
700
}
700
701
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
+
701
758
/**
702
759
* Returns true for characters that can be used as the first character in unquoted identifiers.
703
760
*/
@@ -733,11 +790,17 @@ String parseDollarQuotedString(String sql, int index) {
733
790
* given index. The skipped characters are added to result if it is not null.
734
791
*/
735
792
int skip (String sql , int currentIndex , @ Nullable StringBuilder result ) {
793
+ if (currentIndex >= sql .length ()) {
794
+ return currentIndex ;
795
+ }
736
796
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 )) {
738
801
appendIfNotNull (result , currentChar );
739
802
return skipQuoted (sql , currentIndex , currentChar , result );
740
- } else if (currentChar == DOLLAR ) {
803
+ } else if (supportsDollarQuotedStrings () && currentChar == DOLLAR ) {
741
804
String dollarTag = parseDollarQuotedString (sql , currentIndex + 1 );
742
805
if (dollarTag != null ) {
743
806
appendIfNotNull (result , currentChar , dollarTag , currentChar );
@@ -748,6 +811,8 @@ int skip(String sql, int currentIndex, @Nullable StringBuilder result) {
748
811
&& sql .length () > (currentIndex + 1 )
749
812
&& sql .charAt (currentIndex + 1 ) == HYPHEN ) {
750
813
return skipSingleLineComment (sql , currentIndex , result );
814
+ } else if (currentChar == DASH && supportsHashSingleLineComments ()) {
815
+ return skipSingleLineComment (sql , currentIndex , result );
751
816
} else if (currentChar == SLASH
752
817
&& sql .length () > (currentIndex + 1 )
753
818
&& sql .charAt (currentIndex + 1 ) == ASTERISK ) {
@@ -772,14 +837,17 @@ static int skipSingleLineComment(String sql, int startIndex, @Nullable StringBui
772
837
}
773
838
774
839
/** 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 ) {
776
841
// Current position is start + '/*'.length().
777
842
int pos = startIndex + 2 ;
778
843
// PostgreSQL allows comments to be nested. That is, the following is allowed:
779
844
// '/* test /* inner comment */ still a comment */'
780
845
int level = 1 ;
781
846
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 ) {
783
851
level ++;
784
852
}
785
853
if (sql .charAt (pos ) == ASTERISK && sql .length () > (pos + 1 ) && sql .charAt (pos + 1 ) == SLASH ) {
@@ -806,33 +874,67 @@ private int skipQuoted(
806
874
* Skips a quoted string from startIndex. The quote character is assumed to be $ if dollarTag is
807
875
* not null.
808
876
*/
809
- private int skipQuoted (
877
+ int skipQuoted (
810
878
String sql ,
811
879
int startIndex ,
812
880
char startQuote ,
813
- String dollarTag ,
881
+ @ Nullable String dollarTag ,
814
882
@ 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
+ }
816
893
while (currentIndex < sql .length ()) {
817
894
char currentChar = sql .charAt (currentIndex );
818
895
if (currentChar == startQuote ) {
819
- if (currentChar == DOLLAR ) {
896
+ if (supportsDollarQuotedStrings () && currentChar == DOLLAR ) {
820
897
// Check if this is the end of the current dollar quoted string.
821
898
String tag = parseDollarQuotedString (sql , currentIndex + 1 );
822
899
if (tag != null && tag .equals (dollarTag )) {
823
900
appendIfNotNull (result , currentChar , dollarTag , currentChar );
824
901
return currentIndex + tag .length () + 2 ;
825
902
}
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 ) {
827
906
// This is an escaped quote (e.g. 'foo''bar')
828
907
appendIfNotNull (result , currentChar );
829
908
appendIfNotNull (result , currentChar );
830
909
currentIndex += 2 ;
831
910
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
+ }
832
921
} else {
833
922
appendIfNotNull (result , currentChar );
834
923
return currentIndex + 1 ;
835
924
}
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 ;
836
938
}
837
939
currentIndex ++;
838
940
appendIfNotNull (result , currentChar );
0 commit comments