Rollback nach Select zerstört Transaktion

HI zusammen,

ich habe ein großes Problem mit dem ODBC Treiber von PostgreSQL, aber erst mal eine Kurze Beschreibung worum es geht:

Ich portiere zur Zeit eine Anwendung, die mit Gupta-SQL-Windows programmiert ist (4GL Sprache vergleichbar mit Delphi), WindowsXP von der eigenen Gupta-SQL-Base auf PostgreSQL 8.2.
Auf die Datenbank greife ich über ODBC zu (Version 8.02.02.00 Unicode, Datenbank ist UTF8 codiert). So weit so gut.
Da ich ziemlich umfangreiche Transaktionen habe, arbeite ich (natürlich) ohne Autocommit und bin dabei auf folgendes Verhalten gestoßen:

Zum Beispiel werden Datensätze in eine Tabelle eingefügt und dann wieder ausgelesen. Da alles innerhalb einer Transaktion läuft sollte man meinen, dass man die Datensätze sieht, die da eben eingefügt wurden.

Der Code mit dem das passiert könnte so aussehen:


Set sUser = ‘SYSADM’
Set sWS = ‘WS_JSP’
!
Set sInfo = ‘Test1’
Set nError = nError and SqlPrepareAndExecute( hSql, 'insert into sysadm.log (dbuser, ws, info) Values ( :sUser, :sWS, :sInfo ) ’ )
!
Set sInfo = ‘Test2’
Call SqlPrepareAndExecute( hSql, 'insert into sysadm.log (dbuser, ws, info) Values ( :sUser, :sWS, :sInfo ) ’ )
!
Set sInfo = ‘Test3’
Call SqlPrepareAndExecute( hSql, 'insert into sysadm.log (dbuser, ws, info) Values ( :sUser, :sWS, :sInfo ) ’ )
!
Set sInfo = ‘’
!
Call SqlPrepareAndExecute( hSql, ‘Select info from sysadm.log into :sInfo’ )
Call SqlFetchNext( hSql, nError )
Call SQLCommit(hSql)

sUser, sWS und sInfo sind lokale String Variablen, hSql ist ein SQL-Handle oder eine Connection
(Es handelt sich hier natürlich nicht um den echten Code, sondern nur um ein Beispiel, das ganze Fehlerhandling fehlt hier)

Wenn ich vor dem Select ein Commit mache geht alles wunderbar. Wenn ich den Commit dort mache, wo er jetzt steht wird nichts selektiert und die Datensätze kommen auch nicht in der Tabelle an.

Wenn man sich jetzt das Logfile des ODBC-Treibers ansieht erkennt man auch sofort warum:


conn=01ED9A78, PGAPI_DriverConnect( in)='DSN=ALSAPLOG;UID=sysadm;PWD=xxxxxxxx;DATABASE=alsaplog;SERVER=localhost;PORT=5432;CA=d;A6=;A7=100;A8=4096;B0=254;B1=32000;BI=0;C2=dd;CX=1bd27bb;A1=7.4-1;’, fDriverCompletion=0
DSN info: DSN=‘ALSAPLOG’,server=‘localhost’,port=‘5432’,dbase=‘alsaplog’,user=‘sysadm’,passwd=‘xxxxx’
onlyread=‘0’,protocol=‘7.4’,showoid=‘0’,fakeoidindex=‘0’,showsystable=‘0’
conn_settings=’’,conn_encoding=‘OTHER’
translation_dll=’’,translation_option=’’
Global Options: Version=‘08.02.0200’, fetch=100, socket=4096, unknown_sizes=0, max_varchar_size=254, max_longvarchar_size=32000
disable_optimizer=1, ksqo=1, unique_index=1, use_declarefetch=0
text_as_longvarchar=1, unknowns_as_longvarchar=0, bools_as_char=1 NAMEDATALEN=64
extra_systable_prefixes=‘dd_’, conn_settings=’’ conn_encoding=‘OTHER’
[ PostgreSQL version string = ‘8.2.1’ ]
[ PostgreSQL version number = ‘8.2’ ]
conn=01ED9A78, query=‘select oid, typbasetype from pg_type where typname = ‘lo’’
[ fetched 0 rows ]
[ Large Object oid = -999 ]
[ Client encoding = ‘UTF8’ (code = 6) ]
conn=01ED9A78, PGAPI_DriverConnect(out)=’(NULL)’
CONN ERROR: func=SQLDriverConnectW, desc=’’, errnum=215, errmsg=‘the ConnStrOut is too small’

henv=01ED3E10, conn=01ED9A78, status=1, num_stmts=16
sock=01EDE8B0, stmts=01ED8C80, lobj_type=-999
---------------- Socket Info -------------------------------
socket=1536, reverse=0, errornumber=0, errormsg=’(NULL)’
buffer_in=32359872, buffer_out=32370328
buffer_filled_in=77, buffer_filled_out=0, buffer_read_in=77
conn=01ED9A78, query=‘show max_identifier_length’
[ fetched 1 rows ]
conn=01ED9A78, query=‘insert into sysadm.log (dbuser, ws, info) Values ( E’SYSADM’ , E’WS_JSP’ , E’Test1’ ) ’
conn=01ED9A78, query=‘insert into sysadm.log (dbuser, ws, info) Values ( E’SYSADM’ , E’WS_JSP’ , E’Test2’ ) ’
conn=01ED9A78, query=‘insert into sysadm.log (dbuser, ws, info) Values ( E’SYSADM’ , E’WS_JSP’ , E’Test3’ ) ’
conn=01ED9A78, query='Select info from sysadm.log ’
[ fetched 3 rows ]
conn=01ED9A78, query=‘select n.nspname, c.relname, a.attname, a.atttypid, t.typname, a.attnum, a.attlen, a.atttypmod, a.attnotnull, c.relhasrules, c.relkind, c.oid, d.adsrc from (((pg_catalog.pg_class c inner join pg_catalog.pg_namespace n on n.oid = c.relnamespace and c.oid = 54883) inner join pg_catalog.pg_attribute a on (not a.attisdropped) and a.attnum > 0 and a.attrelid = c.oid) inner join pg_catalog.pg_type t on t.oid = a.atttypid) left outer join pg_attrdef d on a.atthasdef and d.adrelid = a.attrelid and d.adnum = a.attnum order by n.nspname, c.relname, attnum’
[ fetched 12 rows ]
PGAPI_Columns: table=‘log’,field_name=‘zeit’,type=1114,name=‘timestamp’
PGAPI_Columns: table=‘log’,field_name=‘dbuser’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘ws’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘modul’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘info’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘fenster’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘salcomtype’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘iserror’,type=21,name=‘int2’
PGAPI_Columns: table=‘log’,field_name=‘errnumber’,type=23,name=‘int4’
PGAPI_Columns: table=‘log’,field_name=‘statement’,type=25,name=‘text’
PGAPI_Columns: table=‘log’,field_name=‘comtyp’,type=1043,name=‘varchar’
PGAPI_Columns: table=‘log’,field_name=‘dbtable’,type=1043,name=‘varchar’
conn=01ED9A78, query='ROLLBACK’
conn=01ED9A78, query='Select info from sysadm.log ’
[ fetched 0 rows ]
conn=01ED9A78, query=‘COMMIT’
conn=01ED9A78, PGAPI_Disconnect_
Hier sieht man in der unteren Hälfte sehr deutlich die Inserts und dann den Select. Dann folgen Befehle, mit denen das Laufzeitsystem ermittelt, welche Spalten die Tabelle hat, auf der der Select läuft, und dann ein ROLLBACK. Mitten in der Transaktion. Und dann sind die eingefügten Zeilen natürlich wieder weg.

Der erste Select ist also nur ein Dummy-Select, um die Struktur des Ergebnisses zu erfahren, dann kommt der Rollback und dann erfolgt der eigentliche Select, um die Daten zu holen, die dann natürlich weg sind.
In der Log-Datei, die die Gupta schreibt fehlt dieses Rollback und auch mit SQL-Server ODBC funktioniert es einwandfrei. Deswegen habe ich den PG-ODBC Treiber in Verdacht. Habe ich hier evtl nur die falschen Einstellungen ? oder hat da sonst jemand eine Idee was ich falsch mache.

Aber grundsätzlich muss es doch möglich sein innerhalb einer Transaktion Daten zu schreiben und dann evtl. auch ganz andere zu lesen. Das kann doch nicht sein, dass da noch ein Fehler an dieser Stelle im Treiber ist.

Der ODBC Treiber ist mit folgenden Einstellungen installiert.

Datasource/Page1
Disable Genetic Optimizer = ON
KSQO = ON
Recognize Unique Indexes = ON
Use Declare/Fetch = OFF
CommLog = ON (zum testen, sonst OFF)
Parse Statements = OFF
Cancel as FreeStmt = OFF (testweise ON, bringt aber auch nichts)
MyLog = OFF
Unknown Size = Maximum
Data Type Options
Test as LongVarChar = ON
Unknowns as LongVarChar = OFF
Bools as Char = ON
Miscellaneous
Max Varchar = 254
Max Long Varchar = 32000
Chache Size = 100
Sys Table Prefix = dd_:

Datasource/Page2
Read Only = OFF
Show System Table = OFF
LF <-> CR/LF conversion = ON
Updateable Cursors = ON
bytea as LO = OFF
Row Versioning = ON
Disallow Premature = OFF
True is -1 = OFF
Server side prepare = OFF
INT9 as = Default
Protokoll = 7.4+
Level of rollback on errors = Transaction
OID Opitions = OFF

Treiber PostgreSQL Unicode

Für Hilfe wäre ich sehr dankbar, sonst kann ich das Projekt abschreiben.

JSP

Hi zusammen,

ich denke ich habe meinen Fehler gefunden:

Im ODBC-Treiber muss die Einstellung Parse Statement = ON gesetzt werden und dann funktioniert das ganze.
Ich bin mir aber jetzt immer noch nicht sicher, wo der Fehler herkam, denn mit perl tritt er nicht auf.

Auf jeden Fall wird das Rollback dann nicht gemacht.

Danke trotzdem

Danke für die Rückinfo, finde ich nett für alle, die drüber nachgedacht haben.

Andreas

Hallo, leider ist das Problem doch noch nicht gelöst…

Hier ist ein Auszug aus psqlodbc_****.log:

conn=01FE0E70, query='INSERT INTO T_AKTION   (ROWVERSION, NAKTIONKEY, AENDERUNGSDATUM, BEARBEITER, DTDATUMBIS, DTDATUMVON, ERFASSER, ERFASSUNGSDATUM, NMANDANTKEY, SBEZEICHNUNG)   VALUES (1,'68'::float8, '2007-03-06 09:58:08'::timestamp, E'IP', '2007-03-16 00:00:00'::timestamp, '2007-03-06 00:00:00'::timestamp, E'IP'             ,'2007-03-06 09:58:08'::timestamp, '1'::float8, E'06.03-16.03-104-IP'                 )'
conn=01FE0E70, query='SELECT IntToChar( ROWVERSION) from T_AKTION WHERE NAKTIONKEY=NULL               '
    [ fetched 0 rows ]
conn=01FE0E70, query='ROLLBACK'
conn=01FE0E70, query='SELECT IntToChar( ROWVERSION) from T_AKTION WHERE NAKTIONKEY='68'::float8               '
    [ fetched 0 rows ]
conn=01FE0E70, query='  UPDATE T_Lookuptable SET Aenderungsdatum  = '2007-03-06 09:58:08'::timestamp WHERE LutID = '4'::float8 and nMandantKey = '1'::float8'
conn=01FE0E70, query='COMMIT'

Der ODBC-Treiber verursacht folgende Befehle:

conn=01FE0E70, query='SELECT IntToChar( ROWVERSION)  from T_AKTION WHERE NAKTIONKEY=NULL '
    [ fetched 0 rows ]
conn=01FE0E70, query='ROLLBACK'

Wie kann ich ihm das abgewöhnen???
Wo gibt es ein genaue Dokumentation des ODBC-Treibers?

Ich kann dem ODBC-Treiber “Connect-Settings” mitgeben, welche wären das?

Brauche dringend Hilfe, da am Montag eine Pilotierung bevorsteht. :astonished:

alle Einstellungen sind wie von JSpatz beschrieben…

MFG
Ingo

Hallo,

Danke für die schnelle Antwort!

Das kommt definitiv nicht von mir…
Meine beiden Statements sind die folgenden:

INSERT INTO T_AKTION   (ROWVERSION, NAKTIONKEY, AENDERUNGSDATUM, BEARBEITER, DTDATUMBIS, DTDATUMVON, ERFASSER, ERFASSUNGSDATUM, NMANDANTKEY, SBEZEICHNUNG)   VALUES (1,'68'::float8, '2007-03-06 09:58:08'::timestamp, E'IP', '2007-03-16 00:00:00'::timestamp, '2007-03-06 00:00:00'::timestamp, E'IP', '2007-03-06 09:58:08'::timestamp, '1'::float8, E'06.03-16.03-104-IP')'

und

'SELECT IntToChar( ROWVERSION) from T_AKTION WHERE NAKTIONKEY='68'::float8'

Die beiden anderen Statements kommen irgendwo anders her…
und da habe ich den ODBC-Treiber im Verdacht…

Abgesehen davon funktionieren aber beide Ausdrücke die du erwähnt hast (= NULL) und (IS NULL)

Kipp einen Bugreport bei den ODBC-Leuten rein, das ist ein Bug, wenn es nicht von Dir kommt. Vielleicht kannst Du irgendwas mit Pass-trough machen, um den Treiber davon abzuhalten. Jedenfalls dürfte ein Vergleich der Form ‘= NULL’ einen Fehler und damit ein Rollback auslösen.

Andreas

folgender Eintrag im ODBC-Treiber hilft weiter:

Disallow Premature = 1

Damit wird dann der ODBC-Treiber anscheinen dazu gezwungen für “seine” Statements einen eigenen Cursor zu verwenden, sodass der Rollback sich dann auf sein “Reich” beschränkt ohne meine laufende Session zu stören…

Trotzdem vielen Dank…

PS:
Großen Dank an JSpatz für die Lösung des Problems.

PPS:
Wo kann ich den Bugreport genau einreichen?

Abgesehen davon funktionieren aber beide Ausdrücke die du erwähnt hast (= NULL) und (IS NULL)

Mmh, stimmt. Sauber aber nicht:

test=*# select * from tab;
 t | t2
---+----
 x | y
 x |
(2 rows)

test=*# select * from tab where t2=null;
 t | t2
---+----
(0 rows)

test=*# select * from tab where t2 is null;
 t | t2
---+----
 x |
(1 row)

Andreas

Na wie du siehst ist dann der Ausdruck “= NULL” doch genau richtig… schliesslich geht es dem ODBC-Treiber darum die Tabellenstruktur rauszufinden. Und da muss dann sichergestellt sein das gar nichts zurückkommt… er braucht ja nur die spalten…

Dafür hat man aber das information_schema erfunden, wo man auch gleich die Spaltentypen etc. erfragen kann. IMHO ist das doof, was der ODBC-Treiber da macht.

Andreas