Bug #64731 StringUtils.getBytesWrapped throws StringIndexOutOfBoundsException
Submitted: 22 Mar 2012 7:01 Modified: 20 Jun 2012 21:46
Reporter: zhang jinyan Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.1.18 OS:Any
Assigned to: Alexander Soklakov CPU Architecture:Any
Tags: getBytesWrapped, StringIndexOutOfBoundsException, StringUtils

[22 Mar 2012 7:01] zhang jinyan
Description:
When connection encoding is "gbk", server encoding is "latin1"。
Use PreparedStatement.setString(1, "0f0f0702"),exception will be thrown:

Exception in thread "main" 
java.lang.StringIndexOutOfBoundsException: String index out of range: 8
  at java.lang.String.charAt(String.java:686)
  at com.mysql.jdbc.StringUtils.escapeEasternUnicodeByteStream(StringUtils.java:231)
  at com.mysql.jdbc.StringUtils.getBytesWrapped(StringUtils.java:575)
  

How to repeat:
Test case:
byte[] data = StringUtils.getBytesWrapped("0f0f0702", '\'', '\'',null,
  "gbk", "latin1", false, null);
System.out.println(Arrays.toString(data));

Suggested fix:
mysql-connector-java-5.1.18
StringUtils.java(line 595~609):
  StringBuffer buf = new StringBuffer(s.length() + 2);
  buf.append(beginWrap);
  buf.append(s);
  buf.append(endWrap);
  b = buf.toString().getBytes(encoding);
  if (!parserKnowsUnicode && (encoding.equalsIgnoreCase("SJIS") //$NON-NLS-1$
      || encoding.equalsIgnoreCase("BIG5") //$NON-NLS-1$
      || encoding.equalsIgnoreCase("GBK"))) { //$NON-NLS-1$

    if (!encoding.equalsIgnoreCase(serverEncoding)) {
      b = escapeEasternUnicodeByteStream(b, s, 0, s.length());
    }
  }

When invoke escapeEasternUnicodeByteStream(b, s, 0, s.length());
variable b is not the corresponding byte[] for string variable s。
Fix code:
  b = s.getBytes(encoding);
  if (!parserKnowsUnicode && (encoding.equalsIgnoreCase("SJIS") //$NON-NLS-1$
      || encoding.equalsIgnoreCase("BIG5") //$NON-NLS-1$
      || encoding.equalsIgnoreCase("GBK"))) { //$NON-NLS-1$

      if (!encoding.equalsIgnoreCase(serverEncoding)) {
          b = StringUtils.escapeEasternUnicodeByteStream(b, s, 0,
              s.length());
      }
  }
  StringBuffer buf = new StringBuffer(s.length() + 2);
  buf.append(beginWrap);
  buf.append(s);
  buf.append(endWrap);

  b = buf.toString().getBytes(encoding);
[22 Mar 2012 16:18] zhang jinyan
Fix code should be:
  StringBuffer buf = new StringBuffer(s.length() + 2);
  buf.append(beginWrap);
  buf.append(s);
  buf.append(endWrap);

  s = buf.toString();// fix code
  b = s.getBytes(encoding);// fix code

  if (!parserKnowsUnicode && (encoding.equalsIgnoreCase("SJIS") //$NON-NLS-1$
    || encoding.equalsIgnoreCase("BIG5") //$NON-NLS-1$
    || encoding.equalsIgnoreCase("GBK"))) { //$NON-NLS-1$
      if (!encoding.equalsIgnoreCase(serverEncoding)) {
          b = escapeEasternUnicodeByteStream(b, s, 0, s.length());
      }
  }
[10 Apr 2012 8:22] Alexander Soklakov
Verified as described.
[20 Jun 2012 21:46] John Russell
Added to changelog for 5.1.21: 

With a connection encoding of gbk and a server encoding of latin1, a
call such as PreparedStatement.setString(1, "0f0f0702") could thrown
an exception java.lang.StringIndexOutOfBoundsException.