использование pyodbc в linux для вставки символов unicode или utf-8 в поле nvarchar mssql - PullRequest
21 голосов
/ 04 июня 2009

Я использую Ubuntu 9.04

Я установил следующие версии пакета:

unixodbc and unixodbc-dev: 2.2.11-16build3
tdsodbc: 0.82-4
libsybdb5: 0.82-4
freetds-common and freetds-dev: 0.82-4

Я настроил /etc/unixodbc.ini так:

[FreeTDS]
Description             = TDS driver (Sybase/MS SQL)
Driver          = /usr/lib/odbc/libtdsodbc.so
Setup           = /usr/lib/odbc/libtdsS.so
CPTimeout               = 
CPReuse         = 
UsageCount              = 2

Я настроил /etc/freetds/freetds.conf так:

[global]
    tds version = 8.0
    client charset = UTF-8

Я взял pyodbc ревизию 31e2fae4adbf1b2af1726e5668a3414cf46b454f из http://github.com/mkleehammer/pyodbc и установил ее, используя "python setup.py install"

У меня машина Windows с Microsoft SQL Server 2000 , установленной в моей локальной сети, и прослушивание по локальному IP-адресу 10.32.42.69. У меня есть пустая база данных, созданная с именем «Common». У меня есть пользователь "sa" с паролем "secret" с полными привилегиями.

Я использую следующий код Python для настройки соединения:

import pyodbc
odbcstring = "SERVER=10.32.42.69;UID=sa;PWD=secret;DATABASE=Common;DRIVER=FreeTDS"
con = pyodbc.connect(s)
cur = con.cursor()
cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    name NVARCHAR(200) NULL, 
    PRIMARY KEY (id)
)
    ''')
con.commit()

Все РАБОТАЕТ до этого момента. Я использовал SQLServer Enterprise Manager на сервере, и новая таблица там. Теперь я хочу вставить некоторые данные в таблицу.

cur = con.cursor()
cur.execute('INSERT INTO testing (name) VALUES (?)', (u'something',))

Это не удается !! Вот ошибка, которую я получаю:

pyodbc.Error: ('HY004', '[HY004] [FreeTDS][SQL Server]Invalid data type 
(0) (SQLBindParameter)'

Поскольку мой клиент настроен на использование UTF-8, я подумал, что смогу решить проблему путем кодирования данных в UTF-8. Это работает, но потом я получаю странные данные:

cur = con.cursor()
cur.execute('DELETE FROM testing')
cur.execute('INSERT INTO testing (name) VALUES (?)', (u'somé string'.encode('utf-8'),))
con.commit()
# fetching data back
cur = con.cursor()
cur.execute('SELECT name FROM testing')
data = cur.fetchone()
print type(data[0]), data[0]

Это не дает ошибки, но возвращаемые данные не совпадают с отправленными! Я получаю:

<type 'unicode'> somé string

То есть pyodbc не принимает объект Unicode напрямую, но возвращает объекты Unicode мне! И кодировка путается!

Теперь на вопрос:

Я хочу, чтобы код вставлял данные Unicode в поле NVARCHAR и / или NTEXT. Когда я делаю запрос назад, мне нужны те же данные, которые я вставил обратно.

Это может быть сделано путем другой конфигурации системы или использования функции-оболочки, способной правильно преобразовывать данные в / из Юникода при вставке или извлечении

Это не много спрашивает, не так ли?

Ответы [ 4 ]

21 голосов
/ 08 июня 2009

Я помню такие глупые проблемы с использованием драйверов odbc, даже если в то время это была комбинация java + oracle.

Суть в том, что драйвер odbc, по-видимому, кодирует строку запроса при отправке ее в БД. Даже если поле имеет Unicode и если вы предоставляете Unicode, в некоторых случаях это не имеет значения.

Необходимо убедиться, что отправляемое драйвером имеет ту же кодировку, что и ваша база данных (не только сервер, но и база данных). В противном случае, конечно, вы получите забавные символы, потому что либо клиент, либо сервер смешивают вещи при кодировании / или декодировании. Есть ли у вас какие-либо представления о кодировке (кодовая точка, которую MS хотел бы сказать), которую ваш сервер использует по умолчанию для декодирования данных?

Сличение не имеет ничего общего с этой проблемой:)

См. на этой странице MS , например. Для полей Unicode сортировка используется только для определения порядка сортировки в столбце, не , чтобы указать, как хранятся данные.

Если вы храните свои данные в формате Unicode, существует уникальный способ их представления, это цель Unicode: нет необходимости определять кодировку, совместимую со всеми языками, которые вы собираетесь использовать :)

Вопрос здесь «что происходит, когда я передаю данные на сервер, который не Unicode?». Например:

  • Когда я отправляю на сервер строку UTF-8, как она это понимает?
  • Когда я отправляю на сервер строку UTF-16, как она это понимает?
  • Когда я отправляю строку Latin1 на сервер, как она это понимает?

С точки зрения сервера, все эти 3 строки представляют собой только поток байтов. Сервер не может угадать кодировку, в которой вы их кодировали. Это означает, что у вас будут проблемы, если ваш клиент odbc в конечном итоге отправит на сервер bytestrings (закодированную строку) вместо отправки unicode данных: если вы это сделаете Итак, сервер будет использовать предопределенную кодировку (это был мой вопрос: какую кодировку будет использовать сервер? Поскольку это не угадывание, это должно быть значение параметра), и если строка была закодирована с использованием другой кодировки, dzing , данные будут повреждены.

Это точно так же, как в Python:

uni = u'Hey my name is André'
in_utf8 = uni.encode('utf-8')
# send the utf-8 data to server
# send(in_utf8)

# on server side
# server receives it. But server is Japanese.
# So the server treats the data with the National charset, shift-jis:
some_string = in_utf8 # some_string = receive()    
decoded = some_string.decode('sjis')

Просто попробуйте. Это весело. Предполагается, что декодированная строка «Эй, меня зовут Андре», но «Эй, меня зовут Андр テ テ». заменяется на японский テ ゥ

Отсюда мое предложение: вам нужно убедиться, что pyodbc может напрямую отправлять данные в формате Unicode. Если pyodbc этого не сделает, вы получите неожиданные результаты.

И я описал проблему в пути от клиента к серверу. Но такие же проблемы могут возникать при обратной связи с сервера клиенту. Если Клиент не может понять данные Unicode, вы, вероятно, столкнетесь с проблемами.

FreeTDS обрабатывает Unicode для вас.

На самом деле FreeTDS позаботится о вас и переведет все данные в кодировку UCS2. ( Источник ).

  • Сервер <-> FreeTDS: данные UCS2
  • FreeTDS <-> pyodbc: закодированные строки, закодированные в UTF-8 (из /etc/freetds/freetds.conf)

Так что я ожидаю, что ваше приложение будет работать правильно, если вы передадите данные UTF-8 в pyodbc. На самом деле, как говорится в django-pyodbc билете, django-pyodbc взаимодействует в UTF-8 с pyodbc, так что с вами все будет в порядке.

FreeTDS 0.82

Однако, cramm0 говорит, что FreeTDS 0.82 не является полностью безглючной, и что существуют значительные различия между 0.82 и официальной исправленной версией 0.82, которую можно найти здесь . Возможно, вам следует попробовать использовать пропатченную FreeTDS


Отредактировано : удалены старые данные, которые не имели ничего общего с FreeTDS, но относились только к коммерческому драйверу Easysoft odbc. К сожалению.

2 голосов
/ 05 июня 2009

Я использую UCS-2 для взаимодействия с SQL Server, а не UTF-8.

Исправление: я изменил запись .freetds.conf, чтобы клиент использовал UTF-8

    tds version = 8.0
    client charset = UTF-8
    text size = 32768

Теперь значения связывания работают нормально для строк в кодировке UTF-8. Драйвер осуществляет прозрачное преобразование между UCS-2, используемым для хранения на стороне сервера данных, и закодированными в UTF-8 строками, передаваемыми / полученными от клиента.

Это с pyodbc 2.0 на Solaris 10 с Python 2.5 и FreeTDS freetds-0.82.1.dev.20081111 и SQL Server 2008


import pyodbc
test_string = u"""Comment ça va ? Très bien ?"""

print type(test_string),repr(test_string)
utf8 = 'utf8:' + test_string.encode('UTF-8')
print type(utf8), repr(utf8)

c = pyodbc.connect('DSN=SA_SQL_SERVER_TEST;UID=XXX;PWD=XXX')

cur = c.cursor()
# This does not work as test_string is not UTF-encoded
try: 
    cur.execute('INSERT unicode_test(t) VALUES(?)', test_string)
    c.commit()
except pyodbc.Error,e:
    print e


# This one does:
try:
    cur.execute('INSERT unicode_test(t) VALUES(?)', utf8)
    c.commit()
except pyodbc.Error,e:
    print e    


Вот вывод из тестовой таблицы (я вручную поместил кучу тестовых данных через Management Studio)

In [41]: for i in cur.execute('SELECT t FROM unicode_test'):
   ....:     print i
   ....:
   ....:
('this is not a banana', )
('\xc3\x85kergatan 24', )
('\xc3\x85kergatan 24', )
('\xe6\xb0\xb4 this is code-point 63CF', )
('Mich\xc3\xa9l', )
('Comment a va ? Trs bien ?', )
('utf8:Comment \xc3\xa7a va ? Tr\xc3\xa8s bien ?', )

Мне удалось вставить некоторые кодовые точки Unicode непосредственно в таблицу из Management Studio с помощью диалогового окна «Редактировать верхние 200 строк» ​​и ввести шестнадцатеричные цифры для кодовой точки Unicode, а затем нажать Alt-X

1 голос
/ 28 октября 2011

У меня была такая же проблема при попытке связать параметр Unicode: '[HY004] [FreeTDS] [SQL Server] Недопустимый тип данных (0) (SQLBindParameter)'

Я решил это, обновив freetds до версии 0.91.

Я использую pyodbc 2.1.11. Мне пришлось применить этот патч, чтобы он работал с юникодом, иначе я иногда получал ошибки повреждения памяти.

0 голосов
/ 15 июня 2009

Вы уверены, что именно INSERT вызывает проблемы с чтением? На pyodbc открыта ошибка при получении данных NTEXT и NVARCHAR .

...