Patch for Statement.getGeneratedKeys() - Mailing list pgsql-jdbc
From | Ken Johanson |
---|---|
Subject | Patch for Statement.getGeneratedKeys() |
Date | |
Msg-id | 4762327D.7090404@kensystem.com Whole thread Raw |
In response to | Re: Synthesize support for Statement.getGeneratedKeys()? (Ken Johanson <pg-user@kensystem.com>) |
Responses |
Re: Patch for Statement.getGeneratedKeys()
Re: Patch for Statement.getGeneratedKeys() |
List | pgsql-jdbc |
Kris, please try to apply the attached and let me know what errors if any you get. All ids are now quoted in: executeUpdate(String sql, String columnIndexes[]), and: int executeUpdate(String sql, int columnIndexes[]) is implemented, however it currently will work ONLY if the fully qualified table is set in the insert: INSERT INTO foocatalog.fooschema.tbl .....(quoted or not) It will support normalizing the not-supplied catalog and schema names -- after I find out how to extract these from the Connection (hopefully this would not require an additional round trip). Any suggestions on this? Ken # This patch file was generated by NetBeans IDE # Following Index: paths are relative to: C:\dev\java\proj\pgjdbc\pgjdbc # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: org/postgresql/jdbc2/AbstractJdbc2Statement.java *** C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\jdbc2\AbstractJdbc2Statement.java Base (1.104) --- C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\jdbc2\AbstractJdbc2Statement.java Locally Modified (Based On 1.104) *************** *** 286,291 **** --- 286,318 ---- } /* + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition + * SQL statements that return nothing such as SQL DDL statements + * can be executed + * + * @param sql a SQL statement + * @return either a row count, or 0 for SQL commands + * @exception SQLException if a database access error occurs + */ + protected int executeUpdateGetResults(String p_sql) throws SQLException + { + if (preparedQuery != null) + throw new PSQLException(GT.tr("Can''t use query methods that take a query string on a PreparedStatement."), + PSQLState.WRONG_OBJECT_TYPE); + if( isFunction ) + { + executeWithFlags(p_sql, 0); + return 0; + } + checkClosed(); + p_sql = replaceProcessing(p_sql); + Query simpleQuery = connection.getQueryExecutor().createSimpleQuery(p_sql); + execute(simpleQuery, null, 0); + this.lastSimpleQuery = simpleQuery; + return getUpdateCount(); + } + + /* * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, * SQL statements that return nothing such as SQL DDL statements can * be executed. Index: org/postgresql/jdbc3/AbstractJdbc3Statement.java *** C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\jdbc3\AbstractJdbc3Statement.java Base (1.21) --- C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\jdbc3\AbstractJdbc3Statement.java Locally Modified (Based On 1.21) *************** *** 11,16 **** --- 11,17 ---- import java.math.BigDecimal; import java.sql.*; + import java.util.ArrayList; import java.util.Calendar; import java.util.Vector; *************** *** 19,24 **** --- 20,28 ---- import org.postgresql.core.QueryExecutor; import org.postgresql.core.Field; import org.postgresql.core.BaseConnection; + import org.postgresql.core.Utils; + import org.postgresql.jdbc2.AbstractJdbc2Connection; + import org.postgresql.jdbc2.AbstractJdbc2Statement.StatementResultHandler; import org.postgresql.util.GT; /** *************** *** 28,33 **** --- 32,38 ---- */ public abstract class AbstractJdbc3Statement extends org.postgresql.jdbc2.AbstractJdbc2Statement { + private final int rsHoldability; public AbstractJdbc3Statement (AbstractJdbc3Connection c, int rsType, int rsConcurrency, int rsHoldability) throwsSQLException *************** *** 106,112 **** */ public ResultSet getGeneratedKeys() throws SQLException { ! return createDriverResultSet(new Field[0], new Vector()); } /** --- 111,119 ---- */ public ResultSet getGeneratedKeys() throws SQLException { ! return result==null ? ! createDriverResultSet(new Field[0], new Vector()) ! : result.getResultSet(); } /** *************** *** 135,141 **** { if (autoGeneratedKeys == Statement.NO_GENERATED_KEYS) return executeUpdate(sql); ! throw new PSQLException(GT.tr("Returning autogenerated keys is not supported."), PSQLState.NOT_IMPLEMENTED); } --- 142,148 ---- { if (autoGeneratedKeys == Statement.NO_GENERATED_KEYS) return executeUpdate(sql); ! //fix me : impl NO_GENERATED_KEYS & RETURN_GENERATED_KEYS throw new PSQLException(GT.tr("Returning autogenerated keys is not supported."), PSQLState.NOT_IMPLEMENTED); } *************** *** 159,172 **** */ public int executeUpdate(String sql, int columnIndexes[]) throws SQLException { ! if (columnIndexes.length == 0) return executeUpdate(sql); ! ! throw new PSQLException(GT.tr("Returning autogenerated keys is not supported."), PSQLState.NOT_IMPLEMENTED); } /** --- 166,206 ---- */ public int executeUpdate(String sql, int columnIndexes[]) throws SQLException { ! if (columnIndexes==null || columnIndexes.length == 0) return executeUpdate(sql); ! String prefix = sql.substring(0,10).toLowerCase(); ! if (columnIndexes==null || prefix.indexOf("insert")==-1) ! { ! return executeUpdateGetResults(sql); } + int start = Utils.position(sql, "INTO", 0); + ArrayList args = Utils.getInsertIds(sql, start); + String pgCols = + "SELECT column_name "+ + "FROM information_schema.columns "+ + "WHERE table_catalog='"+args.get(0)+"' AND table_schema='"+args.get(1)+"' AND table_name='"+args.get(2)+"'"+ + "ORDER BY ordinal_position"; + ResultSet rs = null; + String[] columnNames = new String[columnIndexes.length]; + try { + rs = this.executeQuery(pgCols); + } catch (SQLException ex) { + throw new PSQLException(GT.tr("Could not translate column name indexes.")+" "+ex, PSQLState.UNEXPECTED_ERROR); + } finally { + if (rs!=null) rs.close(); + } + int j=0; + try { + for (; j<columnNames.length; j++) + { + rs.absolute(columnIndexes[j]); + columnNames[j] = rs.getString(1); + } + } catch (SQLException ex) {//invalid column index provided + throw new PSQLException(GT.tr("Column index out of bounds.")+" "+columnIndexes[j], PSQLState.UNEXPECTED_ERROR); + } + return executeUpdate(sql, columnNames); + } /** * Executes the given SQL statement and signals the driver that the *************** *** 184,198 **** */ public int executeUpdate(String sql, String columnNames[]) throws SQLException { ! if (columnNames.length == 0) return executeUpdate(sql); ! ! throw new PSQLException(GT.tr("Returning autogenerated keys is not supported."), PSQLState.NOT_IMPLEMENTED); } /** * Executes the given SQL statement, which may return multiple results, --- 221,262 ---- */ public int executeUpdate(String sql, String columnNames[]) throws SQLException { ! String prefix = sql.substring(0,10).toLowerCase(); ! if (columnNames==null || prefix.indexOf("insert")==-1) ! return executeUpdateGetResults(sql); ! if (!(connection instanceof AbstractJdbc2Connection)) ! { ! throw new PSQLException(GT.tr("Driver version does not support returning generated keys.")+" "+connection.getClass().getName(),PSQLState.NOT_IMPLEMENTED); ! } ! AbstractJdbc2Connection con = (AbstractJdbc2Connection)connection; ! int args = columnNames.length; ! if (!connection.haveMinimumServerVersion("8.2")) ! throw new PSQLException(GT.tr("Server version does not support returning generated keys.")+" (< "+"8.2"+")",PSQLState.NOT_IMPLEMENTED); ! if (args==0) return executeUpdate(sql); ! StringBuffer s = new StringBuffer(sql.length()+(args*32)); ! s.append(sql); ! s.append('\n'); ! s.append("RETURNING"); ! s.append(' '); ! for (int i=0; i<args; i++) ! { ! String arg = columnNames[i]; ! if (arg==null) ! //throw new NullPointerException("executeUpdate: null columnName at index "+i); ! throw new PSQLException(GT.tr("Null value in columnNames"), PSQLState.INVALID_PARAMETER_VALUE); ! if (i!=0) ! s.append(','); ! s.append('"'); ! s.append(arg); ! s.append('"'); } + return executeUpdateGetResults(s.toString()); + //throw new PSQLException(GT.tr("Returning autogenerated keys is not supported."), PSQLState.NOT_IMPLEMENTED); + } + + /** * Executes the given SQL statement, which may return multiple results, * and signals the driver that any Index: org/postgresql/core/Utils.java *** C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\core\Utils.java Base (1.6) --- C:\dev\java\proj\pgjdbc\pgjdbc\org\postgresql\core\Utils.java Locally Modified (Based On 1.6) *************** *** 12,17 **** --- 12,18 ---- package org.postgresql.core; import java.sql.SQLException; + import java.util.ArrayList; import org.postgresql.util.GT; import org.postgresql.util.PSQLException; *************** *** 146,152 **** --- 147,286 ---- return sbuf; } + /** + * return true if the string contains whitespace and is not already quoted, false otherwise + * + * @param in + * @return true if the string contains whitespace and is not already quoted + * @throws java.sql.SQLException if the string contains quotes inside its value + * (foo"bar or "foor"bar"), or contains char 0x00. + */ + public static final boolean needsQuoted(String in) throws SQLException + { + int len = in.length(); + //quoted and non-empty quotes: + boolean already = len>1 && in.charAt(0)=='"' && in.charAt(len-1)=='"'; + if (already && len==2) + throw new PSQLException(GT.tr("Empty quoted value"), PSQLState.INVALID_PARAMETER_VALUE); + int end = len-1; + for (int i=1; i<end; i++) + {//scan for legal + char c = in.charAt(i); + if (c=='"') + throw new PSQLException(GT.tr("Invalid quotes found inside argument"), PSQLState.INVALID_PARAMETER_VALUE); + if (c=='\0') + throw new PSQLException(GT.tr("Null bytes may not occur in identifiers."), PSQLState.INVALID_PARAMETER_VALUE); } + for (int i=1; i<end; i++) + { + char c = in.charAt(i); + if (Character.isWhitespace(c)) + return !already; + } + return false; + } + + /** + * Return an ArrayList of Strings representing the table identifiers, quoted or not. + * Any number of id may exists; no attempt is made to validate the maximum number of IDs. + * @param sql INSERT INTO stmt + * @param start - index of the INTO keyword, after which the <code>catalog.schema.table</code> identifiers appear + * @return ArrayList who first element is the left-most identifiers, and right-most is the table name. + * @author Ken Johanon ken2006@onnet.cc + */ + public static ArrayList getInsertIds(String sql, int start) + { + if (start<0) + throw new IllegalArgumentException("getInsertIds: invalid start index: "+start); + start += 4; + //advance to first alnum + for (; start<sql.length(); start++) + if (Character.isLetterOrDigit(sql.charAt(start))) + break; + //advance to first non-quoted, non-alnum + ArrayList ar = new ArrayList(4); + int end = start; + int pos = start; + boolean inQuote = sql.charAt(end-1)=='"'; + for (; end<sql.length(); end++) + { + char c = sql.charAt(end); + if (inQuote) + { + if (c=='"') + { + ar.add(sql.substring(pos, end)); + end++; + pos = end+1; + inQuote = false; + } + } + else + { + if (c=='"') + { + inQuote = true; + pos = end+1; + } + else if (c=='.') + { + ar.add(sql.substring(pos, end)); + pos = end+1; + } + } + + if (c=='(' || (!inQuote && Character.isSpaceChar(c))) + { + if (pos!=end) + ar.add(sql.substring(pos, end)); + break; + } + } + return ar; + } + + /** + * Search for and return the location of <code>find</code> in String <code>in</code>, case insensitive. + * If in is empty, return -1. If find is empty, return 0. + * @param in + * @param find - string to find + * @param pos - starting position to search + * @return int location, or -1 if not found + * @author Ken Johanon ken2006@onnet.cc + */ + public static int position(CharSequence in, String find, int pos) + { + boolean c = in==null || in.length()==0; + boolean d = find==null || find.length()==0; + if (d || c && d) + return 0; + if (c) + return -1; + int a = in.length(); + int b = find.length(); + int count = 0; + //if (pos>a-b) + // return -1; + char c1, c2; + for (int i=pos; i<a; i++) + { + c1 = in.charAt(i); + c2 = find.charAt(count); + if (c1==c2 || c1==Character.toLowerCase(c2) || c1==Character.toUpperCase(c2)) + count++; + else + { + i -= count; + count = 0; + } + if (count==b) + return i-b+1; + } + return -1; + } + + }
pgsql-jdbc by date: