Thread: getUdateCount() vs. RETURNING clause
Hi, I'm having a problem using an UPDATE statement with a RETURNING clause. It seems I cannot retrieve the number of rows affected using getUpdateCount() This is my statement UPDATE my_table SET the_column = the_column + 1 WHERE id = = RETURNING the_column; My Java code is: PreparedStatement pstmt = con.prepareStatement(UPDATE_SQL); // the statement from above pstmt.setInt(1, 42); boolean hasResult = pstmt.execute(); if (hasResult) { ResultSet rs = pstmt.getResultSet(); if (rs != null && rs.next()) { int newId = rs.getInt(1); System.out.println("newid: " + newId); } } int affected = pstmt.getUpdateCount(); System.out.println("affected: " + affected); I do see the returned ID from the ResultSet but getUpdateCount() always returns -1 even though I know that a row was updated. What am I missing here? My Postgres version is: 8.4.1 My driver version is: PostgreSQL 8.4 JDBC4 (build 701) I'm using Java6 (1.6.0_16) Regards Thomas
Thomas Kellerer wrote: > boolean hasResult = pstmt.execute(); > > if (hasResult) { > ResultSet rs = pstmt.getResultSet(); > if (rs != null && rs.next()) { > int newId = rs.getInt(1); > System.out.println("newid: " + newId); > } > } > > int affected = pstmt.getUpdateCount(); > System.out.println("affected: " + affected); > > I do see the returned ID from the ResultSet but getUpdateCount() always > returns -1 even though I know that a row was updated. > What am I missing here? Update counts and result sets (for a particular result) are mutually exclusive. If getResultSet() returns non-null then getUpdateCount() must return -1; see the javadoc for those two methods. You probably want to call getMoreResults() in there somewhere to step to the next result. -O
Oliver Jowett, 25.11.2009 01:40: > Thomas Kellerer wrote: > >> boolean hasResult = pstmt.execute(); >> >> if (hasResult) { >> ResultSet rs = pstmt.getResultSet(); >> if (rs != null && rs.next()) { >> int newId = rs.getInt(1); >> System.out.println("newid: " + newId); >> } >> } >> >> int affected = pstmt.getUpdateCount(); >> System.out.println("affected: " + affected); >> >> I do see the returned ID from the ResultSet but getUpdateCount() always >> returns -1 even though I know that a row was updated. >> What am I missing here? > > Update counts and result sets (for a particular result) are mutually > exclusive. If getResultSet() returns non-null then getUpdateCount() must > return -1; see the javadoc for those two methods. Hmm, my understand was a bit different. The Javadocs simply say There are no more results when the following is true: ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1)) It doesn't state that they are mutually exclusive. And for me it seemed to imply that I can call getMoreResults() and getUpdateCount()in a loop in order to process everything that is returned. Additionally the Javadocs for getUpdateCount() says: "Gets the *current* result as an update count" and ".. if there are no more results it returns -1" The word "current" here also let me to believe I can call those methods multiple times. Regards Thomas
Thomas Kellerer wrote: > Hmm, my understand was a bit different. > The Javadocs simply say > > There are no more results when the following is true: > ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1)) > > It doesn't state that they are mutually exclusive. And for me it seemed > to imply that I can call getMoreResults() and getUpdateCount() in a loop > in order to process everything that is returned. > > Additionally the Javadocs for getUpdateCount() says: > > "Gets the *current* result as an update count" and ".. if there are no > more results it returns -1" You've done some selective editing there. The javadoc I referred to is this (from the Java 6 javadoc): getResultSet(): Retrieves the current result as a ResultSet object. This method should be called only once per result. Returns: the current result as a ResultSet object or null if the result is an update count or there are no more results --- getUpdateCount(): Retrieves the current result as an update count; if the result is a ResultSet object or there are no more results, -1 is returned. This method should be called only once per result. Returns: the current result as an update count; -1 if the current result is a ResultSet object or there are no more results --- I think that's fairly clear: the "current result" is either a ResultSet, or an update count, but it can't be both. getUpdateCount() explicitly says that it returns "-1 [...] if the current result is a ResultSet object", and getResultSet() explicitly says that it returns "null if the [current] result is an update count." > The word "current" here also let me to believe I can call those methods > multiple times. From memory the postgresql driver doesn't care if you call them multiple times, but FWIW the javadoc says that you should only call them once per result. -O
Oliver Jowett, 25.11.2009 12:13: > > You've done some selective editing there. The javadoc I referred to is > this (from the Java 6 javadoc): > > getResultSet(): > > Retrieves the current result as a ResultSet object. This method should > be called only once per result. Correct, once per *result* not per statement. If the statement returns more than one result, I should be allowed to callit multiple time. I think the base of my (mis)understanding is that the term "current" lead me to believe that the "stack" of results a statementcan hold, could look like this: resultSet update count = 3 update count = 2 resultSet reslutSet So if I create a loop using the condition stated in the Javadocs the program flow would be as follows: 1) stmt.execute() returns true, so I call getResultSet() 2) getMoreResults() returns false, but getUpdateCount() returns 3 ==> go on 3) getMoreResults() returns false, but getUpdateCount() returns 2 ==> go on 4) getMoreResults() returns true, so getResultSet() returns a result set ==> go on 5) getMoreResults() returns true, so getResultSet() returns a result set ==> go on 6) getMoreResults() returns false, getUpdateCount() returns -1 ==> everything was processed. Apparently this interpretation of "current", "next" and "once per _result_" was wrong... Regards Thomas
Thomas Kellerer wrote: > Oliver Jowett, 25.11.2009 12:13: >> >> You've done some selective editing there. The javadoc I referred to is >> this (from the Java 6 javadoc): >> >> getResultSet(): >> >> Retrieves the current result as a ResultSet object. This method should >> be called only once per result. > > Correct, once per *result* not per statement. If the statement returns > more than one result, I should be allowed to call it multiple time. That's right, but you need a call to getMoreResults() to step through the results between calls, as I suggested in my original response. > I think the base of my (mis)understanding is that the term "current" > lead me to believe that the "stack" of results a statement can hold, > could look like this: > > resultSet > update count = 3 > update count = 2 > resultSet > reslutSet Yes, you can have that. You step through the results by calling getMoreResults(). At any particular point, the current result is either a resultset or an update count, but never both. > So if I create a loop using the condition stated in the Javadocs the > program flow would be as follows: > > 1) stmt.execute() returns true, so I call getResultSet() > 2) getMoreResults() returns false, but getUpdateCount() returns 3 ==> go on > 3) getMoreResults() returns false, but getUpdateCount() returns 2 ==> go on > 4) getMoreResults() returns true, so getResultSet() returns a result set > ==> go on > 5) getMoreResults() returns true, so getResultSet() returns a result set > ==> go on > 6) getMoreResults() returns false, getUpdateCount() returns -1 ==> > everything was processed. Yes, this is correct. It will look something like this: boolean hasResultSet = stmt.execute(); int updateCount = stmt.getUpdateCount(); while (hasResultSet || updateCount != -1) { if (hasResultSet) { ResultSet rs = stmt.getResultSet(); // This result is a resultset, process rs. } else { // This result is an update count, process updateCount. } hasResultSet = stmt.getMoreResults(); updateCount = stmt.getUpdateCount(); } -O
Oliver Jowett, 25.11.2009 13:16: >> So if I create a loop using the condition stated in the Javadocs the >> program flow would be as follows: >> >> 1) stmt.execute() returns true, so I call getResultSet() >> 2) getMoreResults() returns false, but getUpdateCount() returns 3 ==> go on >> 3) getMoreResults() returns false, but getUpdateCount() returns 2 ==> go on >> 4) getMoreResults() returns true, so getResultSet() returns a result set >> ==> go on >> 5) getMoreResults() returns true, so getResultSet() returns a result set >> ==> go on >> 6) getMoreResults() returns false, getUpdateCount() returns -1 ==> >> everything was processed. > > Yes, this is correct. It will look something like this: So my understanding was correct ;) Back to my original question then: why doesn't the Postgres driver return 1 as the updateCount in this situation? I only get a single result set (which is correct) but never a 1 as the update count. Regards Thomas
Thomas Kellerer wrote: > Back to my original question then: why doesn't the Postgres driver > return 1 as the updateCount in this situation? > I only get a single result set (which is correct) but never a 1 as the > update count. Back to my original answer then ;-) Quoting your original code: > PreparedStatement pstmt = con.prepareStatement(UPDATE_SQL); // the statement from above > pstmt.setInt(1, 42); > boolean hasResult = pstmt.execute(); > > if (hasResult) { > ResultSet rs = pstmt.getResultSet(); > if (rs != null && rs.next()) { > int newId = rs.getInt(1); > System.out.println("newid: " + newId); > } > } > > int affected = pstmt.getUpdateCount(); > System.out.println("affected: " + affected); You never call getMoreResults(), so you are only looking at a single result, which is either a resultset or an update count, never both. -O
Oliver Jowett wrote: > You never call getMoreResults(), so you are only looking at a single > result, which is either a resultset or an update count, never both. Also, looking at the code a bit more, RETURNING is a bit of a special case. Normally, if you have a command that returns a resultset, the command status is ignored (you generally don't care about the command status of, for example, a SELECT). Presumably that's happening here too. (But the advice regarding getMoreResults() is generally applicable, e.g. if you have a multiple-statement query). You may have more success using something like this: > PreparedStatement pstmt = con.prepareStatement("UPDATE something with no RETURNING clause", new String[] { "some_column"}); > int updateCount = pstmt.executeUpdate(); > ResultSet results = pstmt.getGeneratedKeys(); The driver glues on an appropriate RETURNING clause and arranges for the resulting resultset to appear via getGeneratedKeys(); the update count should still appear as expected. (I haven't actually tried this. caveat emptor) -O
Oliver Jowett, 25.11.2009 14:42: > You never call getMoreResults(), so you are only looking at a single > result, which is either a resultset or an update count, never both. [...] Hmm, sorry I missed that in my initial email then. I did call getMoreResults() The following still returns false for getMoreResults() PreparedStatement pstmt = con.prepareStatement(update); pstmt.setInt(1, 1); boolean hasResult = pstmt.execute(); if (hasResult ) { ResultSet rs = pstmt.getResultSet(); if (rs != null && rs.next()) { int newId = rs.getInt(1); System.out.println("newid: " + newId); } } boolean more = pstmt.getMoreResults(); // returns false > You may have more success using something like this: > >> PreparedStatement pstmt = con.prepareStatement("UPDATE something with no RETURNING clause", new String[] { "some_column"}); >> int updateCount = pstmt.executeUpdate(); >> ResultSet results = pstmt.getGeneratedKeys(); That indeed works! But I still think the behaviour with getMoreResults() is - at least - confusing ;) Thanks a lot for all your patience! Thomas