[DB-SIG] http://www.python.org/topics/database/DatabaseAPI-2.0.html

Gerhard Häring haering_postgresql@gmx.de
Sun, 1 Sep 2002 16:14:38 +0200


* Jekabs Andrushaitis <jekabs.andrusaitis@tietoenator.com> [2002-08-30 17:13 +0200]:
> If I understood the question here correctly - I am looking for answer to it
> too.
> I am using PostgreSQL however, but I have tables with fields whose
> default values are taken from sequence. Since I need to know the
> primary key value for record directly after insertion, I am fetching
> next value from sequence seperately, and only then inserting the row
> into table with primary key field value supplied.
> 
> Is there a way to easily fetch the field values for inserted row after
> insert statement execution?

I have exerimented with this, too and found two options, using pyPgSQL.
Here's one that is almost evil ;-) I'm assuming a table TEST, the table
definition doesn't really matter, as far as the primary id column is
called "id" and is the first column in the table definition. Here's the
server-side code.

create table test (
    id serial,
    name varchar(20),
    age int
);

create or replace function test_ins(varchar[]) returns integer as '
declare
  new_id integer;
  parms alias for $1;
  i int := 1;
  statement varchar(4000);
begin
  select nextval(\'test_id_seq\') into new_id;

  statement := \'insert into test values (\';
  statement := statement || to_char(new_id, ''9999999999999999999999'');
  while parms[i] loop
    statement := statement || '','' || parms[i];
    i := i + 1;
  end loop;
  statement := statement || '');'';

  execute statement;

  return new_id;
end;
' language 'plpgsql';

Now we need to call this PostgreSQL function from Python code.
Unfortunately, I had problems using cursor.callproc(), so for the
moment, I'm just using cursor.execute(). We need to pass /all/
parameters in the table definition, in the right order, quote them
appropriately so the server-side function can put them into the INSERT
statement, then wrap them up into a PostgreSQL VARCHAR array and send
them over:

    from pyPgSQL import PgSQL

    db = PgSQL.connect()
    cursor = db.cursor()
    cursor.execute("select test_ins(%s)", ([map(PgSQL._quote, ['Gerhard',
                                            25])]),)
    last_id = cursor.fetchone()[0]
    cursor.execute("select * from test where id=%s", (last_id,))
    print cursor.fetchone()
    db.commit()

I agree that this is not nice, but seems to work. Now for a less hackish
solution ;-) It's not completely finished, yet - I need to find a way to
properly implement getdb(), perhaps I'll build a context class or some
such:

from __future__ import nested_scopes

from pyPgSQL import PgSQL

db = PgSQL.connect()

def getdb():
    return db

def generic_insert(tablename, **kwargs):
    cursor = getdb().cursor()
    sql = "INSERT INTO %s(%s) VALUES" % \
        (tablename, ", ".join(kwargs.keys()))
    sql = sql + "(" + ",".join(["%s"] * len(kwargs) ) + ")"
    cursor.execute(sql, kwargs.values())

    cursor.execute("SELECT CURRVAL('%s_ID_SEQ')" % tablename)
    return cursor.fetchone()[0]

def generic_update(tablename, id, **kwargs):
    cursor = getdb().cursor()
    sets = ", ".join([k + "=%s" for k in kwargs.keys()])
    statement = "UPDATE %s SET %s WHERE ID=%%s" % \
        (tablename, sets)
    cursor.execute(statement, kwargs.values() + [id])

def generic_delete(tablename, id):
    cursor = getdb().cursor()
    statement = "DELETE FROM %s WHERE id=%%s" % tablename
    cursor.execute(statement, (id,))

def make_functions(tablename):
    globals()[tablename + "_INSERT"] = lambda **kwargs: generic_insert(tablename, **kwargs)
    globals()[tablename + "_UPDATE"] = lambda id, **kwargs: generic_update(tablename, id, **kwargs)
    globals()[tablename + "_DELETE"] = lambda id: generic_delete(tablename, id)
    
make_functions("TEST")

cursor = db.cursor()
cursor.execute("delete from test")
id = TEST_INSERT(name="fooz", age=26)
id = TEST_INSERT(name="foo", age=25)
TEST_UPDATE(id, name="bar")
TEST_DELETE(id)
cursor.execute("select * from test")
print cursor.fetchall()

HTH,

Gerhard
-- 
mail:   gerhard <at> bigfoot <dot> de       registered Linux user #64239
web:    http://www.cs.fhm.edu/~ifw00065/    OpenPGP public key id AD24C930
public key fingerprint: 3FCC 8700 3012 0A9E B0C9  3667 814B 9CAA AD24 C930
reduce(lambda x,y:x+y,map(lambda x:chr(ord(x)^42),tuple('zS^BED\nX_FOY\x0b')))