Pregunta

Estoy utilizando Ubuntu 9.04

He instalado las versiones del paquete siguiente:

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

He configurado /etc/unixodbc.ini como esto:

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

He configurado /etc/freetds/freetds.conf como esto:

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

He agarrado 31e2fae4adbf1b2af1726e5668a3414cf46b454f revisión pyodbc de http://github.com/mkleehammer/pyodbc e instalado usando "python setup.py install"

Tengo una máquina de las ventanas con Microsoft SQL Server 2000 instalado en mi red local, y escucha en la dirección IP 10.32.42.69 local. Tengo una base de datos vacía creada con el nombre "común". Tengo el usuario "sa" con la contraseña "secreto" con privilegios completos.

Estoy utilizando el siguiente código Python para configurar la conexión:

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()

Todo Trabajos hasta este punto. He utilizado el Administrador corporativo de SQL Server en el servidor y la nueva tabla que está allí. Ahora quiero insertar algunos datos sobre la mesa.

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

Esa falla !! Aquí está el error que consigo:

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

Desde mi cliente está configurado para utilizar UTF-8 que pensé que podía resolver mediante la codificación de los datos a UTF-8. Eso funciona, pero cuando me siento datos de nuevo extraño:

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]

Eso le da ningún error, pero los datos devueltos se envía no los mismos datos! Me sale:

<type 'unicode'> somé string

Es decir, pyodbc no aceptará un objeto Unicode directamente, sino que devuelve objetos Unicode de nuevo a mí! Y la codificación está siendo mezclado!

Ahora la pregunta:

Quiero código para insertar datos Unicode en un NVARCHAR y / o campo NTEXT. Cuando consulto hacia atrás, quiero los mismos datos Inserté espalda.

Eso puede ser mediante la configuración del sistema de forma diferente, o usando una función de contenedor capaz de convertir los datos correctamente a / de Unicode al insertar o recuperar

Eso no es pedir mucho, ¿verdad?

¿Fue útil?

Solución

Yo recuerdo haber tenido este tipo de problemas estúpidos utilizando controladores ODBC, incluso si esa vez fue una combinación de Java + Oracle.

Lo fundamental es que al parecer controlador ODBC codifica la cadena de consulta cuando se envía a la base de datos. Incluso si el campo es Unicode, y si proporciona Unicode, no parece en algunos casos a la materia.

Es necesario asegurarse de que lo que se envía por el conductor tiene la misma codificación que su base de datos (no sólo servidor, sino también la base de datos). De lo contrario, por supuesto, se obtiene caracteres extraños, porque el cliente o el servidor está mezclando las cosas cuando la codificación / decodificación o. ¿Tiene alguna idea del juego de caracteres (punto de código como MS gusta decir) que el servidor utiliza como valor predeterminado para decodificar los datos?

intercalación no tiene nada que ver con este problema:)

esa página MS por ejemplo. Para los campos de Unicode, el cotejo se utiliza sólo para definir el orden de clasificación en la columna, no para especificar cómo se almacenan los datos.

Si almacena los datos como Unicode, hay una manera única para representarla, ese es el propósito de Unicode: no hay necesidad de definir un conjunto de caracteres que sea compatible con todos los idiomas que se van a utilizar :)

La pregunta aquí es "lo que sucede cuando doy los datos al servidor que es no Unicode?". Por ejemplo:

  • Cuando envío una cadena UTF-8 en el servidor, ¿cómo se entiende?
  • Cuando envío una cadena UTF-16 con el servidor, ¿cómo se entiende?
  • Cuando envío una cadena Latin1 al servidor, ¿cómo se entiende?

Desde la perspectiva del servidor, todos estos 3 cadenas son solamente un flujo de bytes. El servidor no puede adivinar la codificación en el que se han codificado. Lo que significa que tener problemas si su cliente ODBC termina enviando a cadenas de bytes (una cadena codificada) al servidor en lugar de enviar Unicode de datos: si no es así, el servidor va a utilizar una codificación predefinida (que era mi pregunta: ¿qué codifica el servidor utilizará Dado que no es adivinar, debe ser un valor de parámetro?), y si la cadena se ha codificado utilizando una codificación diferente , dzing , los datos se corrompe.

Es exactamente similar a hacerlo en 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')

Haga la prueba. Es divertido. La cadena descodificada se supone que es "Hola mi nombre es André", pero es "Hola mi nombre es André テ ゥ". é es reemplazado por テ ゥ japonesa

De ahí mi sugerencia: es necesario asegurarse de que pyodbc es capaz de enviar directamente los datos como Unicode. Si pyodbc no lo hace, obtendrá resultados inesperados.

Y he descrito el problema en el cliente a modo de servidor. Pero el mismo tipo de problemas pueden surgir cuando se comunica de vuelta desde el servidor al cliente. Si el cliente no puede entender los datos Unicode, probablemente obtendrá en problemas.

FreeTDS maneja Unicode para usted.

En realidad, FreeTDS se ocupa de las cosas para usted y traduce todos los datos para UCS2 Unicode. ( Fuente ).

  • <-> Servidor: FreeTDS datos UCS2
  • FreeTDS <-> pyodbc: cuerdas codificados, codificados en UTF-8 (de /etc/freetds/freetds.conf)

Así que me gustaría esperar que su aplicación funcione correctamente si se pasa datos UTF-8 a pyodbc. De hecho, como afirma esta entradas django-pyodbc , django-pyodbc comunica en UTF-8 con pyodbc, por lo que debe estar bien.

FreeTDS 0.82

Sin embargo, cramm0 dice que FreeTDS 0,82 no es completamente libre de errores, y que existen diferencias significativas entre los 0,82 y el funcionario parcheado versión 0.82 que se pueden encontrar aquí . Probablemente debería tratar de usar los FreeTDS parcheado


Editado eliminado datos antiguos, que no tenían nada que ver con FreeTDS pero fue sólo es relevante para easySoft controlador ODBC comercial. Lo sentimos.

Otros consejos

utilizo UCS-2 para interactuar con SQL Server, no UTF-8.

Corrección: He cambiado la entrada .freetds.conf para que el cliente utiliza UTF-8

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

Ahora, los valores se unen a funcionar bien para cadenas UTF-8 codificado. El controlador convierte de forma transparente entre los UCS-2 utilizados para el almacenamiento en el lado dataserver y las cadenas UTF-8 codificados dadas a / tomadas desde el cliente.

Este es con pyodbc 2.0 en Solaris 10 ejecuta Python 2.5 y FreeTDS freetds-0.82.1.dev.20081111 y 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    


Aquí está la salida de la tabla de prueba (que había puesto manualmente en un montón de datos de prueba a través de 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 ?', )

Yo era capaz de poner en un cierto en los puntos de código Unicode directamente en la mesa de estudio de la gerencia por el 'Editar Top 200 filas' de diálogo e introducir los dígitos hexadecimales para el punto de código Unicode y pulsando Alt-X

Yo tenía el mismo problema cuando se trata de unir parámetro Unicode: '[HY004] [FreeTDS] [SQL Server] tipo no válido de datos (0) (SQLBindParameter)'

Lo resuelto por la actualización a la versión 0.91 freetds.

Yo uso pyodbc 2.1.11. Tenía que aplicar este parche para hacer que funcione con unicode, de lo contrario se producen errores de corrupción de memoria de vez en cuando.

¿Estás seguro de que es INSERT que está causando un problema no leer? Hay un insecto abierta en pyodbc Problema en la captura de datos NVARCHAR NTEXT y .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top