Pergunta

estou usando Ubuntu 9.04

Eu instalei as seguintes versões do pacote:

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

Eu configurei /etc/unixodbc.ini assim:

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

Eu configurei /etc/freetds/freetds.conf assim:

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

Eu peguei a revisão do Pyodbc 31e2fae4adbf1b2af1726e5668a3414cf46b454f a partir de http://github.com/mkleehammer/pyodbc e instalou -o usando "python setup.py install"

Eu tenho uma máquina Windows com Microsoft SQL Server 2000 Instalado na minha rede local, para cima e ouvindo o endereço IP local 10.32.42.69. Eu tenho um banco de dados vazio criado com o nome "Common". Eu tenho o usuário "SA" com senha "segredo" com privilégios completos.

Estou usando o seguinte código Python para configurar a conexão:

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

Tudo FUNCIONA até este ponto. Eu usei o Enterprise Manager do SQLServer no servidor e a nova tabela está lá. Agora eu quero inserir alguns dados na tabela.

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

Agora aqui na minha pergunta original, eu estava tendo problemas para usar cur.execute(sql, (data,)) Mas agora eu editei a pergunta, porque após a resposta de Vinay Sajip abaixo (obrigado), eu mudei para:

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

E inserção está funcionando perfeitamente. Posso confirmar o tamanho dos dados inseridos usando o seguinte código de teste:

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

Que passa perfeitamente!!!

Agora, o problema está na recuperação dos dados de volta.

Estou tentando a abordagem comum:

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

No entanto, isso falha !!

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

Eu coloquei todo o código acima em um único arquivo aqui, para facilitar o teste de qualquer pessoa que queira ajudar.

Agora para a pergunta:

Eu quero o código Python para inserir um arquivo de imagem no MSSQL. Quero consultar a imagem de volta e mostro ao usuário.

Eu não me importo com o tipo de coluna no MSSQL. Eu estou usando o "IMAGE"Tipo de coluna no exemplo, mas qualquer tipo binário/blob faria, desde que eu receba os dados binários para o arquivo que inseri de volta intocados. Vinay Sajip disse abaixo que este é o tipo de dados preferido para isso no SQL Server 2000.

Os dados agora estão sendo inseridos sem erros; no entanto, quando recuperar os dados, apenas 4K são retornados. (Os dados são truncados em 4096).

Como posso fazer isso funcionar?


EDITAR% S: A resposta de Vinay Sajip abaixo me deu uma dica de usar pyodbc.binary em campo. Eu atualizei a pergunta de acordo. Obrigado Vinay Sajip!

O comentário de Alex Martelli me deu a ideia de usar o DATALENGTH MS SQL Função para testar se os dados estão totalmente carregados na coluna. Obrigado Alex Martelli!

Foi útil?

Solução

Huh, logo após oferecer a recompensa, descobri a solução.

Você tem que usar SET TEXTSIZE 2147483647 Na consulta, além da opção de configuração de tamanho de texto em /etc/freetds/freetds.conf.

Eu tenho usado

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

E tudo funcionou bem.

Strange é o quê A documentação de Freetds diz Sobre a opção de configuração do tamanho do texto:

valor padrão de TEXTSIZE, em bytes. Por text e image Datatipos, define a largura máxima de qualquer coluna retornada. Cf. set TEXTSIZE no T-SQL documentação para o seu servidor.

A configuração também diz que o valor máximo (e o padrão) é de 4.294.967.295. No entanto, ao tentar usar esse valor na consulta, recebo um erro, o número máximo que eu poderia usar na consulta é de 2.147.483.647 (metade).

A partir dessa explicação, pensei que apenas definir essa opção de configuração seria suficiente. Acontece que eu estava errado, definindo o texto na consulta corrigiu o problema.

Abaixo está o código de trabalho completo:

#!/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

Outras dicas

Eu acho que você deveria estar usando um pyodbc.Binary instância para envolver os dados:

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

Recuperar deve ser

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

ATUALIZAR: O problema está em inserção. Altere sua inserção SQL para o seguinte:

"""DECLARE @txtptr varbinary(16)

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

Também atualizei o erro que cometi ao usar o atributo de valor no código de recuperação.

Com essa alteração, sou capaz de inserir e recuperar uma imagem JPEG de 320k no banco de dados (os dados recuperados são idênticos aos dados inseridos).

Nb o image O tipo de dados é depreciado e é substituído por varbinary(max) em versões posteriores do SQL Server. A mesma lógica para inserção/recuperação deve ser aplicada, no entanto, para o tipo de coluna mais recente.

Eu tive um semelhante 4096 Problema de truncamento em TEXT campos, que SET TEXTSIZE 2147483647 consertado para mim, mas isso também consertou para mim:

import os
os.environ['TDSVER'] = '8.0'
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top