@@ -1254,18 +1254,46 @@ export class Deparser implements DeparserVisitor {
12541254 } else if ( nodeAny . sval !== undefined ) {
12551255 if ( typeof nodeAny . sval === 'object' && nodeAny . sval !== null ) {
12561256 if ( nodeAny . sval . sval !== undefined ) {
1257- return QuoteUtils . escape ( nodeAny . sval . sval ) ;
1257+ const value = nodeAny . sval . sval ;
1258+ const needsEscape = this . needsEscapePrefix ( value ) ;
1259+ if ( needsEscape ) {
1260+ const escapedValue = value . replace ( / \\ / g, '\\\\' ) . replace ( / ' / g, "''" ) ;
1261+ return `E'${ escapedValue } '` ;
1262+ } else {
1263+ return QuoteUtils . escape ( value ) ;
1264+ }
12581265 } else if ( nodeAny . sval . String && nodeAny . sval . String . sval !== undefined ) {
1259- return QuoteUtils . escape ( nodeAny . sval . String . sval ) ;
1266+ const value = nodeAny . sval . String . sval ;
1267+ const needsEscape = this . needsEscapePrefix ( value ) ;
1268+ if ( needsEscape ) {
1269+ const escapedValue = value . replace ( / \\ / g, '\\\\' ) . replace ( / ' / g, "''" ) ;
1270+ return `E'${ escapedValue } '` ;
1271+ } else {
1272+ return QuoteUtils . escape ( value ) ;
1273+ }
12601274 } else if ( Object . keys ( nodeAny . sval ) . length === 0 ) {
12611275 return "''" ;
12621276 } else {
1263- return QuoteUtils . escape ( nodeAny . sval . toString ( ) ) ;
1277+ const value = nodeAny . sval . toString ( ) ;
1278+ const needsEscape = this . needsEscapePrefix ( value ) ;
1279+ if ( needsEscape ) {
1280+ const escapedValue = value . replace ( / \\ / g, '\\\\' ) . replace ( / ' / g, "''" ) ;
1281+ return `E'${ escapedValue } '` ;
1282+ } else {
1283+ return QuoteUtils . escape ( value ) ;
1284+ }
12641285 }
12651286 } else if ( nodeAny . sval === null ) {
12661287 return 'NULL' ;
12671288 } else {
1268- return QuoteUtils . escape ( nodeAny . sval ) ;
1289+ const value = nodeAny . sval ;
1290+ const needsEscape = this . needsEscapePrefix ( value ) ;
1291+ if ( needsEscape ) {
1292+ const escapedValue = value . replace ( / \\ / g, '\\\\' ) . replace ( / ' / g, "''" ) ;
1293+ return `E'${ escapedValue } '` ;
1294+ } else {
1295+ return QuoteUtils . escape ( value ) ;
1296+ }
12691297 }
12701298 } else if ( nodeAny . boolval !== undefined ) {
12711299 if ( typeof nodeAny . boolval === 'object' && nodeAny . boolval !== null ) {
@@ -2014,9 +2042,34 @@ export class Deparser implements DeparserVisitor {
20142042 return caseMap [ defName . toLowerCase ( ) ] || defName ;
20152043 }
20162044
2045+ needsEscapePrefix ( value : string ) : boolean {
2046+ // Don't add E-prefix to hexadecimal bytea literals (e.g., \xDeAdBeEf)
2047+ if ( / ^ \\ x [ 0 - 9 a - f A - F ] + $ / i. test ( value ) ) {
2048+ return false ;
2049+ }
2050+
2051+ // Don't add E-prefix to strings that look like bytea literals with mixed content
2052+ if ( / \\ x [ 0 - 9 a - f A - F ] / . test ( value ) && ! / \\ [ n r t b f \\ ' ] / . test ( value ) ) {
2053+ return false ;
2054+ }
2055+
2056+ // Check for common backslash escape sequences that require E-prefix
2057+ return / \\ [ n r t b f \\ ' ] / . test ( value ) || // Basic escapes: \n, \t, \r, \b, \f, \\, \'
2058+ / \\ u [ 0 - 9 a - f A - F ] { 4 } / . test ( value ) || // Unicode escapes: \u0041
2059+ / \\ U [ 0 - 9 a - f A - F ] { 8 } / . test ( value ) || // Extended unicode escapes: \U00000041
2060+ / \\ [ 0 - 7 ] { 1 , 3 } / . test ( value ) ; // Octal escapes: \123
2061+ }
2062+
20172063 String ( node : t . String , context : DeparserContext ) : string {
20182064 if ( context . isStringLiteral || context . isEnumValue ) {
2019- return `'${ node . sval || '' } '` ;
2065+ const value = node . sval || '' ;
2066+ const needsEscape = this . needsEscapePrefix ( value ) ;
2067+ if ( needsEscape ) {
2068+ const escapedValue = value . replace ( / \\ / g, '\\\\' ) . replace ( / ' / g, "''" ) ;
2069+ return `E'${ escapedValue } '` ;
2070+ } else {
2071+ return QuoteUtils . escape ( value ) ;
2072+ }
20202073 }
20212074
20222075 const value = node . sval || '' ;
0 commit comments