2017-12-17 7 views
1

Ich habe eine Anwendung, die pyodbc verwendet, um eine Verbindung zur Datenbank herzustellen. Es kann mit verschiedenen db-Engines laufen. Jetzt versuche ich es an Firebird anzupassen.Bindungsparameter mit pyodbc mit Verbindung zu Firebird

Für einige db Motoren (getestet mit SQLite, Sybase) I nativen Pyodbc Weise verbindlich Parameter zu handhaben können:

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", file_type_id, path, md5) 

Für Oracle hatte ich einige spezielle SQL zur Vorbereitung ausgeführt werden soll:

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
sql = """DECLARE v_file_type_id INT; v_path varchar2(1024); v_md5 varchar2(32); 
     BEGIN 
     v_file_type_id := %d; 
     v_path := '%s'; 
     v_md5 := '%s'; 
     insert into files (file_type_id, path, md5) values (v_file_type_id, v_path, v_md5); 
     END;""" % (file_type_id, path, md5) 
cur.execute(sql) 

Der erste Weg funktioniert nicht mit Firebird. Ich erhalte Fehler: pyodbc.IntegrityError: ('23000', '[23000] [ODBC Firebird Driver][Firebird]validation error for column FILE_TYPE_ID, value "*** null ***" (-625) (SQLExecDirectW)').

Ich habe versucht, dedizierten SQL vorzubereiten, genau wie für Oracle, aber ich habe ein paar Probleme damit.

cnxn = pyodbc.connect(connect_string, autocommit=True) 
cur = cnxn.cursor() 
sql = """set term^; 
     execute block 
     as 
     declare v_file_type_id int = %d; 
     declare v_path varchar(1024) = '%s'; 
     declare v_md5 varchar(32) = '%s'; 
     begin 
     insert into files (file_type_id, path, md5) values (:v_file_type_id, :v_path, :v_md5); 
     end 
     ^
     set term ; ^""" % (file_type_id, path, md5) 
cur.execute(sql) 

Es funktioniert nicht. Der Fehler ist diesmal: pyodbc.Error: ('HY000', '[HY000] [ODBC Firebird Driver][Firebird]Dynamic SQL Error\nSQL error code = -104\nToken unknown - line 1, column 5\nterm (-104) (SQLExecDirectW)').

Ich bin auf der Suche nach jeder Lösung, so dass ich in der Lage sein kann, es in jeder dieser 2 Möglichkeiten zu führen. Performance-Aspekt ist auch sehr wichtig - Anwendung wird nicht sehr oft ausgeführt, aber jedes Mal, wenn dieser Teil des Codes etwa 50k Einsätze zu files Tabelle macht.

+1

Die tatsächliche SQL, die Firebird auszuführen versucht, wäre hier hilfreich. Es gibt keine Möglichkeit zu sehen, dass das eigentliche SQL von 'pyodbc' ausgeführt wird (und nicht sein sollte), da es das SQL und die Parameter übergibt. Können Sie die Abfrageprotokollierung in Firebird aktivieren und die SQL, die den Fehler verursacht, einbeziehen? – FlipperPA

Antwort

1

Der erste Fehler wird durch die Tatsache verursacht Sie einen null Wert in eine not null Spalte eingefügt FILE_TYPE_ID genannt, in anderen Worten: Ihre Variable file_type_id ist null, und dies wird durch die Einschränkungen auf der Säule nicht erlaubt, oder etwas anderes ist falsch machen.

Es ist schon eine Weile her, seit ich mit dem Python oder seinem DB-API tat alles, aber soweit ich mich erinnere, Sie ist der Weg falsch ausführen:

cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", file_type_id, path, md5) 

Sollte sein (beachten Sie die Verwendung von ein Tupel)

cur.execute("insert into files (file_type_id, path, md5) values (?, ?, ?)", (file_type_id, path, md5)) 

der zweite Fehler wird dadurch verursacht, dass set term keine gültige Firebird SQL-Anweisung ist, ist es eine Aussage spezifisch für Client-Tools ist Anweisung Terminator zu wechseln, siehe auch firebird procedural query throwing "token unknown" error at "SET TERM #;"

Execute Block ist eine anonyme "gespeicherte" Prozedur und wird normalerweise nicht verwendet, um vorbereitete Anweisungen auf diese Weise auszuführen. Ihre Verwendung des execute-Blocks ist besonders unsicher, da Sie sich wegen der Verwendung der Zeichenfolgenformatierung für SQL-Injection öffnen, und wegen des execute-Blocks ist dieses Formular gefährlicher als die Ausführung einer 'normalen' Anweisung.

Es gibt mehrere Firebird-Datenbanktreiber für Python, FDB und pyfirebirdsql, so dass Sie ODBC nicht verwenden müssen.

+0

Tatsächlich benötigt pyodbc keine Parameterwerte, die als Tupel übergeben werden (ref: [hier] (https://github.com/mkleehammer/pyodbc/wiki/Features-beyond-the-DB-API#passing-parameters)). –

+0

Ja, der erste Fehler sagt über den Versuch, den Wert 'null' in Spalte zu setzen, die es nicht erlaubt. Aber glauben Sie mir, 'file_type_id' ist nicht' null', ich kann den Wert direkt vor 'cur.execute (...)' ausgeben. Der gleiche Code funktioniert auch, wenn ich 'connect_string' in die SQLite- oder Sybase-Datenbank ändere.Ich habe beide Wege versucht, um Parameter zu übergeben - direkt (wie ich oben geschrieben habe), und in Tumpel. Beides funktioniert in diesem Fall nicht. Ich habe 'set term' part vom Anfang und vom Ende entfernt, und es funktioniert jetzt. Ich verstehe potenziell gefährlich. Danke für Ihre Hilfe. – Yama

+0

@Yama Hmm, das würde einen Fehler im ODBC-Treiber vorschlagen, verwenden Sie Firebird ODBC-Treiber 2.0.5? –

Verwandte Themen