New Patch For CallableStmt (against current CVS) - Mailing list pgsql-patches
From | Paul Bethe |
---|---|
Subject | New Patch For CallableStmt (against current CVS) |
Date | |
Msg-id | 20020613161147.13027.qmail@web20301.mail.yahoo.com Whole thread Raw |
List | pgsql-patches |
A rework of the previously submitted patch for CallableStatements. Basically allow JDBC Syntax '{ [?=] call <some-func> ([? [,?]*]) [;] }' and convert to pgsql syntax 'select <function-call> as RESULT' it appears to the user the same as any other language (eg Oracle) in setting in args and registering/getting return value. -Paul Bethe done against jdbc2 (attached as tgz + inlined.) **** jdbc2/Connection.java Thu Jun 13 11:51:49 2002 --- Connection.java.old Thu Jun 13 11:50:51 2002 *************** *** 135,145 **** public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { ! //throw new PSQLException("postgresql.con.call"); ! CallableStatement s = new CallableStatement(this,sql); ! s.setResultSetType(resultSetType); ! s.setResultSetConcurrency(resultSetConcurrency); ! return s; } /* --- 135,145 ---- public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { ! throw new PSQLException("postgresql.con.call"); ! //CallableStatement s = new CallableStatement(this,sql); ! //s.setResultSetType(resultSetType); ! //s.setResultSetConcurrency(resultSetConcurrency); ! //return s; } /* ***** *** jdbc2/PreparedStatement.java Wed Jun 12 15:42:38 2002 --- PreparedStatement.java.old Wed Jun 12 15:18:10 2002 *************** *** 58,73 **** { super(connection); - this.sql = sql; - this.connection = connection; - parseSqlStmt (); // this allows Callable stmt to override - } - - protected void parseSqlStmt () throws SQLException { Vector v = new Vector(); boolean inQuotes = false; int lastParmEnd = 0, i; for (i = 0; i < sql.length(); ++i) { int c = sql.charAt(i); --- 58,70 ---- { super(connection); Vector v = new Vector(); boolean inQuotes = false; int lastParmEnd = 0, i; + this.sql = sql; + this.connection = connection; + for (i = 0; i < sql.length(); ++i) { int c = sql.charAt(i); *************** *** 120,128 **** * Helper - this compiles the SQL query from the various parameters * This is identical to toString() except it throws an exception if a * parameter is unused. - * ! protected to allow CallableStatment to override. */ ! protected synchronized String compileQuery() throws SQLException { sbuf.setLength(0); --- 117,124 ---- * Helper - this compiles the SQL query from the various parameters * This is identical to toString() except it throws an exception if a * parameter is unused. */ ! private synchronized String compileQuery() throws SQLException { sbuf.setLength(0); *************** *** 817,829 **** * There are a lot of setXXX classes which all basically do * the same thing. We need a method which actually does the * set for us. - * ! protected to allow CallableStatement access * * @param paramIndex the index into the inString * @param s a string to be stored * @exception SQLException if something goes wrong */ ! protected void set(int paramIndex, String s) throws SQLException { if (paramIndex < 1 || paramIndex > inStrings.length) throw new PSQLException("postgresql.prep.range"); --- 813,824 ---- * There are a lot of setXXX classes which all basically do * the same thing. We need a method which actually does the * set for us. * * @param paramIndex the index into the inString * @param s a string to be stored * @exception SQLException if something goes wrong */ ! private void set(int paramIndex, String s) throws SQLException { if (paramIndex < 1 || paramIndex > inStrings.length) throw new PSQLException("postgresql.prep.range"); ***** errors.properties Thu Jun 13 11:49:33 2002 --- errors.properties.old Thu Jun 13 11:49:30 2002 *************** *** 84,95 **** postgresql.updateable.inputstream:Input Stream is null. postgresql.updateable.ioerror:Input Stream Error. - # proposed changes for CallableStatements - postgresql.call.noreturntype:A CallableStatement Function was declared but no call to 'registerOutParameter (1, <some_type>)' was made. - postgresql.call.noinout:PostgreSQL only supports function return value [@ 1] (no OUT or INOUT arguments) - postgresql.call.procasfunc:This Statement [{0}] defines a procedure call (needs ?= call <stmt> to be considered a function. - postgresql.call.malformed:Malformed stmt [{0}] usage : {1} - postgresql.call.funcover:Cannot execute Query a call to setXXX (1, ..) was made where argument 1 is the return value of a function. - postgresql.call.wrongget:Parameter of type {0} was registered but call to get{1} (sqltype={2}) was made. - postgresql.call.noreturnval:A CallableStatement Function was executed with nothing returned. - postgresql.call.wrongrtntype:A CallableStatement Function was executed and the return was of type ({0}) however type={1} was registered. --- 84,86 ---- ***** JDBC2Tests.java Thu Jun 13 11:48:18 2002 --- JDBC2Tests.java.old Thu Jun 13 11:48:09 2002 *************** *** 226,234 **** // Fastpath/LargeObject suite.addTestSuite(BlobTest.class); ! suite.addTestSuite( UpdateableResultTest.class ); - suite.addTestSuite( CallableStmtTest.class ); // That's all folks return suite; } --- 226,233 ---- // Fastpath/LargeObject suite.addTestSuite(BlobTest.class); ! suite.addTestSuite( UpdateableResultTest.class ); // That's all folks return suite; } ** new file test/jdbc2/CallableStmtTest.java package org.postgresql.test.jdbc2; import org.postgresql.test.JDBC2Tests; import junit.framework.TestCase; import java.io.*; import java.sql.*; /* * CallableStatement tests. * @author Paul Bethe */ public class CallableStmtTest extends TestCase { private Connection con; public CallableStmtTest (String name) { super(name); } protected void setUp() throws Exception { con = JDBC2Tests.openDB(); Statement stmt = con.createStatement (); stmt.execute ("CREATE OR REPLACE FUNCTION testspg__getString (varchar) " + "RETURNS varchar AS ' DECLARE inString alias for $1; begin "+ "return ''bob''; end; ' LANGUAGE 'plpgsql';"); stmt.execute ("CREATE OR REPLACE FUNCTION testspg__getDouble (float) " + "RETURNS float AS ' DECLARE inString alias for $1; begin " + "return 42.42; end; ' LANGUAGE 'plpgsql';"); stmt.execute ("CREATE OR REPLACE FUNCTION testspg__getInt (int) RETURNS int " + " AS 'DECLARE inString alias for $1; begin " + "return 42; end;' LANGUAGE 'plpgsql';"); stmt.execute ("CREATE OR REPLACE FUNCTION testspg__getNumeric (numeric) " + "RETURNS numeric AS ' DECLARE inString alias for $1; " + "begin return 42; end; ' LANGUAGE 'plpgsql';"); stmt.close (); } protected void tearDown() throws Exception { Statement stmt = con.createStatement (); stmt.execute ("drop FUNCTION testspg__getString (varchar);"); stmt.execute ("drop FUNCTION testspg__getDouble (float);"); stmt.execute ("drop FUNCTION testspg__getInt (int);"); stmt.execute ("drop FUNCTION testspg__getNumeric (numeric);"); JDBC2Tests.closeDB(con); } final String func = "{ ? = call "; final String pkgName = "testspg__"; // protected void runTest () throws Throwable { //testGetString (); //} public void testGetDouble () throws Throwable { // System.out.println ("Testing CallableStmt Types.DOUBLE"); CallableStatement call = con.prepareCall (func + pkgName + "getDouble (?) }"); call.setDouble (2, (double)3.04); call.registerOutParameter (1, Types.DOUBLE); call.execute (); double result = call.getDouble (1); assertTrue ("correct return from getString ()", result == 42.42); } public void testGetInt () throws Throwable { // System.out.println ("Testing CallableStmt Types.INTEGER"); CallableStatement call = con.prepareCall (func + pkgName + "getInt (?) }"); call.setInt (2, 4); call.registerOutParameter (1, Types.INTEGER); call.execute (); int result = call.getInt (1); assertTrue ("correct return from getString ()", result == 42); } public void testGetNumeric () throws Throwable { // System.out.println ("Testing CallableStmt Types.NUMERIC"); CallableStatement call = con.prepareCall (func + pkgName + "getNumeric (?) }"); call.setBigDecimal (2, new java.math.BigDecimal(4)); call.registerOutParameter (1, Types.NUMERIC); call.execute (); java.math.BigDecimal result = call.getBigDecimal (1); assertTrue ("correct return from getString ()", result.equals (new java.math.BigDecimal(42))); } public void testGetString () throws Throwable { // System.out.println ("Testing CallableStmt Types.VARCHAR"); CallableStatement call = con.prepareCall (func + pkgName + "getString (?) }"); call.setString (2, "foo"); call.registerOutParameter (1, Types.VARCHAR); call.execute (); String result = call.getString (1); assertTrue ("correct return from getString ()", result.equals ("bob")); } public void testBadStmt () throws Throwable { tryOneBadStmt ("{ ?= " + pkgName + "getString (?) }"); tryOneBadStmt ("{ ?= call getString (?) "); tryOneBadStmt ("{ = ? call getString (?); }"); } protected void tryOneBadStmt (String sql) throws Throwable { boolean wasCaught = false; try { CallableStatement call = con.prepareCall (sql); } catch (SQLException e) { wasCaught = true; // good -> this statement was missing something } assertTrue ("bad statment ('"+sql+"')was not caught", wasCaught); } } ** Big patch to Callable Statement *** jdbc2/CallableStatement.java Thu Jun 13 11:42:58 2002 --- CallableStatement.java.old Wed Jun 12 15:28:05 2002 *************** *** 7,13 **** import java.sql.*; import java.math.*; ! import org.postgresql.util.*; /* * CallableStatement is used to execute SQL stored procedures. * --- 7,13 ---- import java.sql.*; import java.math.*; ! /* * CallableStatement is used to execute SQL stored procedures. * *************** *** 37,43 **** * * @see Connection#prepareCall * @see ResultSet - * @author Paul Bethe (implementer) */ public class CallableStatement extends org.postgresql.jdbc2.PreparedStatement implements java.sql.CallableStatement --- 37,42 ---- *************** *** 47,120 **** */ public CallableStatement(Connection c, String q) throws SQLException { ! super(c, q); // don't parse yet.. } - - /** - * allows this class to tweak the standard JDBC call !see Usage - * -> and replace with the pgsql function syntax - * ie. select <function ([params])> as RESULT; - */ - - protected void parseSqlStmt () throws SQLException { - modifyJdbcCall (); - super.parseSqlStmt (); - } - /** - * this method will turn a string of the form - * {? = call <some_function> (?, [?,..]) } - * into the PostgreSQL format which is - * select <some_function> (?, [?, ...]) as result - * - */ - private void modifyJdbcCall () throws SQLException { - //** WOULD like to use Regexp but jakarta is not - // used currently and don't want to require - // Java 1.4 by using native regexp.. - // so syntax checking is not complete only a few basics :( - originalSql = sql; // save for error msgs.. - int index = sql.indexOf ("="); // is implied func or proc? - boolean isValid = false; - if (index != -1) { - isFunction = true; - isValid = sql.indexOf ("?") < index; // ? before = - } - sql = sql.trim (); - if (sql.startsWith ("{") && sql.endsWith ("}")) { - // isValid = isValid && true; - sql = sql.substring (1, sql.length() -1); - } else isValid = false; - index = sql.indexOf ("call"); - if (index == -1 || !isValid) - throw new PSQLException ("postgresql.call.malformed", - new Object[]{sql, JDBC_SYNTAX}); - sql = sql.replace ('{', ' '); // replace these characters - sql = sql.replace ('}', ' '); - sql = sql.replace (';', ' '); - - // this removes the 'call' string and also puts a hidden '?' - // at the front of the line for functions, this will - // allow the registerOutParameter to work correctly - sql = (isFunction ? "?" : "") + sql.substring (index + 4); - - sql = "select " + sql + " as " + RESULT_COLUMN + ";"; - } - - // internals - static final String JDBC_SYNTAX = "{[? =] call <some_function> ([? [,?]*]) }"; - static final String RESULT_COLUMN = "result"; - String originalSql = ""; - boolean isFunction; - // functionReturnType contains the user supplied value to check - // testReturn contains a modified version to make it easier to - // check the getXXX methods.. - int functionReturnType; - int testReturn; - // returnTypeSet is true when a proper call to registerOutParameter has been made - boolean returnTypeSet; - Object result; - /* * Before executing a stored procedure call you must explicitly * call registerOutParameter to register the java.sql.Type of each --- 46,54 ---- */ public CallableStatement(Connection c, String q) throws SQLException { ! super(c, q); } /* * Before executing a stored procedure call you must explicitly * call registerOutParameter to register the java.sql.Type of each *************** *** 124,131 **** * the getXXX method whose Java type XXX corresponds to the * parameter's registered SQL type. * - * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used - * * @param parameterIndex the first parameter is 1, the second is 2,... * @param sqlType SQL type code defined by java.sql.Types; for * parameters of type Numeric or Decimal use the version of --- 58,63 ---- *************** *** 133,188 **** * @exception SQLException if a database-access error occurs. */ public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException ! { ! if (parameterIndex != 1) ! throw new PSQLException ("postgresql.call.noinout"); ! if (!isFunction) ! throw new PSQLException ("postgresql.call.procasfunc", originalSql); ! ! // functionReturnType contains the user supplied value to check ! // testReturn contains a modified version to make it easier to ! // check the getXXX methods.. ! functionReturnType = sqlType; ! testReturn = sqlType; ! if (functionReturnType == Types.CHAR || ! functionReturnType == Types.LONGVARCHAR) ! testReturn = Types.VARCHAR; ! else if (functionReturnType == Types.FLOAT) ! testReturn = Types.REAL; // changes to streamline later error checking ! returnTypeSet = true; ! } ! ! /** ! * allow calls to execute update ! * @return 1 if succesful call otherwise 0 ! */ ! public int executeUpdate() throws SQLException ! { ! // System.out.println ("Executing " + compileQuery()); ! java.sql.ResultSet rs = super.executeQuery (compileQuery()); ! if (isFunction) { ! if (!rs.next ()) ! throw new PSQLException ("postgresql.call.noreturnval"); // TODO-props ! result = rs.getObject (1); ! int columnType = rs.getMetaData().getColumnType (1); ! if (columnType != functionReturnType) ! throw new PSQLException ("postgresql.call.wrongrtntype", ! new Object[]{ ! getSqlTypeName (columnType), getSqlTypeName (functionReturnType) }); ! } ! rs.close (); ! return 1; ! } ! ! ! /** ! * allow calls to execute update ! * @return true if succesful ! */ ! public boolean execute() throws SQLException ! { ! return (executeUpdate() == 1); ! } /* * You must also specify the scale for numeric/decimal types: --- 65,71 ---- * @exception SQLException if a database-access error occurs. */ public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException ! {} /* * You must also specify the scale for numeric/decimal types: *************** *** 199,238 **** */ public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException ! { ! registerOutParameter (parameterIndex, sqlType); // ignore for now.. ! } ! ! /* ! * override this method to check for set @ 1 when declared function.. ! * ! * @param paramIndex the index into the inString ! * @param s a string to be stored ! * @exception SQLException if something goes wrong ! */ ! protected void set(int paramIndex, String s) throws SQLException ! { ! if (paramIndex == 1 && isFunction) // need to registerOut instead ! throw new PSQLException ("postgresql.call.funcover"); ! super.set (paramIndex, s); // else set as usual.. ! } ! ! /* ! * Helper - this compiles the SQL query from the various parameters ! * This is identical to toString() except it throws an exception if a ! * parameter is unused. ! */ ! protected synchronized String compileQuery() ! throws SQLException ! { ! if (isFunction && !returnTypeSet) ! throw new PSQLException("postgresql.call.noreturntype"); ! if (isFunction) { // set entry 1 to dummy entry.. ! inStrings[0] = ""; // dummy entry which ensured that noone overrode ! // and calls to setXXX (2,..) really went to first arg in a function call.. ! } ! return super.compileQuery (); ! } // Old api? //public boolean isNull(int parameterIndex) throws SQLException { --- 82,88 ---- */ public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException ! {} // Old api? //public boolean isNull(int parameterIndex) throws SQLException { *************** *** 251,257 **** public boolean wasNull() throws SQLException { // check to see if the last access threw an exception ! return (result == null); } // Old api? --- 101,107 ---- public boolean wasNull() throws SQLException { // check to see if the last access threw an exception ! return false; // fake it for now } // Old api? *************** *** 269,276 **** */ public String getString(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.VARCHAR, "String"); ! return (String)result; } //public String getVarChar(int parameterIndex) throws SQLException { // return null; --- 119,125 ---- */ public String getString(int parameterIndex) throws SQLException { ! return null; } //public String getVarChar(int parameterIndex) throws SQLException { // return null; *************** *** 289,297 **** */ public boolean getBoolean(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.BIT, "Boolean"); ! if (result == null) return false; ! return ((Boolean)result).booleanValue (); } /* --- 138,144 ---- */ public boolean getBoolean(int parameterIndex) throws SQLException { ! return false; } /* *************** *** 303,311 **** */ public byte getByte(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.TINYINT, "Byte"); ! if (result == null) return 0; ! return (byte)((Integer)result).intValue (); } /* --- 150,156 ---- */ public byte getByte(int parameterIndex) throws SQLException { ! return 0; } /* *************** *** 317,327 **** */ public short getShort(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.SMALLINT, "Short"); ! if (result == null) return 0; ! return (short)((Integer)result).intValue (); } - /* * Get the value of an INTEGER parameter as a Java int. --- 162,169 ---- */ public short getShort(int parameterIndex) throws SQLException { ! return 0; } /* * Get the value of an INTEGER parameter as a Java int. *************** *** 332,340 **** */ public int getInt(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.INTEGER, "Int"); ! if (result == null) return 0; ! return ((Integer)result).intValue (); } /* --- 174,180 ---- */ public int getInt(int parameterIndex) throws SQLException { ! return 0; } /* *************** *** 346,354 **** */ public long getLong(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.BIGINT, "Long"); ! if (result == null) return 0; ! return ((Long)result).longValue (); } /* --- 186,192 ---- */ public long getLong(int parameterIndex) throws SQLException { ! return 0; } /* *************** *** 360,368 **** */ public float getFloat(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.REAL, "Float"); ! if (result == null) return 0; ! return ((Float)result).floatValue (); } /* --- 198,204 ---- */ public float getFloat(int parameterIndex) throws SQLException { ! return (float) 0.0; } /* *************** *** 374,382 **** */ public double getDouble(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.DOUBLE, "Double"); ! if (result == null) return 0; ! return ((Double)result).doubleValue (); } /* --- 210,216 ---- */ public double getDouble(int parameterIndex) throws SQLException { ! return 0.0; } /* *************** *** 393,400 **** public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { ! checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); ! return ((BigDecimal)result); } /* --- 227,233 ---- public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { ! return null; } /* *************** *** 407,414 **** */ public byte[] getBytes(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.VARBINARY, "Bytes"); ! return ((byte [])result); } // New API (JPM) (getLongVarBinary) --- 240,246 ---- */ public byte[] getBytes(int parameterIndex) throws SQLException { ! return null; } // New API (JPM) (getLongVarBinary) *************** *** 425,432 **** */ public java.sql.Date getDate(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.DATE, "Date"); ! return (java.sql.Date)result; } /* --- 257,263 ---- */ public java.sql.Date getDate(int parameterIndex) throws SQLException { ! return null; } /* *************** *** 438,445 **** */ public java.sql.Time getTime(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.TIME, "Time"); ! return (java.sql.Time)result; } /* --- 269,275 ---- */ public java.sql.Time getTime(int parameterIndex) throws SQLException { ! return null; } /* *************** *** 452,459 **** public java.sql.Timestamp getTimestamp(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp"); ! return (java.sql.Timestamp)result; } //---------------------------------------------------------------------- --- 282,288 ---- public java.sql.Timestamp getTimestamp(int parameterIndex) throws SQLException { ! return null; } //---------------------------------------------------------------------- *************** *** 488,495 **** public Object getObject(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex); ! return result; } // ** JDBC 2 Extensions ** --- 317,323 ---- public Object getObject(int parameterIndex) throws SQLException { ! return null; } // ** JDBC 2 Extensions ** *************** *** 499,508 **** throw org.postgresql.Driver.notImplemented(); } ! public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException { ! checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); ! return ((BigDecimal)result); } public Blob getBlob(int i) throws SQLException --- 327,335 ---- throw org.postgresql.Driver.notImplemented(); } ! public java.math.BigDecimal getBigDecimal(int i) throws SQLException { ! throw org.postgresql.Driver.notImplemented(); } public Blob getBlob(int i) throws SQLException *************** *** 540,615 **** throw org.postgresql.Driver.notImplemented(); } - // no custom types allowed yet.. public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { throw org.postgresql.Driver.notImplemented(); } - - - /** helperfunction for the getXXX calls to check isFunction and index == 1 - */ - private void checkIndex (int parameterIndex, int type, String getName) - throws SQLException { - checkIndex (parameterIndex); - if (type != this.testReturn) - throw new PSQLException("postgresql.call.wrongget", - new Object[]{getSqlTypeName (testReturn), - getName, - getSqlTypeName (type)}); - } - /** helperfunction for the getXXX calls to check isFunction and index == 1 - * @param parameterIndex index of getXXX (index) - * check to make sure is a function and index == 1 - */ - private void checkIndex (int parameterIndex) throws SQLException { - if (!isFunction) - throw new PSQLException("postgresql.call.noreturntype"); - if (parameterIndex != 1) - throw new PSQLException("postgresql.call.noinout"); - } - - /** helper function for creating msg with type names - * @param sqlType a java.sql.Types.XX constant - * @return String which is the name of the constant.. - */ - private static String getSqlTypeName (int sqlType) { - switch (sqlType) - { - case Types.BIT: - return "BIT"; - case Types.SMALLINT: - return "SMALLINT"; - case Types.INTEGER: - return "INTEGER"; - case Types.BIGINT: - return "BIGINT"; - case Types.NUMERIC: - return "NUMERIC"; - case Types.REAL: - return "REAL"; - case Types.DOUBLE: - return "DOUBLE"; - case Types.FLOAT: - return "FLOAT"; - case Types.CHAR: - return "CHAR"; - case Types.VARCHAR: - return "VARCHAR"; - case Types.DATE: - return "DATE"; - case Types.TIME: - return "TIME"; - case Types.TIMESTAMP: - return "TIMESTAMP"; - case Types.BINARY: - return "BINARY"; - case Types.VARBINARY: - return "VARBINARY"; - default: - return "UNKNOWN"; - } - } } --- 367,376 ---- __________________________________________________ Do You Yahoo!? Yahoo! - Official partner of 2002 FIFA World Cup http://fifaworldcup.yahoo.com
Attachment
pgsql-patches by date: