используя pyodbc на ubuntu для вставки поля изображения на SQL Server

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

Я настроил /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 text size = 4294967295 

Я схватил 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(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() 

Все работает до этого момента. Я использовал SQL Server Server Enterprise Manager на сервере, и там есть новая таблица. Теперь я хочу вставить некоторые данные в таблицу.

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

Теперь у меня возникли проблемы с использованием cur.execute(sql, (data,)) но теперь я отредактировал вопрос, потому что после ответа Винай Саджипа ниже (THANKS) я изменил его на:

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

Однако это не удается!

 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 

Я поместил весь код выше в один файл здесь , для легкого тестирования любого, кто хочет помочь.

Теперь на вопрос:

Я хочу, чтобы код python вставлял файл изображения в mssql. Я хочу запросить изображение и показать его пользователю.

Мне не нужен тип столбца в mssql. Я использую тип столбца « IMAGE » в примере, но любой тип двоичного / блочного будет делать, пока я получаю двоичные данные для файла, который я вставлял обратно в нетронутый. Vinay Sajip сказал ниже, что это предпочтительный тип данных для этого в SQL SERVER 2000.

Данные теперь вставляются без ошибок, однако, когда я извлекаю данные, возвращается только 4k. (Данные усекаются на 4096).

Как я могу сделать эту работу?


EDITS : ответ Винай Sajip ниже дал мне подсказку использовать pyodbc.Binary на поле. Я соответствующим образом обновил этот вопрос. Спасибо Vinay Sajip!

Комментарий Alex Martelli дал мне идею использования функции DATALENGTH MS SQL, чтобы проверить, полностью ли загружены данные в столбце. Спасибо, Алекс Мартелли!

Да, только после того, как я предложил щедрость, я нашел решение.

Вы должны использовать SET TEXTSIZE 2147483647 по запросу, в дополнение к опции настройки размера текста в /etc/freetds/freetds.conf .

я использовал

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

И все сработало хорошо.

Странно то, что документация FreeTDS говорит о параметре конфигурации размера текста:

значение по умолчанию TEXTSIZE , в байтах. Для типов данных с text и image задает максимальную ширину любого возвращаемого столбца. Ср set TEXTSIZE в документации T-SQL для вашего сервера.

В конфигурации также указано, что максимальное значение (и значение по умолчанию) составляет 4 294 967 295. Однако при попытке использовать это значение в запросе я получаю сообщение об ошибке, максимальное число, которое я мог бы использовать в запросе, составляет 2 147 483 647 (половина).

Из этого объяснения я думал, что достаточно установить этот вариант конфигурации. Оказывается, я ошибся, установив TEXTSIZE в запросе, исправил проблему.

Ниже приведен полный рабочий код:

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

Я думаю, вы должны использовать pyodbc.Binary экземпляр для pyodbc.Binary данных:

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

Извлечение должно быть

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

ОБНОВЛЕНИЕ: проблема заключается в вставке. Измените свой SQL-запрос вставки следующим образом:

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

Я также обновил ошибку, которую я сделал, используя атрибут value в коде поиска.

С этим изменением я могу вставить и извлечь изображение 320 КБ в базу данных (полученные данные идентичны вставленным данным).

NB Тип данных image устарел и заменен на varbinary(max) в более поздних версиях SQL Server. Тем не менее, для новой версии столбца должна применяться одна и та же логика для вставки / извлечения.

У меня была аналогичная проблема с урезанием 4096 на полях TEXT , которые SET TEXTSIZE 2147483647 установлены для SET TEXTSIZE 2147483647 , но это также исправило это для меня:

 import os os.environ['TDSVER'] = '8.0'