SF.net SVN: jython:[7127] trunk/jython

[email protected] Tue, 28 Sep 2010 05:35:21 +0000
Newsgroups gmane.comp.lang.jython.cvs
Message-ID <[email protected]>
Revision: 7127
          http://jython.svn.sourceforge.net/jython/?rev=7127&view=rev
Author:   otmarhumbel
Date:     2010-09-28 05:35:21 +0000 (Tue, 28 Sep 2010)

Log Message:
-----------
handle more SQL types in zxJDBC DataHandler
(fixes issue #1647)
thanks to Stephen Layland for the first patch
thanks to Patrick Reinhart for discovering the missing types

Modified Paths:
--------------
    trunk/jython/NEWS
    trunk/jython/src/com/ziclix/python/sql/DataHandler.java
    trunk/jython/src/com/ziclix/python/sql/Jython22DataHandler.java
    trunk/jython/src/com/ziclix/python/sql/handler/OracleDataHandler.java
    trunk/jython/src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java
    trunk/jython/src/com/ziclix/python/sql/resource/zxJDBCMessages.properties

Added Paths:
-----------
    trunk/jython/tests/java/com/
    trunk/jython/tests/java/com/ziclix/
    trunk/jython/tests/java/com/ziclix/python/
    trunk/jython/tests/java/com/ziclix/python/sql/
    trunk/jython/tests/java/com/ziclix/python/sql/DataHandlerTest.java

Modified: trunk/jython/NEWS
===================================================================
--- trunk/jython/NEWS	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/NEWS	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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)

Modified: trunk/jython/src/com/ziclix/python/sql/DataHandler.java
===================================================================
--- trunk/jython/src/com/ziclix/python/sql/DataHandler.java	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/src/com/ziclix/python/sql/DataHandler.java	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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;

Modified: trunk/jython/src/com/ziclix/python/sql/Jython22DataHandler.java
===================================================================
--- trunk/jython/src/com/ziclix/python/sql/Jython22DataHandler.java	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/src/com/ziclix/python/sql/Jython22DataHandler.java	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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 <i>java.sql.Types</i> 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));
     }

Modified: trunk/jython/src/com/ziclix/python/sql/handler/OracleDataHandler.java
===================================================================
--- trunk/jython/src/com/ziclix/python/sql/handler/OracleDataHandler.java	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/src/com/ziclix/python/sql/handler/OracleDataHandler.java	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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;

Modified: trunk/jython/src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java
===================================================================
--- trunk/jython/src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/src/com/ziclix/python/sql/handler/PostgresqlDataHandler.java	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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.

Modified: trunk/jython/src/com/ziclix/python/sql/resource/zxJDBCMessages.properties
===================================================================
--- trunk/jython/src/com/ziclix/python/sql/resource/zxJDBCMessages.properties	2010-09-27 13:14:22 UTC (rev 7126)
+++ trunk/jython/src/com/ziclix/python/sql/resource/zxJDBCMessages.properties	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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

Added: trunk/jython/tests/java/com/ziclix/python/sql/DataHandlerTest.java
===================================================================
--- trunk/jython/tests/java/com/ziclix/python/sql/DataHandlerTest.java	                        (rev 0)
+++ trunk/jython/tests/java/com/ziclix/python/sql/DataHandlerTest.java	2010-09-28 05:35:21 UTC (rev 7127)
@@ -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<String> 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;
+            }
+        }
+    }
+}


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.

------------------------------------------------------------------------------
Start uncovering the many advantages of virtual appliances
and start using them to simplify application deployment and
accelerate your shift to cloud computing.
http://p.sf.net/sfu/novell-sfdev2dev