Загадка SQLAlchemy Unicode - PullRequest
       2

Загадка SQLAlchemy Unicode

0 голосов
/ 26 ноября 2018

У меня странная проблема, связанная с обработкой Unicode с помощью SQLAlchemy.Короче говоря, когда я вставляю строку Unicode Python в столбец Unicode моей базы данных MySQL, у меня не возникает проблем с ее возвратом.Однако на стороне базы данных он хранится в виде странной 4-байтовой последовательности (и нет, похоже, это не имеет никакого отношения к стандартному значению utf8mb4 в MySQL)

Моя проблема в том, чтоУ меня есть дамп MySQL с другого компьютера, который содержит прямые символы UTF8 в SQL.Когда я пытаюсь получить данные, импортированные с этого другого компьютера, я все время получаю UnicodeDecodeErrors.

Ниже я включил минимальный пример, иллюстрирующий проблему.

  • utf8test.sql: настроить базу данных и создать одну строку с символом Unicode

  • utf8test.py: открыть базу данных с помощью SQLAlchemy, вставить 1 строку с идеей Python относительно символа UTF иполучить обе строки.

Оказывается, что Python может извлекать данные, которые он вставил сам, прекрасно, но он останавливается на литерале 'ä', который я вставил в скрипт импорта SQL.Исследование hexdumps как набора данных mysqldumped, так и файлов двоичных данных самого MySQL показывает, что символ UTF, вставленный с помощью SQL, является реальной сделкой (немецкий umlaut 'ä' = UTF 'c3 bc'), тогда как Python-вставленный 'ä'преобразуется в последовательность' c3 83 c2 a4 ', которую я не понимаю (см. ниже hexdump; я использовал' xxx 'и' yyy 'в качестве маркеров для облегчения поиска их в hexdump).

Кто-нибудь может пролить свет на это?

Это создает тестовую базу данных:

dh@jenna:~/python$ cat utf8test.sql
DROP DATABASE IF EXISTS utftest;
CREATE DATABASE utftest;
USE utftest;
CREATE TABLE x (
    id INTEGER PRIMARY KEY AUTO_INCREMENT,
        text VARCHAR(10)
        );
INSERT INTO x(text) VALUES ('xxxü');
COMMIT;
dh@jenna:~/python$ mysql < utf8test.sql

Вот скрипт Pyhton:

dh@jenna:~/python$ cat utf8test.py
# -*- encoding: utf8 -*-

from sqlalchemy import create_engine, Column, Unicode, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class X(Base):
    __tablename__ = 'x'
    id = Column(Integer, primary_key=True)
    text = Column(Unicode(10))

engine = create_engine('mysql://localhost/utftest',
    encoding='utf8')
Base.metadata.create_all(engine)
Session = sessionmaker(engine)

db = Session()
x = X(text=u'yyyä')
db.add(x)
db.commit()

rs = db.query(X.text).all()
for r in rs:
    print(r.text)

db.close()

Это происходит, когда я запускаюскрипт (запускается без ошибок, когда я опускаю бит INSERT INTO в utf8test.sql):

dh@jenna:~/python$ python utf8test.py
Traceback (most recent call last):
  File "utf8test.py", line 23, in <module>
      rs = db.query(X.text).all()
[...]
UnicodeDecodeError: 'utf8' codec can't decode
    byte 0xfc in position 3: invalid start byte

Вот шестнадцатеричный дамп для подтверждения того, что эти два действительно хранятся в БД по-разному.Используя hd, я также подтвердил, что как Python, так и SQL-скрипты действительно являются UTF.

dh@jenna:~/python$ mysqldump utftest | hd
00000000  2d 2d 20 4d 79 53 51 4c  20 64 75 6d 70 20 31 30  |-- MySQL dump 10|
00000010  2e 31 36 20 20 44 69 73  74 72 69 62 20 31 30 2e  |.16  Distrib 10.|
00000020  31 2e 33 37 2d 4d 61 72  69 61 44 42 2c 20 66 6f  |1.37-MariaDB, fo|
00000030  72 20 64 65 62 69 61 6e  2d 6c 69 6e 75 78 2d 67  |r debian-linux-g|
00000040  6e 75 20 28 69 36 38 36  29 0a 2d 2d 0a 2d 2d 20  |nu (i686).--.-- |
[...]
00000520  4c 45 20 4b 45 59 53 20  2a 2f 3b 0a 49 4e 53 45  |LE KEYS */;.INSE|
00000530  52 54 20 49 4e 54 4f 20  60 78 60 20 56 41 4c 55  |RT INTO `x` VALU|
00000540  45 53 20 28 31 2c 27 78  78 78 c3 bc 27 29 2c 28  |ES (1,'xxx..'),(|
00000550  32 2c 27 79 79 79 c3 83  c2 a4 27 29 3b 0a 2f 2a  |2,'yyy....');./*|

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Добавление? Use_utf8 = 0 к URL БД решает проблему.Обнаружил, что в документах по SQLAlchemy.

0 голосов
/ 27 ноября 2018

c3 83 c2 a4 - это «двойное кодирование» для ä.как указывает Илья.Это обсуждается далее здесь

http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases обеспечивает UPDATE для исправления данных.

Вот контрольный списоквещи, которые, возможно, должны быть исправлены в вашем Python: http://mysql.rjweb.org/doc.php/charcoll#python

Но это страшно: я вижу c3 bc (Моджибаке для ü) и c3 83 c2 a4 (дваждыкодирование ä. Это означает, что у вас две разные проблемы , возникающие в одном и том же коде. Вернитесь к первому нулю, убедитесь, что вы используете utf8 (или utf8mb4) на всех этапах. Ваша база данныхможет быть слишком испорчен, чтобы восстановиться, поэтому подумайте о том, чтобы начать заново.

Возможно, единственная проблема заключается в отсутствии # -*- encoding: utf8 -*- из одного скриптов Python. Но, нет. Вы нужно нужно, но двойное кодирование произошло, когда вы его использовали.

Итог: у вас есть несколько ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...