/*------------------------------------------------------------------------- * * Copyright (c) 2003-2008, PostgreSQL Global Development Group * * IDENTIFICATION * $PostgreSQL: pgjdbc/org/postgresql/util/PGbytea.java,v 1.17 2010/04/06 23:45:09 jurka Exp $ * *------------------------------------------------------------------------- */ package org.teiid.transport.pg; import java.sql.SQLException; /** * Converts to and from the postgresql bytea datatype used by the backend. */ public class PGbytea { private static final int MAX_3_BUFF_SIZE = 2 * 1024 * 1024; /* * Converts a PG bytea raw value (i.e. the raw binary representation * of the bytea data type) into a java byte[] */ public static byte[] toBytes(byte[] s) throws SQLException { if (s == null) return null; // Starting with PG 9.0, a new hex format is supported // that starts with "\x". Figure out which format we're // dealing with here. // if (s.length < 2 || s[0] != '\\' || s[1] != 'x') { return toBytesOctalEscaped(s); } return toBytesHexEscaped(s); } private static byte[] toBytesHexEscaped(byte[] s) { byte[] output = new byte[(s.length - 2) / 2]; for (int i=0; i<output.length; i++) { byte b1 = gethex(s[2 + i*2]); byte b2 = gethex(s[2 + i*2 + 1]); output[i] = (byte)((b1 << 4) | b2); } return output; } private static byte gethex(byte b) { // 0-9 == 48-57 if (b <= 57) return (byte)(b - 48); // a-f == 97-102 if (b >= 97) return (byte)(b - 97 + 10); // A-F == 65-70 return (byte)(b - 65 + 10); } private static byte[] toBytesOctalEscaped(byte[] s) { final int slength = s.length; byte[] buf = null; int correctSize = slength; if (slength > MAX_3_BUFF_SIZE) { // count backslash escapes, they will be either // backslashes or an octal escape \\ or \003 // for (int i = 0; i < slength; ++i) { byte current = s[i]; if (current == '\\') { byte next = s[ ++i ]; if (next == '\\') { --correctSize; } else { correctSize -= 3; } } } buf = new byte[correctSize]; } else { buf = new byte[slength]; } int bufpos = 0; int thebyte; byte nextbyte; byte secondbyte; for (int i = 0; i < slength; i++) { nextbyte = s[i]; if (nextbyte == (byte)'\\') { secondbyte = s[++i]; if (secondbyte == (byte)'\\') { //escaped \ buf[bufpos++] = (byte)'\\'; } else { thebyte = (secondbyte - 48) * 64 + (s[++i] - 48) * 8 + (s[++i] - 48); if (thebyte > 127) thebyte -= 256; buf[bufpos++] = (byte)thebyte; } } else { buf[bufpos++] = nextbyte; } } if (bufpos == correctSize) { return buf; } byte[] l_return = new byte[bufpos]; System.arraycopy(buf, 0, l_return, 0, bufpos); return l_return; } /* * Converts a java byte[] into a PG bytea string (i.e. the text * representation of the bytea data type) */ public static String toPGString(byte[] p_buf) throws SQLException { if (p_buf == null) return null; StringBuffer l_strbuf = new StringBuffer(2 * p_buf.length); for (int i = 0; i < p_buf.length; i++) { int l_int = (int)p_buf[i]; if (l_int < 0) { l_int = 256 + l_int; } //we escape the same non-printable characters as the backend //we must escape all 8bit characters otherwise when convering //from java unicode to the db character set we may end up with //question marks if the character set is SQL_ASCII if (l_int < 040 || l_int > 0176) { //escape charcter with the form \000, but need two \\ because of //the Java parser l_strbuf.append("\\"); l_strbuf.append((char)(((l_int >> 6) & 0x3) + 48)); l_strbuf.append((char)(((l_int >> 3) & 0x7) + 48)); l_strbuf.append((char)((l_int & 0x07) + 48)); } else if (p_buf[i] == (byte)'\\') { //escape the backslash character as \\, but need four \\\\ because //of the Java parser l_strbuf.append("\\\\"); } else { //other characters are left alone l_strbuf.append((char)p_buf[i]); } } return l_strbuf.toString(); } }