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
python2.6-dev

Je l'ai configuré comme ceci /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

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

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

Je l'ai attrapé la révision pyodbc de 31e2fae4adbf1b2af1726e5668a3414cf46b454f et installé http://github.com/mkleehammer/pyodbc en utilisant "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 tous les privilèges.

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(odbcstring)
cur = con.cursor()

cur.execute("""
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'testing')
   DROP TABLE testing
""")
cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    myimage IMAGE 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()
# using web data for exact reproduction of the error by all.
# I'm actually reading a local file in my real code.
url = 'http://www.forestwander.com/wp-content/original/2009_02/west-virginia-mountains.jpg'
data = urllib2.urlopen(url).read()

sql = "INSERT INTO testing (myimage) VALUES (?)"

Maintenant, voici ma question initiale, j'avais du mal à utiliser mais maintenant je cur.execute(sql, (data,)) ai modifié la question, car la réponse suivante de Vinay Sajip ci-dessous (MERCI), je l'ai changé à:

cur.execute(sql, (pyodbc.Binary(data),)) 
con.commit()

insertion fonctionne parfaitement . Je peux confirmer la taille des données insérées à l'aide du code de test suivant:

cur.execute('SELECT DATALENGTH(myimage) FROM testing WHERE id = 1')
data_inside = cur.fetchone()[0]
assert data_inside == len(data)

qui passe parfaitement !!!

Maintenant, le problème est sur la récupération des données en arrière.

Je suis en train de l'approche commune:

cur.execute('SELECT myimage FROM testing WHERE id = 1')
result = cur.fetchone()
returned_data = str(result[0]) # transforming buffer object
print 'Original: %d; Returned: %d' % (len(data), len(returned_data))
assert data == returned_data

Cependant cela échoue !!

Original: 4744611; Returned: 4096
Traceback (most recent call last):
  File "/home/nosklo/devel/teste_mssql_pyodbc_unicode.py", line 53, in <module>
    assert data == returned_data
AssertionError

J'ai mis tout le code ci-dessus dans un seul fichier , pour les tests faciles de toute personne qui veut aider.

Maintenant, pour la question suivante:

Je veux code python pour insérer un fichier image dans MSSQL. Je veux interroger l'image en arrière et montrer à l'utilisateur.

Je ne me soucie pas du type de colonne dans MSSQL. J'utilise le type de colonne « IMAGE » sur l'exemple, mais tout type binaire / blob faire, aussi longtemps que je reçois les données binaires pour le fichier que je réinséré intacte. Vinay Sajip dit ci-dessous que ce soit le type de données préférée pour cela dans SQL Server 2000.

Les données sont maintenant insérées sans erreur, mais lorsque je récupère les données, seulement 4k sont retournés. (Les données sont tronquées sur 4096).

Comment puis-je faire ce travail?


EDITS : la réponse de Vinay Sajip ci-dessous m'a donné un indice d'utiliser pyodbc.Binary sur le terrain. Je l'ai mis à jour en conséquence la question. Merci Vinay Sajip!

Commentaire de Alex Martelli m'a donné l'idée d'utiliser la fonction MS SQL pour tester DATALENGTH si les données sont entièrement chargé sur la colonne. Merci Alex Martelli!

Était-ce utile?

La solution

Huh, juste après avoir offert la générosité, j'ai trouvé la solution.

Vous devez utiliser sur la requête SET TEXTSIZE 2147483647, en plus de l'option de configuration de la taille du texte dans /etc/freetds/freetds.conf.

Je l'ai utilisé

cur.execute('SET TEXTSIZE 2147483647 SELECT myimage FROM testing WHERE id = 1')

Et tout a bien fonctionné.

documentation FreeTDS dit sur l'option de configuration de la taille du texte:

  

valeur par défaut de TEXTSIZE, en octets. Pour text et image types de données, définit la largeur maximale d'une colonne de retour. Cf. Dans la documentation set TEXTSIZE pour votre serveur T-SQL.

La configuration indique également que la valeur maximale (et la valeur par défaut) est 4294967295. Toutefois, lorsque vous essayez d'utiliser cette valeur dans la requête que je reçois une erreur, le nombre maximum que je pouvais utiliser dans la requête est 2147483647 (la moitié).

De cette explication, je pensais que la mise en seule cette option de configuration serait suffisant. Il se trouve que je me trompais, la mise en TEXTSIZE dans la requête fixe la question.

est sous le code de travail complet:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyodbc
import urllib2

odbcstring = "SERVER=10.32.42.69;UID=sa;PWD=secret;DATABASE=Common;DRIVER=FreeTDS"
con = pyodbc.connect(odbcstring)
cur = con.cursor()

cur.execute("""
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'testing')
   DROP TABLE testing
""")

cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    myimage IMAGE NULL,
    PRIMARY KEY (id)
)
    ''')

con.commit()
cur = con.cursor()
url = 'http://www.forestwander.com/wp-content/original/2009_02/west-virginia-mountains.jpg'
data = urllib2.urlopen(url).read()

sql = "INSERT INTO testing (myimage) VALUES (?)"
cur.execute(sql, (pyodbc.Binary(data),))
con.commit()

cur.execute('SELECT DATALENGTH(myimage) FROM testing WHERE id = 1')
data_inside = cur.fetchone()[0]
assert data_inside == len(data)

cur.execute('SET TEXTSIZE 2147483647 SELECT myimage FROM testing WHERE id = 1')
result = cur.fetchone()
returned_data = str(result[0])
print 'Original: %d; Returned; %d' % (len(data), len(returned_data))
assert data == returned_data

Autres conseils

Je pense que vous devriez utiliser un exemple pour envelopper pyodbc.Binary les données:

cur.execute('INSERT INTO testing (myimage) VALUES (?)', (pyodbc.Binary(data),))

Récupération doit être

cur.execute('SELECT myimage FROM testing')
print "image bytes: %r" % str(cur.fetchall()[0][0])

UPDATE: Le problème est en insertion. Modifier votre insertion SQL sur les points suivants:

"""DECLARE @txtptr varbinary(16)

INSERT INTO testing (myimage) VALUES ('')
SELECT @txtptr = TEXTPTR(myimage) FROM testing 
WRITETEXT testing.myimage @txtptr ?
"""

Je suis aussi mis à jour l'erreur que je fait en utilisant l'attribut de valeur dans le code de récupération.

Avec ce changement, je suis en mesure d'insérer et d'extraire une image JPEG 320K dans la base de données (données récupérées est identique aux données insérées).

N.B.. Le type de données image est dépréciée, et est remplacé par dans les versions ultérieures varbinary(max) de SQL Server. La même logique pour l'insertion / extraction doit appliquer, cependant, pour le type de colonne plus récente.

J'ai eu un problème de troncature similaire sur 4096 champs qui TEXT fixés pour moi SET TEXTSIZE 2147483647, mais il fixe aussi pour moi:

import os
os.environ['TDSVER'] = '8.0'
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top