Skip site navigation (1) Skip section navigation (2)

Peripheral Links

Header And Logo

PostgreSQL
| The world's most advanced open source database.

Site Navigation

Search for
  Advanced Search

Re: currval() race condition on server?



Dave Cramer wrote:

On 23-Oct-06, at 9:49 AM, Adriaan Joubert wrote:

Hi,

I've run into an intermittent problem with our code recently. We have the following set-up:

table A : some data table
table B : a history table for table A

A trigger copies the old version of a row into table B whenever an update is done on table A. Both A and B contain an audit number, and the trigger obtains an audit number from a sequence and inserts it into the row inserted into table A.

For some bookkeeping purposes I need the new audit number back from the update, so I submit a prepared statement through jdbc of the form

UPDATE A SET ....; SELECT currval('ip_audit_seq');

On the first call I get

ERROR: currval of sequence "ip_audit_seq" is not yet defined in this session
currval is only defined after you call nextval in that connection.

Yes, and this is done in a C trigger that is called as a result of the update (it is a BEFORE INSERT OR DELETE OR UPDATE ... trigger). The C code does a

  /* Find a plan for getting the next sequence number */
  plan = find_plan(sequence_name, &ExecPlans, &nExecPlans);
  if (plan->splan == NULL) {
    sprintf(query, "SELECT nextval('%s')", sequence_name);
    /* Prepare plan for query */
    pplan = SPI_prepare(query, 0, NULL);
    if (pplan == NULL)
      elog(ERROR, "audit(%s): SPI_prepare returned %d",
	   sequence_name, SPI_result);
    pplan = SPI_saveplan(pplan);
    if (pplan == NULL)
      elog(ERROR, "audit(%s): SPI_saveplan returned %d",
	   sequence_name, SPI_result);
    plan->splan = pplan;
  }
  /* Execute the plan */
  ret = SPI_execp(plan->splan, NULL, NULL, 0);
  if (ret < 0)
    elog(ERROR, "audit(%s): SPI_execp returned %d", sequence_name, ret);
  /* Get the new sequence number */
  new_seq = (int) DatumGetInt64
    ( SPI_getbinval( SPI_tuptable->vals[0], SPI_tuptable->tupdesc,
		     1, &isnull) );


The update part of the trigger then inserts the sequence number into the row (new_tuple being the tuple that is passed into the trigger)

    newtuple = SPI_modifytuple(rel, newtuple, 1, &i_audit,
			       (Datum *) &new_seq, NULL);

which puts the sequence number into the relation. As this is in a BEFORE trigger, I would have through that it should be available to currval before executing the second statement in the query.


As to Tom's question: the jdbc driver is executing the update without any problems. On the java side I can do an

	PreparedStatement st = connection.prepareStatement(...);
	...
	st.execute();
	nUpdated = st.getUpdateCount();
         if (nUpdated == 1 && st.getMoreResults()) {
                ResultSet rs = st.getResultSet();
                if (rs.next()) {
                    oldAudit_ = audit_;
                    setAudit(rs.getInt(1));
                }
        }
        st.close();

which works well. So the real question is why currval is not working even though the update succeeds? Or rather - the update does not succeed, as the transaction is rolled back, but if I try to do the same thing a second time it does.

Perhaps the assumption that the update has succeeded is incorrect - but then, if the update statement fails, it should never attempt to execute the SELECT currval(), should it? Certainly in updates that fail we do not get an error from the SELECT currval().

Thanks for all your responses!

Adriaan



Home | Main Index | Thread Index

Privacy Policy | PostgreSQL Archives hosted by Command Prompt, Inc. | Designed by tinysofa
Copyright © 1996 – 2008 PostgreSQL Global Development Group