@@ -216,6 +216,13 @@ func (p *StatementParser) supportsBackslashEscape() bool {
216216 return p .Dialect != databasepb .DatabaseDialect_POSTGRESQL
217217}
218218
219+ // supportsEscapeStrings returns true if the dialect supports enabling escaping using backslashes
220+ // by prepending an e or E to the string. Example:
221+ // e'It\'s a valid string' => This is the string "It's a valid string".
222+ func (p * StatementParser ) supportsEscapeStrings () bool {
223+ return p .Dialect == databasepb .DatabaseDialect_POSTGRESQL
224+ }
225+
219226// supportsEscapeQuoteWithQuote returns true if the dialect supports escaping a quote within a quoted
220227// literal by repeating the quote twice. Example (note that the way that two single quotes are written in the following
221228// examples is something that is enforced by gofmt):
@@ -402,6 +409,17 @@ func (p *StatementParser) skipMultiLineComment(sql []byte, pos int) int {
402409// could not be read.
403410// The quote length is either 1 for normal quoted strings, and 3 for triple-quoted string.
404411func (p * StatementParser ) skipQuoted (sql []byte , pos int , quote byte ) (int , int , error ) {
412+ isEscapeString := false
413+ if p .supportsEscapeStrings () && pos > 0 {
414+ // TODO: Also implement support for the standard_conforming_strings property in PostgreSQL.
415+ // See https://www.postgresql.org/docs/current/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS
416+ // Check if it is an escape-string. This enables the use of a backslash to start an escape sequence, even if
417+ // the dialect normally does not support that. Escape strings start with an e or E, e.g. "e'It\'s valid'".
418+ // The second part of the check is to verify that the e or E is not part of a keyword, e.g. WHERE.
419+ // The following is valid SQL, but does not designate an escape-string:
420+ // SELECT * FROM my_table WHERE'test'=col1;
421+ isEscapeString = (sql [pos - 1 ] == 'e' || sql [pos - 1 ] == 'E' ) && (pos == 1 || ! isLatinLetter (sql [pos - 2 ]))
422+ }
405423 isTripleQuoted := p .supportsTripleQuotedLiterals () && len (sql ) > pos + 2 && sql [pos + 1 ] == quote && sql [pos + 2 ] == quote
406424 if isTripleQuoted && (isMultibyte (sql [pos + 1 ]) || isMultibyte (sql [pos + 2 ])) {
407425 isTripleQuoted = false
@@ -434,7 +452,7 @@ func (p *StatementParser) skipQuoted(sql []byte, pos int, quote byte) (int, int,
434452 // This was the end quote.
435453 return pos + 1 , quoteLength , nil
436454 }
437- } else if p .supportsBackslashEscape () && len (sql ) > pos + 1 && c == '\\' && sql [pos + 1 ] == quote {
455+ } else if ( p .supportsBackslashEscape () || isEscapeString ) && len (sql ) > pos + 1 && c == '\\' && sql [pos + 1 ] == quote {
438456 // This is an escaped quote (e.g. 'foo\'bar').
439457 // Note that in raw strings, the \ officially does not start an
440458 // escape sequence, but the result is still the same, as in a raw
0 commit comments