package example; import java.io.*; import java.sql.*; import java.util.*; /* * Test inserting and extracting Unicode-encoded strings. * * Synopsis: * example.Unicode * where must specify an existing database to which and * give access and which has UNICODE as its encoding. * (To create a database with UNICODE encoding, you need to compile * postgres with "--enable-multibyte" and run createdb with the * flag "-E UNICODE".) * * This test only produces output on error. * * @author William Webber */ public class Unicode { /* * The url for the database to connect to. */ private String url; /* * The user to connect as. */ private String user; /* * The password to connect with. */ private String password; private static void usage() { log("usage: example.Unicode "); } private static void log(String message) { System.err.println(message); } private static void log(String message, Exception e) { System.err.println(message); e.printStackTrace(); } public Unicode(String url, String user, String password) { this.url = url; this.user = user; this.password = password; } /* * Establish and return a connection to the database. */ private Connection getConnection() throws SQLException, ClassNotFoundException { Class.forName("org.postgresql.Driver"); Properties info = new Properties(); info.put("user", user); info.put("password", password); info.put("charSet", "utf-8"); return DriverManager.getConnection(url, info); } /* * Get string representing a block of 256 consecutive unicode characters. * We exclude the null character, "'", and "\". */ private String getSqlSafeUnicodeBlock(int blockNum) { if (blockNum < 0 || blockNum > 255) throw new IllegalArgumentException("blockNum must be from 0 to " + "255: " + blockNum); StringBuffer sb = new StringBuffer(256); int blockFirst = blockNum * 256; int blockLast = blockFirst + 256; for (int i = blockFirst; i < blockLast; i++) { char c = (char) i; if (c == '\0' || c == '\'' || c == '\\') continue; sb.append(c); } return sb.toString(); } /* * Is the block a block of valid unicode values. * d800 to db7f is the "unassigned high surrogate" range. * db80 to dbff is the "private use" range. * These should not be used in actual Unicode strings; * at least, jdk1.2 will not convert them to utf-8. */ private boolean isValidUnicodeBlock(int blockNum) { if (blockNum >= 0xd8 && blockNum <= 0xdb) return false; else return true; } /* * Report incorrect block retrieval. */ private void reportRetrievalError(int blockNum, String block, String retrieved) { String message = "Block " + blockNum + " returned incorrectly: "; int i = 0; for (i = 0; i < block.length(); i++) { if (i >= retrieved.length()) { message += "too short"; break; } else if (retrieved.charAt(i) != block.charAt(i)) { message += "first changed character at position " + i + ", sent as 0x" + Integer.toHexString((int) block.charAt(i)) + ", retrieved as 0x" + Integer.toHexString ((int) retrieved.charAt(i)); break; } } if (i >= block.length()) message += "too long"; log(message); } /* * Do the testing. */ public void runTest() { Connection connection = null; Statement statement = null; int blockNum = 0; final int CREATE = 0; final int INSERT = 1; final int SELECT = 2; final int LIKE = 3; int mode = CREATE; try { connection = getConnection(); statement = connection.createStatement(); statement.executeUpdate("CREATE TABLE test_unicode " + "( blockNum INT PRIMARY KEY, " + "block TEXT );"); mode = INSERT; for (blockNum = 0; blockNum < 256; blockNum++) { if (isValidUnicodeBlock(blockNum)) { String block = getSqlSafeUnicodeBlock(blockNum); statement.executeUpdate ("INSERT INTO test_unicode VALUES ( " + blockNum + ", '" + block + "');"); } } mode = SELECT; for (blockNum = 0; blockNum < 256; blockNum++) { if (isValidUnicodeBlock(blockNum)) { String block = getSqlSafeUnicodeBlock(blockNum); ResultSet rs = statement.executeQuery ("SELECT block FROM test_unicode WHERE blockNum = " + blockNum + ";"); if (!rs.next()) log("Could not retrieve block " + blockNum); else { String retrieved = rs.getString(1); if (!retrieved.equals(block)) { reportRetrievalError(blockNum, block, retrieved); } } } } mode = LIKE; for (blockNum = 0; blockNum < 256; blockNum++) { if (isValidUnicodeBlock(blockNum)) { String block = getSqlSafeUnicodeBlock(blockNum); String likeString = "%" + block.substring(2, block.length() - 3) + "%" ; ResultSet rs = statement.executeQuery ("SELECT blockNum FROM test_unicode WHERE block LIKE '" + likeString + "';"); if (!rs.next()) log("Could get block " + blockNum + " using LIKE"); } } } catch (SQLException sqle) { switch (mode) { case CREATE: log("Exception creating database", sqle); break; case INSERT: log("Exception inserting block " + blockNum, sqle); break; case SELECT: log("Exception selecting block " + blockNum, sqle); break; case LIKE: log("Exception doing LIKE on block " + blockNum, sqle); break; default: log("Exception", sqle); break; } } catch (ClassNotFoundException cnfe) { log("Unable to load driver", cnfe); return ; } try { if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (SQLException sqle) { log("Exception closing connections", sqle); } if (mode > CREATE) { // If the backend gets what it regards as garbage on a connection, // that connection may become unusable. To be safe, we create // a fresh connection to delete the table. try { connection = getConnection(); statement = connection.createStatement(); statement.executeUpdate("DROP TABLE test_unicode;"); } catch (Exception sqle) { log("*** ERROR: unable to delete test table " + "test_unicode; must be deleted manually", sqle); } } } public static void main(String [] args) { if (args.length != 3) { usage(); System.exit(1); } new Unicode(args[0], args[1], args[2]).runTest(); } }