### Eclipse Workspace Patch 1.0 #P jython-trunk Index: src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java =================================================================== --- src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java (revision 6980) +++ src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java (working copy) @@ -38,6 +38,7 @@ super(datahandler); } + @Override protected String getRowIdMethodName() { return "getLastOID"; } @@ -51,6 +52,7 @@ * @return the mapped Python object * @throws SQLException thrown for a sql exception */ + @Override public PyObject getPyObject(ResultSet set, int col, int type) throws SQLException { PyObject obj = Py.None; @@ -90,6 +92,7 @@ * @param type * @throws SQLException */ + @Override public void setJDBCObject(PreparedStatement stmt, int index, PyObject object, int type) throws SQLException { if (DataHandler.checkNull(stmt, index, object, type)) { @@ -115,6 +118,8 @@ super.setJDBCObject(stmt, index, object, type); } } + + @Override public void setJDBCObject(PreparedStatement stmt, int index, PyObject object) throws SQLException { // PostgreSQL doesn't support BigIntegers without explicitely setting the // type. Index: tests/java/com/ziclix/python/sql/DataHandlerTest.java =================================================================== --- tests/java/com/ziclix/python/sql/DataHandlerTest.java (revision 0) +++ tests/java/com/ziclix/python/sql/DataHandlerTest.java (revision 0) @@ -0,0 +1,95 @@ +package com.ziclix.python.sql; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Arrays; +import java.util.List; + +import org.python.core.PySystemState; + +import junit.framework.TestCase; + +public class DataHandlerTest extends TestCase { + + private DataHandler _handler; + + @Override + protected void setUp() throws Exception { + PySystemState.initialize(); + _handler = new DataHandler(); + } + + /** + * make sure we handle every {@link java.sql.Types} somehow + * + * @throws Exception + */ + public void testGetPyObjectResultSetIntInt() throws Exception { + ResultSet rs = (ResultSet)Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] {ResultSet.class}, + new DefaultReturnHandler()); + List unsupportedTypes = Arrays.asList("ARRAY", + "DATALINK", + "DISTINCT", + "REF", + "STRUCT"); + for (Field field : Types.class.getDeclaredFields()) { + String typeName = field.getName(); + int type = field.getInt(null); + if (unsupportedTypes.contains(typeName)) { + try { + _handler.getPyObject(rs, 1, type); + fail("SQLException expected"); + } catch (SQLException sqle) { + // expected + } + } else { + assertNotNull(typeName + " should return None", _handler.getPyObject(rs, 1, type)); + } + } + } + + /** + * This is a poor man's mock - i cannot introduce a mock framework at this point in time + */ + static class DefaultReturnHandler implements InvocationHandler { + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Class returnType = method.getReturnType(); + if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) { + return Boolean.FALSE; + } else if (Character.TYPE.equals(returnType)) { + return Character.valueOf('0'); + } else if (Byte.TYPE.equals(returnType)) { + return Byte.valueOf((byte)0); + } else if (Short.TYPE.equals(returnType)) { + return Short.valueOf((short)0); + } else if (Integer.TYPE.equals(returnType)) { + return Integer.valueOf(0); + } else if (Long.TYPE.equals(returnType)) { + return Long.valueOf(0L); + } else if (Float.TYPE.equals(returnType)) { + return Float.valueOf(0); + } else if (Double.TYPE.equals(returnType)) { + return Double.valueOf(0); + } else if (returnType.isPrimitive()) { + throw new RuntimeException("unhandled primitve type " + returnType); + } else if (returnType.isAssignableFrom(BigInteger.class)) { + return BigInteger.ZERO; + } else if (returnType.isAssignableFrom(BigDecimal.class)) { + return BigDecimal.ZERO; + } else if (returnType.isAssignableFrom(Number.class)) { + return 0; + } else { + return null; + } + } + } +} Index: src/com/ziclix/python/sql/handler/OracleDataHandler.java =================================================================== --- src/com/ziclix/python/sql/handler/OracleDataHandler.java (revision 6980) +++ src/com/ziclix/python/sql/handler/OracleDataHandler.java (working copy) @@ -8,26 +8,25 @@ */ package com.ziclix.python.sql.handler; -import com.ziclix.python.sql.DataHandler; -import com.ziclix.python.sql.FilterDataHandler; -import com.ziclix.python.sql.zxJDBC; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; + import oracle.jdbc.OracleResultSet; import oracle.jdbc.OracleTypes; import oracle.sql.BLOB; import oracle.sql.ROWID; + import org.python.core.Py; import org.python.core.PyInteger; import org.python.core.PyObject; -import java.io.BufferedInputStream; -import java.io.InputStream; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; +import com.ziclix.python.sql.DataHandler; +import com.ziclix.python.sql.FilterDataHandler; /** * Oracle specific data handling. @@ -96,13 +95,6 @@ super.setJDBCObject(stmt, index, object, Types.DOUBLE); break; - case Types.BLOB: - case Types.CLOB: - Integer[] vals = {new Integer(index), new Integer(type)}; - String msg = zxJDBC.getString("errorSettingIndex", vals); - - throw new SQLException(msg); - case OracleTypes.ROWID: stmt.setString(index, (String) object.__tojava__(String.class)); break; Index: src/com/ziclix/python/sql/resource/zxJDBCMessages.properties =================================================================== --- src/com/ziclix/python/sql/resource/zxJDBCMessages.properties (revision 6980) +++ src/com/ziclix/python/sql/resource/zxJDBCMessages.properties (working copy) @@ -94,8 +94,7 @@ noColInfo=unable to obtain column info excludedAllCols=excluded all columns invalidTableName=invalid table name [None] -errorSettingIndex=error setting index [{0}], type [{1}] -errorGettingIndex=error getting index [{0}], type [{1}] +unsupportedTypeForColumn=type [{0}] is not supported for column index: {1} maybeCallproc=use .callproc() for stored procedures nodynamiccursors=this version of jdbc does not support dynamic cursors nocallprocsupport=dynamic cursor does not support .callproc; use static cursors instead Index: src/com/ziclix/python/sql/DataHandler.java =================================================================== --- src/com/ziclix/python/sql/DataHandler.java (revision 6980) +++ src/com/ziclix/python/sql/DataHandler.java (working copy) @@ -8,11 +8,6 @@ */ package com.ziclix.python.sql; -import org.python.core.Py; -import org.python.core.PyFile; -import org.python.core.PyObject; -import org.python.core.PyList; - import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -20,9 +15,11 @@ import java.io.Reader; import java.io.StringReader; import java.lang.reflect.Constructor; +import java.math.BigDecimal; import java.math.BigInteger; -import java.math.BigDecimal; +import java.sql.Blob; import java.sql.CallableStatement; +import java.sql.Clob; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -32,6 +29,11 @@ import java.sql.Timestamp; import java.sql.Types; +import org.python.core.Py; +import org.python.core.PyFile; +import org.python.core.PyList; +import org.python.core.PyObject; + /** * The DataHandler is responsible mapping the JDBC data type to * a Jython object. Depending on the version of the JDBC @@ -250,6 +252,7 @@ break; case Types.BIT: + case Types.BOOLEAN: obj = set.getBoolean(col) ? Py.True : Py.False; break; @@ -291,6 +294,7 @@ break; case Types.OTHER: + case Types.JAVA_OBJECT: obj = Py.java2py(set.getObject(col)); break; @@ -300,16 +304,41 @@ obj = Py.java2py(set.getBytes(col)); break; + case Types.BLOB: + Blob blob = set.getBlob(col); + obj = blob == null ? Py.None : Py.java2py(read(blob.getBinaryStream())); + break; + + case Types.CLOB: + Clob clob = set.getClob(col); + obj = clob == null ? Py.None : Py.java2py(read(clob.getCharacterStream())); + break; + + // TODO can we support these? + case Types.ARRAY: + throw createUnsupportedTypeSQLException("ARRAY", col); + case Types.DATALINK: + throw createUnsupportedTypeSQLException("DATALINK", col); + case Types.DISTINCT: + throw createUnsupportedTypeSQLException("DISTINCT", col); + case Types.REF: + throw createUnsupportedTypeSQLException("REF", col); + case Types.STRUCT: + throw createUnsupportedTypeSQLException("STRUCT", col); + default : - Integer[] vals = {new Integer(col), new Integer(type)}; - String msg = zxJDBC.getString("errorGettingIndex", vals); - - throw new SQLException(msg); + throw createUnsupportedTypeSQLException(new Integer(type), col); } return set.wasNull() || obj == null ? Py.None : obj; } + protected final SQLException createUnsupportedTypeSQLException(Object type, int col) { + Object[] vals = {type, new Integer(col)}; + String msg = zxJDBC.getString("unsupportedTypeForColumn", vals); + return new SQLException(msg); + } + /** * Given a CallableStatement, column and type, return the appropriate * Jython object. @@ -387,10 +416,7 @@ break; default : - Integer[] vals = {new Integer(col), new Integer(type)}; - String msg = zxJDBC.getString("errorGettingIndex", vals); - - throw new SQLException(msg); + createUnsupportedTypeSQLException(type, col); } return stmt.wasNull() || obj == null ? Py.None : obj; Index: NEWS =================================================================== --- NEWS (revision 7123) +++ NEWS (working copy) @@ -1,5 +1,9 @@ Jython NEWS +Jython 2.5.2rc1 + Bugs Fixed + - [ 1647 ] zxJDBC does not handle NVARCHAR + Jython 2.5.2b2 Bugs Fixed - [ 1327 ] Classloaders cannot GC, which exhausts permgen (partial bug fix) Index: src/com/ziclix/python/sql/Jython22DataHandler.java =================================================================== --- src/com/ziclix/python/sql/Jython22DataHandler.java (revision 6980) +++ src/com/ziclix/python/sql/Jython22DataHandler.java (working copy) @@ -51,6 +51,7 @@ * most notably Oracle. This callback allows a DataHandler to affect the * name. */ + @Override public String getMetaDataName(PyObject name) { return ((name == Py.None) ? null : name.__str__().toString()); } @@ -63,6 +64,7 @@ * @return an instance of a Procedure * @throws SQLException */ + @Override public Procedure getProcedure(PyCursor cursor, PyObject name) throws SQLException { return new Procedure(cursor, name); } @@ -75,6 +77,7 @@ * @throws SQLException thrown if an exception occurs * */ + @Override public PyObject getRowId(Statement stmt) throws SQLException { return Py.None; } @@ -83,6 +86,7 @@ * A callback prior to each execution of the statement. If the statement is * a PreparedStatement, all the parameters will have been set. */ + @Override public void preExecute(Statement stmt) throws SQLException { return; } @@ -90,6 +94,7 @@ /** * A callback after successfully executing the statement. */ + @Override public void postExecute(Statement stmt) throws SQLException { return; } @@ -103,6 +108,7 @@ * @param object the PyObject in question * @throws SQLException */ + @Override public void setJDBCObject(PreparedStatement stmt, int index, PyObject object) throws SQLException { try { @@ -133,6 +139,7 @@ * @param type the java.sql.Types for which this PyObject should be bound * @throws SQLException */ + @Override public void setJDBCObject(PreparedStatement stmt, int index, PyObject object, int type) throws SQLException { try { @@ -209,6 +216,7 @@ * @param type the column type * @throws SQLException if the type is unmappable */ + @Override @SuppressWarnings("deprecation") public PyObject getPyObject(ResultSet set, int col, int type) throws SQLException { @@ -308,10 +316,7 @@ break; default : - Integer[] vals = {new Integer(col), new Integer(type)}; - String msg = zxJDBC.getString("errorGettingIndex", vals); - - throw new SQLException(msg); + throw createUnsupportedTypeSQLException(new Integer(type), col); } return (set.wasNull() || (obj == null)) ? Py.None : obj; @@ -326,6 +331,7 @@ * @param type the column type * @throws SQLException if the type is unmappable */ + @Override @SuppressWarnings("deprecation") public PyObject getPyObject(CallableStatement stmt, int col, int type) throws SQLException { @@ -398,10 +404,7 @@ break; default : - Integer[] vals = {new Integer(col), new Integer(type)}; - String msg = zxJDBC.getString("errorGettingIndex", vals); - - throw new SQLException(msg); + throw createUnsupportedTypeSQLException(new Integer(type), col); } return (stmt.wasNull() || (obj == null)) ? Py.None : obj; @@ -420,6 +423,7 @@ * @throws SQLException * */ + @Override public void registerOut(CallableStatement statement, int index, int colType, int dataType, String dataTypeName) throws SQLException { try { @@ -445,6 +449,7 @@ * * @return a list of datahandlers */ + @Override public PyObject __chain__() { return new PyList(Py.javas2pys(this)); }