Subject: | DBD::ODBC fails to insert a large files (above 400kb) in VARBINARY column. |
Date: | Thu, 04 Sep 2008 14:02:38 +0300 |
To: | bug-DBD-ODBC [...] rt.cpan.org |
From: | Julian Lishev <julian.lishev [...] imperia.net> |
BUG:
---
DBD::ODBC fails to insert a large files (above 400kb) in VARBINARY(MAX)
column.
Description:
-----------
After installation of SQL Server 2005 (old SQL Server 2000 were removed
from the same machine) currently installed version of DBD::ODBC (1.14)
crashed Perl everytime I run my script.
After update to 1.15 (latest verion in ActiveState repository) script
stoped to crush with exception, but start waiting in never ending cycle
without doing nothing.
Using VS 2005 I've compiled ver 1.16 but not luck bug to disappear. The
same story with HEAD snapshot of SVN.
After short debuging I've found that SQLPutData throws the error (see
below what [ODBC SQL Server Driver] and [SQL Server] say about this
problem).
First bug fix without understanding why exactly this worked (according
MSDN documentation that shouldn't work) follows:
(see dbdimp.c.patch)
In other words, when you call SQLBindParameter() with parameter
"&phs->cbValue" that exceed 400k (a well-known limit, above which ODBC
based application should use SQLPutData) then an error is trhown.
However this change does not prevent SQL Server to insert the whole file
and it does NOT trunkate the data as expected(?!)
However PHP odbc module works (I wrote a small php script) properly and
insert large files in VARBINARY columns without any problems, so I've
downloaded PHP source code (ver 4.4.9) to skim for any differences in
implementation.
Second patch is inspirated from PHP code of ODBC module, where they
don't supply real length and use just 0:
(see dbdimp.c.patch2)
I think with this fix, ODBC works even faster (?!)
OS and soft details:
-------------------
Windows XP Professional (with latest MS updates);
Perl v5.10.0 built for MSWin32-x86-multi-thread;
DBD::ODBC: versions: 1.14, 1.15, 1.16 and current SVN snapshot.
Driver: SQL Server Version: 2000.85.1132.00 File: SQLSRV32.DLL Date:
4/14/2008
Driver Manager: Version 3.525.1132.0 C:\Windows\system32\odbc32.dll
SQL Server: tested with "2005 Standart" and "2005 Express" Edition.
Here is are table structure:
---------------------------
CREATE TABLE binary_meta (
doc_id INTEGER NOT NULL,
meta_name VARCHAR (255),
meta_value VARBINARY(MAX),
meta_idx INTEGER,
from_ver BIGINT,
to_ver BIGINT
)
Insert statement that fails:
---------------------------
INSERT into binary_meta VALUES (?, ?, ?, ?, ?, ?)
Selected parts from the log (error log level 5):
-----------------------------------------------
bind 3 <== 'PK...' (size cur=1726168/len=1726172/max=0, svtype 6, otype
1, sqltype -4)
bind 3 <== 'PK...' (len 1726168/1726170, null 0)
bind 3: type=-2, LONG VARBINARY, cs=1726168, dd=0, bl=1726168.
bind 3: type=-2, LONG VARBINARY, cs=1726168, dd=0, bl=1726168.
SQLBindParameter: idx = 3: param_type=1, name=3, value_type=-2, SQL_Type
= -4, column_size=1726168, d_digits=0, value_ptr=3a904c4,
buffer_length=1726168, cbValue = -1726268
dbd_st_execute (NEED DATA)...
!!dbd_error2(err_rc=-1, what=st_execute/SQLExecute,
handles=(28512a0,2851348,2857a40)
!SQLError(28512a0,2851348,2857a40) = (HY000, 0, [Microsoft][ODBC SQL
Server Driver]Warning: Partial insert/update. The insert/update of a
text or image column(s) did not succeed.)
!SQLError(28512a0,2851348,2857a40) = (42000, 7125, [Microsoft][ODBC
SQL Server Driver][SQL Server]The text, ntext, or image pointer value
conflicts with the column name specified.)
!! ERROR: 1 '[Microsoft][ODBC SQL Server Driver]Warning: Partial
insert/update. The insert/update of a text or image column(s) did not
succeed. (SQL-HY000) [state was HY000 now 42000]
[Microsoft][ODBC SQL Server Driver][SQL Server]The text, ntext, or image
pointer value conflicts with the column name specified. (SQL-42000)' (err#2)
P.S. Please confirm that you have received this mail
Regards,
Julian Lishev
Index: dbdimp.c
===================================================================
--- dbdimp.c (revision 11739)
+++ dbdimp.c (working copy)
@@ -3222,6 +3222,8 @@
* SQLPutData.
*/
SQLLEN vl = value_len;
+ if (value_type == SQL_C_BINARY && vl > 400*1024)
+ vl = 400*1024;
d_digits = 0; /* not relevant to lobs */
phs->cbValue = SQL_LEN_DATA_AT_EXEC(vl);
value_ptr = (UCHAR*) phs;
Index: dbdimp.c
===================================================================
--- dbdimp.c (revision 11739)
+++ dbdimp.c (working copy)
@@ -3223,7 +3223,7 @@
*/
SQLLEN vl = value_len;
d_digits = 0; /* not relevant to lobs */
- phs->cbValue = SQL_LEN_DATA_AT_EXEC(vl);
+ phs->cbValue = SQL_LEN_DATA_AT_EXEC(0);
value_ptr = (UCHAR*) phs;
}
@@ -3257,7 +3257,7 @@
rc = SQLBindParameter(imp_sth->hstmt,
phs->idx, param_type, value_type, phs->sql_type,
column_size, d_digits,
- value_ptr, buffer_length, &phs->cbValue);
+ value_ptr, 0, &phs->cbValue);
if (!SQL_SUCCEEDED(rc)) {
dbd_error(sth, rc, "_rebind_ph/SQLBindParameter");