-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Wrong Id value fetched with lastval (Postgres)
PostPosted: Fri May 09, 2008 2:20 pm 
Newbie

Joined: Fri May 09, 2008 1:55 pm
Posts: 3
Hi,

I was wondering if any of you ever ran into this problem before. We have an entity defined with its Id primary key set as native (Postgres serial / sequence).

The problem we are having is that this particular table storing that entity has a trigger on it that executes on every insert on the table. The problem is that depending on the values inserted, the trigger will insert a row in another table (with an Id column also defined as serial / sequence based). Now when that happens, the SQL to retrieve the Id of the newly created entity by NHibernate "select lastval()" will get a bogus id (that SQL function retrieves the last updated value of any sequence, in this case the sequence current value of the other table populated by the trigger).

I think the problem stems from this code (http://jira.nhibernate.org:8080/jira/secure/attachment/11854/PostgreSQL81Dialect.cs)

Code:
        /// <summary>
        /// PostgreSQL 8.1 and above defined the fuction <c>lastval()</c> that returns the
        /// value of the last sequence that <c>nextval()</c> was used on in the current session.
        /// Call <c>lastval()</c> if <c>nextval()</c> has not yet been called in the current
        /// session throw an exception.
        /// </summary>
        /// <param name="identityColumn">not used</param>
        /// <param name="tableName">not used</param>
        /// <returns></returns>
        public override string GetIdentitySelectString(string identityColumn, string tableName)
        {
            return "select lastval()";
        }


I'm trying to figure out a different approach that would solve this issue, but ideas are welcome :)

Edit:
After looking around on the Postgres site, this could potentially work for serial columns:

select currval(pg_get_serial_sequence('[table]', '[column]'));
or
select currval(pg_get_serial_sequence('[schema].[table]', '[column]'));


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 2:31 pm 
Newbie

Joined: Tue May 01, 2007 8:35 am
Posts: 4
the lastval() function in PostgreSQL has similar behaviour as the @@IDENTITY function in MS SQL Server.
To solve this kind of problem, MS have defined the SCOPE_IDENTITY() function.
Unfortunately PostgreSQL didn't actually provide such functionality.

---

One possibility for solving this problem could be to change the declaration of your triggers that are apparently of kind AFTER, to BEFORE.
That way the trigger code will be executed before the insertion of the row in the table that contain the desired ID, and lastval() will return the correct value.
But depending of your trigger's code, this could not be possible...

---

The second possibility that I can imagine is to use the RETURNING clause:
PostgreSQL 8.2 and above have introduced the RETURNING clause for INSERT, UPDATE and DELETE statements.

Exemple:
Code:
CREATE TABLE foo (id serial, textfield varchar);
INSERT INTO foo VALUES (default, 'my text') RETURNING id;

This returns the value of foo.id for the inserted row.

You can try to modify the dialect for PostgreSQL 8.2 (PostgreSQL82Dialect.cs) by adding something like:
Code:
public override SqlString AddIdentitySelectToInsert(SqlString insertSql, string identityColumn, string tableName)
{
   return insertSql.Append(" returning ").Append(identityColumn);
}

This should work for NHibernate 1.2.x

Apparently the Dialect.AddIdentitySelectToInsert method from NH1.2x was replaced by this one in NH2.0alpha:
Code:
public virtual SqlString AppendIdentitySelectToInsert(SqlString insertString)

And this method does no more takes the identityColumn parameter. Maybe there is another method to overload in NH2.0 that allow us to do the same thing. I haven't spent long time there.



ref:
SQL Server 2005 SCOPE_IDENTITY():
http://msdn.microsoft.com/en-us/library/ms190315.aspx
PostgreSQL 8.0 CREATE TRIGGER:
http://www.postgresql.org/docs/8.0/interactive/sql-createtrigger.html
PostgreSQL 8.2 INSERT:
http://www.postgresql.org/docs/8.2/interactive/sql-insert.html


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 13, 2008 7:01 pm 
Newbie

Joined: Fri May 09, 2008 1:55 pm
Posts: 3
Thanks dbachmann, your second possibilty was exactly what we were looking for, quite elegant and straight forward.

We did have to modify our Insert Rules on all our Views (to include a RETURNING *) so that insert statements with returning clause on our views would work properly, but it seems to be working beautifully since. I'll report back if I run into any other problems.

As for NHibernate 2.0, hopefully this issue will be looked at.

Cheers


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 8:24 am 
Newbie

Joined: Tue May 01, 2007 8:35 am
Posts: 4
It would be great if we can find a way to solve this issue for NH1.2 AND NH2.0 (certainly with the said RETURNING clause), but this discussion should continue on JIRA rather than in the user forum...

Could you please report this issue to NHibernate issue tracking system (http://jira.nhibernate.org). Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 14, 2008 11:02 am 
Newbie

Joined: Fri May 09, 2008 1:55 pm
Posts: 3
I posted the issue as you suggested.

http://jira.nhibernate.org:8080/jira/browse/NH-1316


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.