2009-06-29 10 views
7

ich Ubuntu 9.04mit Pyodbc auf ubuntu ein Bildfeld auf SQL Server einfügen

Ich habe die folgenden Paketversionen installiert bin mit:

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 

ich /etc/unixodbc.ini wie diese konfiguriert haben:

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

Ich habe /etc/freetds/freetds.conf wie folgt konfiguriert:

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

Ich habe Pyodbc Revision packte 31e2fae4adbf1b2af1726e5668a3414cf46b454f von http://github.com/mkleehammer/pyodbc und installierte es auf meinem lokalen Netzwerk „python setup.py install

Ich habe einen Windows-Rechner mit Microsoft SQL Server 2000 installiert wird, und auf der lokalen IP-Hören Adresse 10.32.42.69. Ich habe eine leere Datenbank mit dem Namen "Common" erstellt. Ich habe den Benutzer "sa" mit Passwort "geheim" mit vollen Rechten.

Ich bin mit dem folgenden Python-Code zum Einrichten der Verbindung:

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

Alles bis zu diesem Punkt funktioniert. Ich habe SQL Server Enterprise Manager auf dem Server verwendet und die neue Tabelle ist da. Jetzt möchte ich einige Daten in die Tabelle einfügen.

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

Jetzt hier auf meiner ursprünglichen Frage war ich Probleme, cur.execute(sql, (data,)) verwenden, aber jetzt habe ich die Frage bearbeitet, weil folgende Vinay Sajip Antwort unten (DANKE), es es geändert haben:

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

Und Einfügung funktioniert einwandfrei. Ich kann die Größe der eingefügten Daten mit dem folgenden Testcode bestätigen:

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

Welche perfekt geht !!!

Jetzt ist das Problem beim Abrufen der Daten zurück.

ich den gemeinsamen Ansatz versuchen:

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 

jedoch, die nicht !!

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 

Ich habe in einer einzigen Datei über die gesamten Code setzen here, für die einfache Prüfung von jemandem, der helfen will.

Nun zur Frage:

Ich möchte Python-Code eine Bilddatei in mssql einzufügen. Ich möchte das Bild zurückfragen und es dem Benutzer zeigen.

Ich interessiere mich nicht für den Spaltentyp in mssql.Ich verwende den Spaltentyp "IMAGE" im Beispiel, aber irgendein binärer/blob Typ würde tun, solange ich die binären Daten für die Datei bekomme, die ich unberührt eingefügt habe. Vinay Sajip sagte unten, dass dies der bevorzugte Datentyp dafür in SQL SERVER 2000 ist.

Die Daten werden jetzt ohne Fehler eingefügt, aber wenn ich die Daten abrufe, werden nur 4k zurückgegeben. (Daten werden bei 4096 abgeschnitten).

Wie kann ich das machen?


EDITS: Vinay Sajip Antwort gab mir unter einen Hinweis pyodbc.Binary auf dem Feld zu verwenden. Ich habe die Frage entsprechend aktualisiert. Danke Vinay Sajip!

Alex Martellis Kommentar gab mir die Idee, die MS SQL-Funktion DATALENGTH zu verwenden, um zu testen, ob die Daten vollständig in der Spalte geladen sind. Danke Alex Martelli!

+0

was erhalten Sie, wenn Sie "DATALENGTH (myimage) FROM testing" wählen? Zumindest wird Ihnen das sagen, ob Ihr Problem beim Speichern oder beim Abrufen liegt. –

Antwort

5

Huh, kurz nachdem ich das Kopfgeld angeboten habe, habe ich die Lösung gefunden.

Sie müssen SET TEXTSIZE 2147483647 für die Abfrage verwenden, zusätzlich zur Textgrößen-Konfigurationsoption in /etc/freetds/freetds.conf.

Ich habe

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

Und alles hat gut funktioniert.

Seltsam ist, was FreeTDS documentation says über die Textgröße Konfigurationsoption:

default value of TEXTSIZE , in bytes. For text and image datatypes, sets the maximum width of any returned column. Cf. set TEXTSIZE in the T-SQL documentation for your server.

Die Konfiguration sagt auch, dass der Maximalwert (und die Standardeinstellung) 4294967295 ist. Wenn ich jedoch versuche, diesen Wert in der Abfrage zu verwenden, bekomme ich einen Fehler, die maximale Anzahl, die ich in der Abfrage verwenden könnte, ist 2.147.483.647 (halb).

Von dieser Erklärung dachte ich, dass nur die Einstellung dieser Konfigurationsoption würde ausreichen. Es stellt sich heraus, dass ich mich geirrt habe, das Setzen von TEXTSIZE in der Abfrage hat das Problem behoben.

Unten ist der komplette Arbeitscode:

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

Ich glaube, Sie sollten eine pyodbc.Binary Beispiel werden unter Verwendung der Daten zu wickeln:

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

Apportieren

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

UPDATE sein sollte: Das Problem bei der Insertion ist. Ändern Sie Ihre Einfügung SQL auf die folgenden:

"""DECLARE @txtptr varbinary(16) 

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

Ich habe auch den Fehler, aktualisiert ich bei der Verwendung der Wertattribut in dem Abrufcode gemacht.

Mit dieser Änderung kann ich ein 320K JPEG-Bild in die Datenbank einfügen und abrufen (abgerufene Daten sind identisch mit eingefügten Daten).

N.B. Der image-Datentyp ist veraltet und wird in späteren Versionen von SQL Server durch varbinary(max) ersetzt. Für den neueren Spaltentyp sollte jedoch dieselbe Logik zum Einfügen/Abrufen gelten.

+0

Korrekt, obwohl ich str (cur.fetchall() [0] [0]) verwenden musste, um die Daten zu erhalten. .value zurückgegeben: AttributeError: 'Puffer' Objekt hat kein Attribut 'Wert' – nosklo

+0

Ich habe immer noch Probleme. Ich habe die Frage aktualisiert, bitte werfen Sie einen Blick darauf. – nosklo

+0

Thans Vinay für das Update. Stellt sich heraus, dass das Problem nicht in der Einfügung ist. Ich habe den TEXTPTR-Code ausprobiert und das gleiche Ergebnis erhalten. Vielen Dank für Ihre Zeit. Ich habe die Frage erneut bearbeitet, um das aktuelle Problem anzuzeigen. Kannst du mir den vollständigen Code geben, den du gesagt hast? Kannst du mir Details zu deiner Einrichtung geben? Ich kann es immer noch nicht schaffen. Wenn es nicht viel verlangt, können Sie bitte auch meinen Code unverändert auf Ihrem Setup ausführen und mir die Ergebnisse mitteilen? Ich habe es auf http://paste.pocoo.org/show/125955/ zur Verfügung gestellt und es benutzt eine Datei im Internet, so dass es genau so läuft. – nosklo

1

Ich hatte ein ähnliches 4096 Abschneiden Problem auf TEXT Feldern, die SET TEXTSIZE 2147483647 für mich fest, aber das es auch für mich festgelegt:

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