Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Möglicher Fehler im Postgres-Treiber 'pg8000'

4 views
Skip to first unread message

Volker Böhm

unread,
Apr 13, 2013, 11:49:52 AM4/13/13
to
Hallo,
beim Versuch mit dem Modul 'pg8000' auf eine Postgres-Datenbank
zuzugreifen bin ich auf einen Fehler gestoßen, der meiner Meinung nach im
pg8000 enthalten ist.

Das Programm macht eine Query und arbeitet der Reihe nach alle Sätze ab.
Dabei erstellt es neue Sätze für eine andere Tabelle, die dort mit INSERT
hineingeschrieben werden. Wenn ich alle Sätze verarbeite und zum Schluss
erst ein COMMIT mache, geht alles gut.
Da das Originalprogramm aber ca. 450.000 Sätze verarbeitet und dabei mehr
als 2.000.000 neue Sätze in die Ziel-Tabelle schreibt, wofür es ca. 100
Stunden braucht, wollte ich natürlich zwischendurch gelegentlich ein
COMMIT machen. Wenn ich dies tue bricht das Programm aber kurze Zeit
später - genauer nach 100 Sätzen - mit dem Fehler
pg8000.errors.ProgrammingError: ('ERROR', '34000',
'portal "pg8000_portal_10007" does not exist')
ab.

Ich habe das Programm soweit vereinfacht, dass sich der Fehler
reproduzieren lässt:

--------- schnipp ---------------------------------------------
#!/usr/bin/env python

import random
from pg8000 import DBAPI as DB
#import psycopg2 as DB
#import pgdb as DB

random.seed()
conn = DB.connect(host = 'myhost', database = 'mydb',
port = 5432, user = 'me', password = 'mypwd')
cur1 = conn.cursor()
cur2 = conn.cursor()

#------------------- Tabellen erstellen -----------------------
cur1.execute('DROP TABLE IF EXISTS t1')
cur1.execute('''
create table t1 (
a integer,
b integer)''')
cur1.execute('DROP TABLE IF EXISTS t2')
cur1.execute('''
create table t2 (
c integer,
d integer)''')
#------------------- t1 befüllen ------------------------------
for i in range(10000):
cur1.execute('''
INSERT INTO t1(a,b)
VALUES(%s, %s)''',
(random.randint(1, 1000000), random.randint(1, 1000000)))
conn.commit()

#------------------- Hier geht's los --------------------------
cur1.execute('''
SELECT a,b
FROM t1''');
row = cur1.fetchone()
while row:
print row
a, b = row
cur2.execute('''
INSERT INTO t2(c, d)
VALUES(%s, %s)''',(3*a, 5*b))
conn.commit()
row = cur1.fetchone()
conn.close()
--------- schnapp ---------------------------------------------

Habe ich etwas übersehen oder mache ich etwas Prinzipielles falsch?

Wenn ich 'psycopg2' oder 'PygeSQL' als Treiber benutze (Tausch der IMPORT-
Zeilen), tritt der Fehler nicht auf.

Es sieht so aus, als würde der COMMIT-Befehl den Cursor cur1 Schließen
und dann nur noch die restlichen gepufferten Sätze gelesen werden. Es
kommt übrigens nicht auf die Anzahl der geschriebenen Sätze oder die
Anzahl der COMMITs an: Ein einziges COMMIT sorgt dafür, dass nach 100
Sätzen Schluss ist.

mfg Volker
--
Volker Böhm Tel.: +49 4141 981152 www.vboehm.de
Voßkuhl 5 Fax: +49 4141 981154
D-21682 Stade mailto:vol...@vboehm.de

Volker Böhm

unread,
Apr 13, 2013, 11:53:49 AM4/13/13
to
Am Sat, 13 Apr 2013 15:49:52 +0000 schrieb Volker Böhm:

> --------- schnipp ---------------------------------------------
> #!/usr/bin/env python
>
> import random from pg8000 import DBAPI as DB #import psycopg2 as DB
> #import pgdb as DB
>
> [..]

Das ist beim Posten irgendwas schief gelaufen. Es muss natürlich

> import random
> from pg8000 import DBAPI as DB
> #import psycopg2 as DB
> #import pgdb as DB

heißen.

Diez B. Roggisch

unread,
Apr 13, 2013, 12:09:17 PM4/13/13
to Die Deutsche Python Mailingliste
Hallo,

random.seed()

unnoetig, passiert eh beim import von random. siehe http://docs.python.org/2/library/random.html#random.seed
Ich finde es komisch, dass du zwei Cursor aufmachst, von denen einer ueber Transaktionen hinweg aufgehalten wird. Das ist IMHO unsauber und undefiniert.
Natuerlich ist das schon komisch, dass psycopg2 das unterstuetzt, pg8000 aber nicht.

Um dein Problem zu loesen, wuerde ich einfach zwei Verbindungen aufmachen, und aus der einen Arbeitspakete fuer die andere generieren. Oder deine Queries so umschreiben, dass das SELECT nur Datensaetze liefert, die im Durchlauf davor noch nicht vearbeitet wurden.

Last but not least - wenn du das wirklich als Fehler in pg8000 ansiehst, solltest du das dessen Autor melden. Das gut gemachte Beispiel hilft da sicher. Denn hier wird sich kaum jemand so intim mit dem PG-Protokoll auskennen. 

Diez

Volker Böhm

unread,
Apr 13, 2013, 1:00:15 PM4/13/13
to
Am Sat, 13 Apr 2013 18:09:17 +0200 schrieb Diez B. Roggisch:

> Ich finde es komisch, dass du zwei Cursor aufmachst, von denen einer
> ueber Transaktionen hinweg aufgehalten wird. Das ist IMHO unsauber und
> undefiniert.

Finde ich gar nicht. Ich arbeite seit mehr als zehn Jahren mit Postgres
(meist mit Java aber auch mit Python, früher mit Perl) und habe noch nie
von einem Programm aus mehr als eine Connection im selben Thread
aufgemacht. In einem Dutzend Methoden mit drei bis acht Queries und
Update-Statements zu arbeiten, halte ich für einen ganz normalen Vorgang;
und _selbstverständlich_ auf einer einzigen Connection, weil man -
zumindest wenn man mit Transaktionen arbeitet - sich sonst viel zu
schnell einen Deadlock produziert.

> Natuerlich ist das schon komisch, dass psycopg2 das unterstuetzt, pg8000
> aber nicht.
>
> Um dein Problem zu loesen, wuerde ich einfach zwei Verbindungen
> aufmachen, und aus der einen Arbeitspakete fuer die andere generieren.

Das ist natürlich in diesem einfachen Fall ein möglicher Workaround.

> Oder deine Queries so umschreiben, dass das SELECT nur Datensaetze
> liefert, die im Durchlauf davor noch nicht vearbeitet wurden.

Eigentlich wollte ich natürlich nur einen Durchlauf machen aber als
Notanker ist das echte Programm so geschrieben das es nach einem Abbruch
an der richtigen Stelle wieder aufsetzen kann. Aber derzeit habe ich mit
pg8000 alle 100 Sätze einen Abbruch; und das macht keinen Spaß.

> Last but not least - wenn du das wirklich als Fehler in pg8000 ansiehst,
> solltest du das dessen Autor melden. Das gut gemachte Beispiel hilft da
> sicher. Denn hier wird sich kaum jemand so intim mit dem PG-Protokoll
> auskennen.

Ja, wenn mir nicht noch jemand nachweist, dass ich groben Bockmist
verzapft habe :-) werde ich das wohl auf tun. Denn pg8000 ist wohl
derzeit der einzige native Python-Treiber für Postgres 2.x.. Und mit den
anderen Treibern, die eine DLL benutzen, kämpfe ich gerade unter cygwin :-
(

Diez B. Roggisch

unread,
Apr 13, 2013, 1:33:54 PM4/13/13
to Die Deutsche Python Mailingliste
On Apr 13, 2013, at 7:00 PM, Volker Böhm wrote:

Am Sat, 13 Apr 2013 18:09:17 +0200 schrieb Diez B. Roggisch:

Ich finde es komisch, dass du zwei Cursor aufmachst, von denen einer
ueber Transaktionen hinweg aufgehalten wird. Das ist IMHO unsauber und
undefiniert.

Finde ich gar nicht. Ich arbeite seit mehr als zehn Jahren mit Postgres
(meist mit Java aber auch mit Python, früher mit Perl) und habe noch nie
von einem Programm aus mehr als eine Connection im selben Thread
aufgemacht. In einem Dutzend Methoden mit drei bis acht Queries und
Update-Statements zu arbeiten, halte ich für einen ganz normalen Vorgang;
und _selbstverständlich_ auf einer einzigen Connection, weil man -
zumindest wenn man mit Transaktionen arbeitet - sich sonst viel zu
schnell einen Deadlock produziert.

Das ist nicht das, was ich gesagt habe. Das komische sind nicht die verschiedenen Cursor, sondern deren Nutzung ueber Transaktionsgrenzen hinweg. Das habe ich noch nie so gemacht (oder machen muessen). Und es ist auch semantisch finde ich hoechst fraglich: was bekommt denn ein Cursor zu sehen, der mit einem bestimmten Transaktions-Isolations-Level läuft, wenn du zwischendurch ein COMMIT machst, das Daten anderer Cursor zurueckgeschrieben hat. Sind die dann sichtbar, unsichtbar, undefiniert?

Und weil das so kompliziert ist, kann Postgres das auch nicht. So einfach ist das :)



Ja, wenn mir nicht noch jemand nachweist, dass ich groben Bockmist
verzapft habe :-) werde ich das wohl auf tun. Denn pg8000 ist wohl
derzeit der einzige native Python-Treiber für Postgres 2.x.. Und mit den
anderen Treibern, die eine DLL benutzen, kämpfe ich gerade unter cygwin :-

Denke ich habe ich nachgewiesen ;) Wenn psycopg2 et al das unterstuetzen, dann ist das also eher feature statt bug. Das kannst du natuerlich beantragen. Ich frage mich natuerlich jetzt, *WIE* psycopg2 das denn genau macht. Denn offensichtlich sind interne Strukturen der Postgres DB dazu nicht vorbereitet, und so muesste zb ein implizites tracking des lese-fortschrittes erfolgen. Kann ich mir schwer vorstellen.

Ein kurzer Blick in den Sourccode von psycopg2 ist da auch nicht erhellend, die benutzen auch einfach deklarierte Cursor.

UU liegt es an anderen Default-Transaktions-Verhaltensweisen - zB Autocommit oder sowas - zwischen den Adapter-Implementierungen.

Diez

Volker Böhm

unread,
Apr 13, 2013, 2:55:38 PM4/13/13
to
Leider nicht so ganz :-)
Dein Zitat (Kapitel 39.7.3) stammt aus der Beschreibung von PL/SQL und
nicht aus der Beschreibung zum SQL-Cursor. Die ist in der Kommando-
Referenz unter 'DECLARE' zu finden.

> Ein kurzer Blick in den Sourccode von psycopg2 ist da auch nicht
> erhellend, die benutzen auch einfach deklarierte Cursor.
>
> UU liegt es an anderen Default-Transaktions-Verhaltensweisen - zB
> Autocommit oder sowas - zwischen den Adapter-Implementierungen.

Ahh,
jetzt ahne ich worauf Du hinaus willst. Ich arbeite meistens mit Java. Da
hat man eine Connection, aus der man mit einer SQL-Anweisung ein
(Prepared-)Statement erstellt und dann beim Execute einen ResultSet
erhält. Ich hatte mir bei Python das auch immer so vorgestellt nur dass
der Python-Cursor erst ein Statement und nach der Ausführung den ResultSet
hält. Auf die Idee, dass Python einen echten PostGres-Cursor benutzt, bin
ich nie gekommen. Ich habe so was noch nie benutzt, aber zumindest in der
aktuellen Beschreibung steht:

WITH HOLD
WITHOUT HOLD

WITH HOLD specifies that the cursor can continue to be used after the
transaction that created it successfully commits. WITHOUT HOLD
specifies that the cursor cannot be used outside of the transaction
that created it. If neither WITHOUT HOLD nor WITH HOLD is specified,
WITHOUT HOLD is the default.

Dieser Modifier wird übrigens erstmalig in PostGres 7.4 erwähnt.

Also taucht die Frage auf, ob es unter Python das Äquivalent zu einem
ResultSet gibt, das per se keine Einschränkung durch COMMITs kennt. Wenn
die anderen (PsycoPg2 und PyGreSQL) aber mit der WITH-HOLD-Variante
arbeiten, wäre das nicht nötig.

Es bleibt spannend ...

Diez B. Roggisch

unread,
Apr 13, 2013, 3:13:44 PM4/13/13
to Volker Böhm, pyth...@python.org


Mobile Mail. Excuse brevity.
Ah, spannend. Das das die plsql Referenz war ist mir entgangen.

Aber dann ist die Lösung doch relativ simpel: pg8000 wird without Hold Cursor definieren. Psycopg with Hold. Beides hat vor & Nachteile, die frage ist, ob man das Bedarfsweise umschalten kann. Und das sollte als Feature request ja uU einfacher umzusetzen sein.

Diez




>
> Es bleibt spannend ...
>
> mfg Volker
> --
> Volker Böhm Tel.: +49 4141 981152 www.vboehm.de
> Voßkuhl 5 Fax: +49 4141 981154
> D-21682 Stade mailto:vol...@vboehm.de
> _______________________________________________
> python-de maillist - pyth...@python.org
> http://mail.python.org/mailman/listinfo/python-de
>
0 new messages