en utilisant pyodbc sur Linux pour insérer unicode ou UTF-8 caractères dans un champ nvarchar mssql

StackOverflow https://stackoverflow.com/questions/947077

Question

J'utilise Ubuntu 9.04

J'ai installé les versions de package suivant:

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

Je l'ai configuré /etc/unixodbc.ini comme ceci:

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

Je l'ai configuré /etc/freetds/freetds.conf comme ceci:

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

Je l'ai attrapé 31e2fae4adbf1b2af1726e5668a3414cf46b454f de révision pyodbc de http://github.com/mkleehammer/pyodbc et installé à l'aide de "python setup.py install"

J'ai une machine Windows avec Microsoft SQL Server 2000 installé sur mon réseau local, et l'écoute sur l'adresse IP locale 10.32.42.69. J'ai une base de données vide créé avec le nom « commun ». Je l'utilisateur « sa » mot de passe « secret » avec plein priviledges.

Je suis en utilisant le code Python ci-dessous pour configurer la connexion:

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

Tout WORKS jusqu'à ce point. Je l'ai utilisé Enterprise Manager de SQL Server sur le serveur et la nouvelle table est là. Maintenant, je veux insérer des données sur la table.

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

qui ne !! Voici l'erreur que je reçois:

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

Depuis que mon client est configuré pour utiliser UTF-8 je pensais que je pouvais résoudre par le codage des données en UTF-8. Cela fonctionne, mais je récupérer des données étranges:

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]

Cela donne aucune erreur, mais les données renvoyées ne sont pas les mêmes données envoyées! Je reçois:

<type 'unicode'> somé string

C'est, pyodbc n'acceptera pas un objet unicode directement, mais il renvoie des objets unicode vers moi! Et l'encodage est mélangé!

Maintenant, pour la question suivante:

Je veux code à insérer des données unicode dans un NVARCHAR et / ou sur le terrain NTEXT. Quand je fais une recherche en arrière, je veux les mêmes données que je réinséré.

Cela peut être en configurant différemment le système, ou en utilisant une fonction wrapper capable de convertir les données correctement / de unicode lors de l'insertion ou la récupération

Ce ne demande pas grand-chose, est-il?

Était-ce utile?

La solution

Je me souviens d'avoir ce genre de problèmes stupides en utilisant les pilotes odbc, même si ce temps était une combinaison java + oracle.

La chose principale est que le pilote odbc code apparemment la chaîne de requête lors de l'envoi à la DB. Même si le champ est Unicode, et si vous Unicode, dans certains cas, il ne semble pas à la matière.

Vous devez vous assurer que ce qui est envoyé par le conducteur a le même encodage que votre base de données (non seulement serveur, mais aussi la base de données). Sinon, bien sûr, vous obtenez des caractères funky parce que le client ou le serveur est le mélange des choses lorsque l'encodage / ou le décodage. Avez-vous une idée du jeu de caractères (codepoint que MS voulez dire) que votre serveur utilise par défaut pour le décodage des données?

Collation n'a rien à voir avec ce problème:)

Voir cette page MS par exemple. Pour les champs Unicode, le classement est uniquement utilisé pour définir l'ordre de tri dans la colonne, pas pour spécifier la façon dont les données sont stockées.

Si vous stockez vos données en Unicode, il existe un moyen unique pour le représenter, c'est le but d'Unicode: pas besoin de définir un jeu de caractères qui est compatible avec toutes les langues que vous allez utiliser :)

La question ici est « ce qui se passe quand je donne des données au serveur qui est pas Unicode? ». Par exemple:

  • Lorsque j'envoie une chaîne UTF-8 sur le serveur, comment faut-il comprendre?
  • Lorsque j'envoie une chaîne UTF-16 au serveur, comment faut-il comprendre?
  • Lorsque j'envoie une chaîne Latin1 au serveur, comment faut-il comprendre?

Du point de vue du serveur, toutes ces 3 chaînes ne sont que d'un flux d'octets. Le serveur ne peut pas deviner l'encodage dans lequel vous les encodées. Ce qui signifie que vous get problèmes si votre client odbc finit par envoyer Les chaînes d'octets (une chaîne codée) au serveur au lieu d'envoyer unicode données: si vous le faites, le serveur utilisera un codage prédéfini (qui était ma question: quel encodage utilisé par le serveur comme il n'est pas deviner, il doit être une valeur de paramètre?), et si la chaîne avait été codé en utilisant un codage différent , Dzing , les données sont endommagés.

Il est exactement similaire à faire 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')

Il suffit de l'essayer. C'est marrant. La chaîne décodée est censé être « Hey mon nom est André », mais est « Hey mon nom est Andr テ ゥ ». é remplacé par テ obtient japonais ゥ

D'où ma suggestion: vous devez vous assurer que pyodbc est en mesure d'envoyer directement les données Unicode. Si pyodbc ne parvient pas à faire cela, vous obtiendrez des résultats inattendus.

Et je l'ai décrit le problème dans le client du mode serveur. Mais le même genre de problèmes peuvent surgir lors de la communication à partir du serveur au client. Si le client ne peut pas comprendre les données Unicode, vous aurez probablement entrer dans des problèmes.

FreeTDS poignées Unicode pour vous.

En fait, FreeTDS prend soin des choses pour vous et traduit toutes les données à UCS2 unicode. ( source).

  • Serveur <-> FreeTDS: données UCS2
  • FreeTDS <-> pyodbc: chaînes codées, codées en UTF-8 (de /etc/freetds/freetds.conf)

Je me attends à votre application fonctionne correctement si vous passez données UTF-8 à pyodbc. En fait, comme les États de cette billet django-pyodbc, django-pyodbc communique en UTF-8 avec pyodbc, donc vous devriez être bien.

FreeTDS 0,82

Cependant, cramm0 dit que FreeTDS 0,82 est pas complètement exempt de bogues, et qu'il existe des différences significatives entre 0,82 et le fonctionnaire patché 0,82 version qui peut être trouvé ici . Vous devriez probablement essayer d'utiliser les FreeTDS patchés


Edité : retiré les anciennes données, qui ont rien à voir avec FreeTDS mais ne concernait que easysoft pilote odbc commercial. Désolé.

Autres conseils

J'utilise UCS-2 pour interagir avec SQL Server, pas UTF-8.

Correction: J'ai changé l'entrée de .freetds.conf afin que le client utilise UTF-8

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

Maintenant, les valeurs de liaison fonctionnent bien pour les chaînes codées UTF-8. Le pilote convertit de façon transparente entre les UCS-2 utilisés pour le stockage sur le côté de serveur de données et les chaînes codées UTF-8 données à / prélevés par le client.

est avec pyodbc 2.0 sur Solaris 10 en cours d'exécution Python 2.5 et FreeTDS freetds-0.82.1.dev.20081111 et 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    


Voici la sortie de la table de test (j'avais mis la main dans un tas de données de test via 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 ?', )

J'ai pu mettre dans certains points de code unicode directement dans la table de Management Studio par le « Edit Top 200 lignes » dialogue et en saisissant les chiffres hexadécimaux pour le point de code unicode puis en appuyant sur Alt-X

J'ai eu le même problème en essayant de lier le paramètre unicode: '[HY004] [FreeTDS] [SQL Server] type de données non valide (0) (SQLBindParameter)

Je l'ai résolu par la mise à niveau freetds à la version 0.91.

J'utilise pyodbc 2.1.11. Je devais appliquer ce patch pour le faire fonctionner avec unicode, sinon je recevais des erreurs de corruption de mémoire de temps en temps.

Êtes-vous sûr qu'il est INSERT qui est problème causant ne pas lire? Il y a un bug ouvert sur pyodbc problème et la récupération des données NTEXT NVARCHAR .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top