Seite 1 von 1

Probleme mit MySQLdb - Warnung

Verfasst: Sonntag 26. November 2006, 11:16
von geraldf
Hallo allerseits,

das Python-Modul MySQLdb vermittelt zwischen Python und MySQL. Derzeit haben viele Entwickler damit Probleme.

Das Projekt ist auf Sourceforge gehostet.
http://sourceforge.net/projects/mysql-python/

Ich habe im Forum "Open Discussion" einen offenen Brief an den Entwickler gepostet.
http://sourceforge.net/forum/forum.php? ... m_id=70460

Das Folgende ist eine auszugsweise Übersetzung aus diesem Posting:

---

Es gab eine - stille? - Interface-Änderung zwischen den Versionen 1.2.1c3 und and 1.2.1_p2.

Alte Version: cursor.execute() erhält einen String als Parameter.
Neue Version: cursor.execute() erwartet ein Unicode-Objekt.

Aus diesem Grund kämpfen derzeit viele Entwickler mit nicht mehr funktionierenden Anwendungen. Oder sie haben dieses Erlebnis noch vor sich.
z. B. wenn sie von Ubuntu 6.06 auf Version 6.10 wechseln.

Derzeit scheint es keine "Produktivversion" von MySQLdb zu geben, die unter Linux UND Windows funktioniert.

Niemand ist für das Testen unter Windows verantwortlich, obwohl in der Projektzusammenfassung "32-bit MS Windows (NT/2000/XP)" als Plattformen angeführt sind.

--- Auszug Ende

Da bei vielen Leuten schon recht viel Arbeitszeit den Bach hinuntergegangen ist, rate ich aus derzeitiger Sicht zu großer Vorsicht beim Einsatz von MySQLdb.
Lest auf Sourceforge, python-forum.de, im Usenet etc. alle relevanten Postings und Bugreports und zieht Eure Schlüsse.
Testet rechtzeitig auf allen Ziel-Plattformen.

Verfasst: Sonntag 26. November 2006, 13:18
von Y0Gi
Ich bin nicht sicher, wie direkt da Zusammenhänge bestehen, aber ich hatte ein größeres Problem mit Encodings mit der noch aktuellen stabilen Version von MySQLdb. Auf http://homework.nwsnet.de/releases/2d1e ... e-imminent habe ich dazu ein paar Worte geschrieben und einen Weg aufgezeigt, dies mit einem einfachen Patch zu umgehen. Leider lässt 1.2.2 schon viel zu lange auf sich warten :(

Re: Probleme mit MySQLdb - Warnung

Verfasst: Montag 4. Dezember 2006, 06:56
von jens
geraldf hat geschrieben:Interface-Änderung zwischen den Versionen 1.2.1c3 und and 1.2.1_p2.

Alte Version: cursor.execute() erhält einen String als Parameter.
Neue Version: cursor.execute() erwartet ein Unicode-Objekt.
Aha! Das ist aber interessant!
Ich hab in PyLucid mit relativ viel Aufwand selber das de-/encoding eingebaut...

Gibt es denn wirklich einen Traceback, wenn man bei der neuen Version einen String und kein Unicode Objekt benutzt???

Verfasst: Donnerstag 7. Dezember 2006, 12:10
von jens
Wie sieht es nun aus mit unicode und MySQLdb... Gibt es da neue Erkenntnisse???

Ich bastel mir jetzt mal ein Skript zum testen.

Verfasst: Donnerstag 7. Dezember 2006, 13:46
von jens
So, ich hab mal ein test Skript geschrieben: http://paste.pocoo.org/show/298/

Ich hab es getestet mit MySQLdb Version: 1.2.1g2 und 1.2.1_p2 mehr habe ich z.Z. nicht zur Verfügung...

Ich muß da aber irgendwie einen Fehler drin haben... Hier mal die Ausgaben, die eigentlich bei beiden Versionen gleich sind:
-==[MySQLdb unicode test]==-

MySQLdb Version: 1.2.1_p2
MySQLdb version_info: (1, 2, 1, 'final', 2)
MySQL server encoding: latin1
-------------------------------------------------------------------------------
CREATE TABLE temp_test... OK

-------------------------------------------------------------------------------
inser the teststring in unicode!
INSERT & SELECT... OK
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf-8'
INSERT & SELECT... OK
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'latin-1'
INSERT & SELECT... OK
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'iso-8859-15'
INSERT & SELECT... OK
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

DROP TABLE... OK

Close Connection... OK
Zur Funktionsweise:
Das Skript erzeugt eine Tabelle mit einem varchar Feld.
Es wird der Test-String in den Varianten "unicode", "utf-8", "latin-1", "iso-8859-15" eingefügt und danach wieder ausgelesen.
Der ausgelesene String wird versucht mit "unicode", "utf-8", "latin-1", "iso-8859-15" zu decodieren.

Wie sich zeigt, ist es 1. egal in welchem Encoding man Daten einfügt (Was irgendwie sehr merkwürdig ist) und 2. erhält man die Daten immer mit dem Codec zurück, der beim Server eingestellt ist.
Es ist der Codec der in der Variable "character_set_server" angegeben ist. Bei MySQL in der neueren Version ist es per default "latin-1"...

Irgendwie kann ich aber nicht glauben, das man die Daten in egal welchem Code in die DB packen kann.... Da muß irgendwo ein Fehler im Skript sein.

Verfasst: Donnerstag 7. Dezember 2006, 14:03
von BlackJack
jens hat geschrieben:Irgendwie kann ich aber nicht glauben, das man die Daten in egal welchem Code in die DB packen kann.... Da muß irgendwo ein Fehler im Skript sein.
Warum versuchst Du zu raten was Du zurückbekommst? Viel interessanter wäre es doch direkt den Vergleich zu zeigen: was Du reinsteckst und was Du zurückbekommst. Beides als `repr()` damit man wirklich Byte für Byte sieht wie die Daten aussehen.

Verfasst: Donnerstag 7. Dezember 2006, 14:09
von jens
Sehr gute Idee! Ich sollte mir endlich mal repr() merken :oops:

Aktualisiert: http://paste.pocoo.org/show/299/
Diff: http://paste.pocoo.org/compare/299/

Code: Alles auswählen

-==[MySQLdb unicode test]==-

MySQLdb Version: 1.2.1_p2
MySQLdb version_info: (1, 2, 1, 'final', 2)

MySQL server encoding:
	MySQL variable 'character_set_server': latin1
	connection.character_set_name: latin1

-------------------------------------------------------------------------------
CREATE TABLE temp_test... OK

-------------------------------------------------------------------------------
inser the teststring in unicode!
INSERT & SELECT... OK
db_input...: u'Test \xe4\xf6\xfc\xdf'
db_output..: 'Test \xe4\xf6\xfc\xdf'
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf-8':
INSERT & SELECT... OK
db_input...: 'Test \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f'
db_output..: 'Test \xe4\xf6\xfc\xdf'
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'latin-1':
INSERT & SELECT... OK
db_input...: 'Test \xe4\xf6\xfc\xdf'
db_output..: 'Test \xe4\xf6\xfc\xdf'
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'iso-8859-15':
INSERT & SELECT... OK
db_input...: 'Test \xe4\xf6\xfc\xdf'
db_output..: 'Test \xe4\xf6\xfc\xdf'
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf_16_be':
INSERT & SELECT... OK
db_input...: '\x00T\x00e\x00s\x00t\x00 \x00\xe4\x00\xf6\x00\xfc\x00\xdf'
db_output..: 'Test \xe4\xf6\xfc\xdf'
The String can be encoded with 'latin-1'
Test output with 'utf-8': Test äöüß

DROP TABLE... OK

Close Connection... OK
Wie man sieht, stecke ich schon den String richtig encodiert in die DB... Und es kommt immer in "latin-1" raus und ist der selbe Test-String...

Ich werde mit mal eine Uralte MySQLdb installieren und damit mal testen...

EDIT: Gesagt, getan... Das Ergebnis ist das selbe mit MySQLdb version_info: (1, 2, 0, 'final', 1)
EDIT 2: Das gleiche bei MySQLdb version_info: (1, 0, 0, 'final', 1) ... Ist die älteste für Python 2.4... Mal sehen, wie es mit Python 2.3 aussieht!
EDIT 3: Mit Python 2.3 und MySQLdb version_info: (1, 2, 0, 'final', 1) aus das selbe...
EDIT4: Und noch Python 2.3 mit MySQLdb version_info: (0, 9, 2, 'final', 1) das selbe...

Verfasst: Donnerstag 7. Dezember 2006, 14:21
von mitsuhiko
Ich werfe zzzeeks sqlalchemy in den Raum.

Verfasst: Donnerstag 7. Dezember 2006, 14:23
von Leonidas
blackbird hat geschrieben:Ich werfe zzzeeks sqlalchemy in den Raum.
Ich schmeiß auch noch PostgreSQL ein - ein DMBS das sehr gut funktioniert und viele verschiedene teilweise auch sehr gute Python-Interfaces hat. Wird auch von SQLAlchemy unterstützt.

Verfasst: Donnerstag 7. Dezember 2006, 14:32
von jens
SQLAlchemy und PostgreSQL hilft mir jetzt unheimlich toll weiter... :?

Also: Was kann ich jetzt aus meinem Test folgern?

Das man egal in welchem Encoding Daten in die DB schmeißen kann und sie kommen immer sauber im Server Encoding (also latin-1) zurück?

Das kann doch irgendwie nicht sein, oder?

Verfasst: Donnerstag 7. Dezember 2006, 14:47
von sape
jens hat geschrieben:[...]
Das man egal in welchem Encoding Daten in die DB schmeißen kann und sie kommen immer sauber im Server Encoding (also latin-1) zurück?

Das kann doch irgendwie nicht sein, oder?
Ne das kann auch nicht sein ;) Spätesten wenn du ein encoding für Chinesisch oder ähnliches benutzt und es dann in ein "Coding rausholst" das den Zeichensatz nicht unterstützt, wird es nicht gehen.

lg

Verfasst: Donnerstag 7. Dezember 2006, 15:00
von jens
Hm... Nagut... Wenn der Server latin-1 als encoding eingestellt hat, kann er auch IMHO nur alle Zeichen die latin-1 abbilden kann speichern... Latin-1 müßte somit alle Zeichen von 0x0000 bis 0x00FF abbilden, oder ?

Ich mache mal ein Test damit...

Verfasst: Donnerstag 7. Dezember 2006, 15:08
von jens
Jep, so ist es...

Als Testdaten hab ich mal das genommen:

Code: Alles auswählen

TEST_STRING = u"".join([unichr(i) for i in xrange(0, 256)])
Das kann ich als "unicode" oder encodiert in "utf-8", "latin-1" oder "utf_16_be" in die DB schmeißen... Raus kommt latin-1 der decodiert genau dem Test-String entspricht...

Also ist ja schön, das MySQLdb es so macht, aber wie macht es das???

Wenn ich einen String im exotischen "utf_16_be" Encoding in die DB schreibe, wie kann das Modul wissen, das es dieser Codec ist?

Verfasst: Donnerstag 7. Dezember 2006, 15:25
von jens
Hier mal der latin-1 Test: http://paste.pocoo.org/show/301/

Wirft das raus:

Code: Alles auswählen

-==[MySQLdb unicode test]==-

MySQLdb Version: 1.2.0
MySQLdb version_info: (1, 2, 0, 'final', 1)

MySQL server encoding:
	MySQL variable 'character_set_server': latin1
	connection.character_set_name: latin1

-------------------------------------------------------------------------------
CREATE TABLE temp_test... OK

-------------------------------------------------------------------------------
inser the teststring in unicode!
INSERT & SELECT... OK
db_input...: u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: True

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf-8':
INSERT & SELECT... OK
db_input...: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: True

-------------------------------------------------------------------------------
insert the teststring encoded in 'latin-1':
INSERT & SELECT... OK
db_input...: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: True

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf_16_be':
INSERT & SELECT... OK
db_input...: '\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: True

DROP TABLE... OK

Close Connection... OK

Ach, und noch was...

Man kann ab MySQL v4.1 das verwendete character set ändern. So:

Code: Alles auswählen

cursor.execute('set character set %s;', ("utf8",))
Das scheint aber keine Gute Idee zu sein... In dem Fall sind alle Werte "defekt". Egal ob ich versuche die Ausgaben mit "latin-1" oder "utf-8" zu encodieren:

Code: Alles auswählen

inser the teststring in unicode!
INSERT & SELECT... OK
db_input...: u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: False

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf-8':
INSERT & SELECT... OK
db_input...: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: False

-------------------------------------------------------------------------------
insert the teststring encoded in 'latin-1':
INSERT & SELECT... OK
db_input...: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: False

-------------------------------------------------------------------------------
insert the teststring encoded in 'utf_16_be':
INSERT & SELECT... OK
db_input...: '\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\...
db_output..: '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x1...
Encode db output with server encoding 'latin1'... OK
Is test-String == db output: False

Verfasst: Donnerstag 7. Dezember 2006, 18:15
von jens
So, nach den tests habe ich jetzt auch anpassungen in PyLucid unternommen.

Mit https://pylucid.net/trac/changeset/643 wird nun folgendes gemacht bzw. nicht mehr gemacht:
  • 1. Das DB encoding wird nicht mehr mit 'set character set %s;' geändert. Es wird immer das default encoding des Servers genommen.
    2. Die Daten, zur DB gehen, werden nicht mehr von in ein anderes encoding gebracht.
Und, ja, ich werde auf SQLAlchemy umsteigen ;) btw. wie bekommt man da die Daten zurück? Direkt als unicode oder in dem encoding der DB???

Verfasst: Donnerstag 7. Dezember 2006, 20:08
von mitsuhiko
jens hat geschrieben:Und, ja, ich werde auf SQLAlchemy umsteigen ;) btw. wie bekommt man da die Daten zurück? Direkt als unicode oder in dem encoding der DB???
Wenn du eine Column als "String" im table definierst bekommst du das encoding zurück, oder bullshit. Letzteres besonders gerne beim MySQL adapter. Das würde ich gar nicht verwenden. Wenn deine Column als "Unicode" definiert ist bekommst du, tadadada: unicode :)

Verfasst: Mittwoch 13. Dezember 2006, 10:15
von jens

Verfasst: Donnerstag 8. Februar 2007, 18:17
von jens
geraldf hat geschrieben:Interface-Änderung zwischen den Versionen 1.2.1c3 und and 1.2.1_p2.

Alte Version: cursor.execute() erhält einen String als Parameter.
Neue Version: cursor.execute() erwartet ein Unicode-Objekt.
Da hat sich wohl noch mehr geändert...

Beim connecten kann man einmal ein kwarg "charset" angeben, in einer älteren v1.2.1 nicht, dafür aber "use_unicode"...

Irgendwie blicke ich in den Versionnummern nicht durch. Von daher kann man auch schlecht testen, welches kwargs nun geht, wie hier:

Code: Alles auswählen

        from MySQLdb import version_info
        if version_info < (1, 2, 1):
            # Für alte Versionen
            kwargs["use_unicode"] = True
        else:
            # ab python-mysqldb v1.2.1
            kwargs["charset"] = self.encoding
Ich hab es jetzt so gemacht: Ich probiere es mit dem neuen "charset"="utf8", wenn das nicht geht, setzte ich "use_unicode"=true :?

Ich frage mich dann, ob der trac-Patch auch richtig arbeitet.