From: brian z. <bz...@us...> - 2001-12-21 03:44:58
|
Update of /cvsroot/jython/jython/com/ziclix/python/sql In directory usw-pr-cvs1:/tmp/cvs-serv27534/ziclix/python/sql Modified Files: DataHandler.java Fetch.java Procedure.java Log Message: stored procedures/functions now return results Index: DataHandler.java =================================================================== RCS file: /cvsroot/jython/jython/com/ziclix/python/sql/DataHandler.java,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** DataHandler.java 2001/12/07 02:56:39 1.3 --- DataHandler.java 2001/12/21 03:44:55 1.4 *************** *** 282,285 **** --- 282,387 ---- /** + * Given a CallableStatement, column and type, return the appropriate + * Jython object. + * + * @param stmt the CallableStatement + * @param col the column number (adjusted properly for JDBC) + * @param type the column type + * @throws SQLException if the type is unmappable + */ + public PyObject getPyObject(CallableStatement stmt, int col, int type) throws SQLException { + + PyObject obj = Py.None; + + switch (type) { + + case Types.CHAR : + case Types.VARCHAR : + case Types.LONGVARCHAR : + String string = stmt.getString(col); + + obj = (string == null) ? Py.None : Py.newString(string); + break; + + case Types.NUMERIC : + case Types.DECIMAL : + BigDecimal bd = stmt.getBigDecimal(col, 10); + + obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue()); + break; + + case Types.BIT : + obj = stmt.getBoolean(col) ? Py.One : Py.Zero; + break; + + case Types.INTEGER : + case Types.TINYINT : + case Types.SMALLINT : + obj = Py.newInteger(stmt.getInt(col)); + break; + + case Types.BIGINT : + obj = new PyLong(stmt.getLong(col)); + break; + + case Types.FLOAT : + case Types.REAL : + case Types.DOUBLE : + obj = Py.newFloat(stmt.getFloat(col)); + break; + + case Types.TIME : + obj = Py.java2py(stmt.getTime(col)); + break; + + case Types.TIMESTAMP : + obj = Py.java2py(stmt.getTimestamp(col)); + break; + + case Types.DATE : + obj = Py.java2py(stmt.getDate(col)); + break; + + case Types.NULL : + obj = Py.None; + break; + + case Types.OTHER : + obj = Py.java2py(stmt.getObject(col)); + break; + + case Types.BINARY : + case Types.VARBINARY : + case Types.LONGVARBINARY : + obj = Py.java2py(stmt.getBytes(col)); + break; + + default : + Integer[] vals = { new Integer(col), new Integer(type) }; + String msg = zxJDBC.getString("errorGettingIndex", vals); + + throw new SQLException(msg); + } + + return (stmt.wasNull() || (obj == null)) ? Py.None : obj; + } + + /** + * Called when a stored procedure or function is executed and OUT parameters + * need to be registered with the statement. + * + * @param CallableStatement statement + * @param int index the JDBC offset column number + * @param int colType the column as from DatabaseMetaData (eg, procedureColumnOut) + * @param int dataType the JDBC datatype from Types + * + * @throws SQLException + * + */ + public void registerOut(CallableStatement statement, int index, int colType, int dataType) throws SQLException { + statement.registerOutParameter(index, dataType); + } + + /** * Handles checking if the object is null or None and setting it on the statement. * Index: Fetch.java =================================================================== RCS file: /cvsroot/jython/jython/com/ziclix/python/sql/Fetch.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Fetch.java 2001/12/17 03:44:42 1.2 --- Fetch.java 2001/12/21 03:44:55 1.3 *************** *** 15,33 **** /** ! * <p>The responsibility of a Fetch instance is to manage the iteration of a ResultSet. Two different ! * alogorithms are available: static or dynamic.</p> * ! * <p><b>Static</b> The static variety iterates the entire set immediately, creating the necessary Jython ! * objects and storing them. It is able to immediately close the ResultSet so a call to close() is ! * essentially a no-op from a database resource perspective (it does clear the results list however). ! * This approach also allows for the correct rowcount to be determined since the entire result set ! * has been iterated.</p> * ! * <p><b>Dynamic</b> The dynamic variety iterates the result set only as requested. This holds a bit truer to ! * the intent of the API as the fetch*() methods actually fetch when instructed. This is especially ! * useful for managing exeedingly large results, but is unable to determine the rowcount without having ! * worked through the entire result set. The other disadvantage is the ResultSet remains open throughout ! * the entire iteration. So the tradeoff is in open database resources versus JVM resources since the ! * application can keep constant space if it doesn't require the entire result set be presented as one.</p> * * @author brian zimmer --- 15,36 ---- /** ! * <p>The responsibility of a Fetch instance is to manage the iteration of a ! * ResultSet. Two different alogorithms are available: static or dynamic.</p> * ! * <p><b>Static</b> The static variety iterates the entire set immediately, ! * creating the necessary Jython objects and storing them. It is able to ! * immediately close the ResultSet so a call to close() is essentially a no-op ! * from a database resource perspective (it does clear the results list however). ! * This approach also allows for the correct rowcount to be determined since ! * the entire result set has been iterated.</p> * ! * <p><b>Dynamic</b> The dynamic variety iterates the result set only as requested. ! * This holds a bit truer to the intent of the API as the fetch*() methods actually ! * fetch when instructed. This is especially useful for managing exeedingly large ! * results, but is unable to determine the rowcount without having worked through ! * the entire result set. The other disadvantage is the ResultSet remains open ! * throughout the entire iteration. So the tradeoff is in open database resources ! * versus JVM resources since the application can keep constant space if it doesn't ! * require the entire result set be presented as one.</p> * * @author brian zimmer *************** *** 45,48 **** --- 48,57 ---- protected PyObject description; + /** True if a CallableStatement was added, false otherwise. */ + protected boolean callable; + + /** Field callableResults */ + protected PyObject callableResults; + /** * Constructor Fetch *************** *** 56,59 **** --- 65,70 ---- this.description = Py.None; this.rowcount = -1; + this.callable = false; + this.callableResults = Py.None; } *************** *** 110,113 **** --- 121,163 ---- */ public void add(CallableStatement callableStatement, Procedure procedure) { + + // set this regardless of whether the statement has results + this.callable = true; + + try { + createDescription(procedure); + + if (description.__len__() == 0) { + return; + } + + PyObject[] row = new PyObject[description.__len__()]; + + for (int i = 0, j = 0, len = procedure.columns.__len__(); i < len; i++) { + PyObject column = procedure.columns.__getitem__(i); + int colType = column.__getitem__(Procedure.COLUMN_TYPE).__int__().getValue(); + int dataType = column.__getitem__(Procedure.DATA_TYPE).__int__().getValue(); + + switch (colType) { + + case DatabaseMetaData.procedureColumnOut : + case DatabaseMetaData.procedureColumnInOut : + case DatabaseMetaData.procedureColumnReturn : + row[j++] = cursor.getDataHandler().getPyObject(callableStatement, i + 1, dataType); + break; + } + } + + this.callableResults = new PyList(); + + ((PyList)this.callableResults).append(new PyTuple(row)); + + this.rowcount = this.callableResults.__len__(); + } catch (PyException e) { + throw e; + } catch (Exception e) { + throw zxJDBC.newError(e); + } + return; } *************** *** 144,148 **** */ public final PyObject fetchall() { ! return doFetchall(); } --- 194,207 ---- */ public final PyObject fetchall() { ! ! if (callable) { ! PyObject tmp = this.callableResults; ! ! this.callableResults = Py.None; ! ! return tmp; ! } else { ! return doFetchall(); ! } } *************** *** 177,181 **** */ public final PyObject fetchmany(int size) { ! return doFetchmany(size); } --- 236,249 ---- */ public final PyObject fetchmany(int size) { ! ! if (callable) { ! PyObject tmp = this.callableResults; ! ! this.callableResults = Py.None; ! ! return tmp; ! } else { ! return doFetchmany(size); ! } } *************** *** 196,200 **** */ public final PyObject nextset() { ! return doNextset(); } --- 264,270 ---- */ public final PyObject nextset() { ! ! // no support for result sets within callable statements ! return callable ? Py.None : doNextset(); } *************** *** 210,216 **** * Cleanup any resources. */ ! abstract public void close() throws SQLException; /** * Builds a tuple containing the meta-information about each column. * --- 280,299 ---- * Cleanup any resources. */ ! public final void close() throws SQLException { + doClose(); + + this.callableResults = Py.None; + } + /** + * Method doClose + * + * @throws SQLException + * + */ + abstract public void doClose() throws SQLException; + + /** * Builds a tuple containing the meta-information about each column. * *************** *** 226,232 **** PyObject[] a = new PyObject[7]; ! a[0] = new PyString(meta.getColumnName(i)); ! a[1] = new PyInteger(meta.getColumnType(i)); ! a[2] = new PyInteger(meta.getColumnDisplaySize(i)); a[3] = Py.None; --- 309,315 ---- PyObject[] a = new PyObject[7]; ! a[0] = Py.newString(meta.getColumnName(i)); ! a[1] = Py.newInteger(meta.getColumnType(i)); ! a[2] = Py.newInteger(meta.getColumnDisplaySize(i)); a[3] = Py.None; *************** *** 240,245 **** case Types.INTEGER : case Types.SMALLINT : ! a[4] = new PyInteger(meta.getPrecision(i)); ! a[5] = new PyInteger(meta.getScale(i)); break; --- 323,328 ---- case Types.INTEGER : case Types.SMALLINT : ! a[4] = Py.newInteger(meta.getPrecision(i)); ! a[5] = Py.newInteger(meta.getScale(i)); break; *************** *** 250,254 **** } ! a[6] = new PyInteger(meta.isNullable(i)); ((PyList)this.description).append(new PyTuple(a)); --- 333,337 ---- } ! a[6] = Py.newInteger(meta.isNullable(i)); ((PyList)this.description).append(new PyTuple(a)); *************** *** 257,260 **** --- 340,399 ---- /** + * Builds a tuple containing the meta-information about each column. + * + * (name, type_code, display_size, internal_size, precision, scale, null_ok) + * + * precision and scale are only available for numeric types + */ + protected void createDescription(Procedure procedure) throws SQLException { + + this.description = new PyList(); + + for (int i = 0, len = procedure.columns.__len__(); i < len; i++) { + PyObject column = procedure.columns.__getitem__(i); + int colType = column.__getitem__(Procedure.COLUMN_TYPE).__int__().getValue(); + + switch (colType) { + + case DatabaseMetaData.procedureColumnOut : + case DatabaseMetaData.procedureColumnInOut : + case DatabaseMetaData.procedureColumnReturn : + PyObject[] a = new PyObject[7]; + + a[0] = column.__getitem__(Procedure.NAME); + a[1] = column.__getitem__(Procedure.DATA_TYPE); + a[2] = Py.newInteger(-1); + a[3] = column.__getitem__(Procedure.LENGTH); + + switch (a[1].__int__().getValue()) { + + case Types.BIGINT : + case Types.BIT : + case Types.DECIMAL : + case Types.DOUBLE : + case Types.FLOAT : + case Types.INTEGER : + case Types.SMALLINT : + a[4] = column.__getitem__(Procedure.PRECISION); + a[5] = column.__getitem__(Procedure.SCALE); + break; + + default : + a[4] = Py.None; + a[5] = Py.None; + break; + } + + int nullable = column.__getitem__(Procedure.NULLABLE).__int__().getValue(); + + a[6] = (nullable == DatabaseMetaData.procedureNullable) ? Py.One : Py.Zero; + + ((PyList)this.description).append(new PyTuple(a)); + break; + } + } + } + + /** * Creates the results of a query. Iterates through the list and builds the tuple. * *************** *** 447,451 **** counter += size; ! res = current.__getslice__(new PyInteger(start), new PyInteger(counter + 1), new PyInteger(1)); } --- 586,590 ---- counter += size; ! res = current.__getslice__(Py.newInteger(start), Py.newInteger(counter + 1), Py.newInteger(1)); } *************** *** 476,480 **** * Remove the results. */ ! public void close() throws SQLException { this.counter = -1; --- 615,619 ---- * Remove the results. */ ! public void doClose() throws SQLException { this.counter = -1; *************** *** 539,542 **** --- 678,683 ---- this.skipCols = skipCols; } + } catch (PyException e) { + throw e; } catch (Exception e) { throw zxJDBC.newError(e); *************** *** 583,586 **** --- 724,729 ---- this.rowcount = (this.rowcount == -1) ? 1 : this.rowcount + 1; } + } catch (PyException e) { + throw e; } catch (Exception e) { throw zxJDBC.newError(e); *************** *** 600,604 **** * Close the underlying ResultSet. */ ! public void close() throws SQLException { if (this.resultSet == null) { --- 743,747 ---- * Close the underlying ResultSet. */ ! public void doClose() throws SQLException { if (this.resultSet == null) { Index: Procedure.java =================================================================== RCS file: /cvsroot/jython/jython/com/ziclix/python/sql/Procedure.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Procedure.java 2001/12/17 03:44:42 1.1 --- Procedure.java 2001/12/21 03:44:55 1.2 *************** *** 25,28 **** --- 25,31 ---- public class Procedure extends Object { + /** Field NAME */ + protected static final int NAME = 3; + /** Field COLUMN_TYPE */ protected static final int COLUMN_TYPE = 4; *************** *** 31,34 **** --- 34,49 ---- protected static final int DATA_TYPE = 5; + /** Field PRECISION */ + protected static final int PRECISION = 7; + + /** Field LENGTH */ + protected static final int LENGTH = 8; + + /** Field SCALE */ + protected static final int SCALE = 9; + + /** Field NULLABLE */ + protected static final int NULLABLE = 11; + /** Field PLACEHOLDER */ public static final PyObject PLACEHOLDER = new PyObject(); *************** *** 106,117 **** public PyObject normalizeParams(PyObject params) { - if (columns == Py.None) { - throw zxJDBC.makeException(zxJDBC.ProgrammingError, "too many params for input"); - } - if (params == Py.None) { return Py.None; } int j = 0, plen = params.__len__(); PyList population = new PyList(); --- 121,132 ---- public PyObject normalizeParams(PyObject params) { if (params == Py.None) { return Py.None; } + if (columns == Py.None) { + throw zxJDBC.makeException(zxJDBC.ProgrammingError, "too many params for input"); + } + int j = 0, plen = params.__len__(); PyList population = new PyList(); *************** *** 226,230 **** * */ ! private final void registerOutParameters(CallableStatement statement) throws SQLException { if (columns == Py.None) { --- 241,245 ---- * */ ! protected void registerOutParameters(CallableStatement statement) throws SQLException { if (columns == Py.None) { *************** *** 239,265 **** switch (colType) { - case DatabaseMetaData.procedureColumnIn : case DatabaseMetaData.procedureColumnInOut : case DatabaseMetaData.procedureColumnOut : case DatabaseMetaData.procedureColumnReturn : ! doRegister(statement, i + 1, colType, dataType); break; } } - } - - /** - * Method doRegister - * - * @param CallableStatement statement - * @param int index - * @param int colType - * @param int dataType - * - * @throws SQLException - * - */ - protected void doRegister(CallableStatement statement, int index, int colType, int dataType) throws SQLException { - statement.registerOutParameter(index, dataType); } --- 254,264 ---- switch (colType) { case DatabaseMetaData.procedureColumnInOut : case DatabaseMetaData.procedureColumnOut : case DatabaseMetaData.procedureColumnReturn : ! cursor.datahandler.registerOut(statement, i + 1, colType, dataType); break; } } } |