Подтвердить что ты не робот

Используя pyodbc на linux для вставки символов unicode или utf-8 в поле nvarchar mssql

Я использую 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

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

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

Я использую следующий код 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. Когда я возвращаю запрос, я хочу, чтобы те же данные, которые я вставлял обратно.

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Если вы сохраняете свои данные как 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 заботится о вас и переводит все данные в Unicode UCS2. (Источник).

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

Поэтому я ожидаю, что ваше приложение будет работать правильно, если вы передадите данные UTF-8 в pyodbc. Фактически, поскольку этот django-pyodbc ticket, django-pyodbc общается в UTF-8 с pyodbc, так что вы должны быть в порядке.

FreeTDS 0.82

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


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

Ответ 2

Я использую 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, используемым для хранения на стороне dataserver, и кодированными строками 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 в диалоговом окне "Edit Top 200 rows" и ввести шестнадцатеричные цифры для кодовой точки Unicode, а затем нажать Alt-X

Ответ 3

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

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

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