[long] Win32 / ODBC failure
| From: | Jon Evans | Date: | Wed, 09 Aug 2000 11:25:01 +0000 |
| Subject: | [long] Win32 / ODBC failure | ||
| Groups: | php.dev | ||
| Request: | Send a blank email to php-dev+get-28239@lists.php.net to get a copy of this message | ||
Hi,
I'm developing a site for a customer using PHP4. It will reside on an
NT server using an ODBC connection to a MS SQL datasource. I'm
testing it on NT, with a connection to a local Access database (using
ODBC) and a connection to a MySQL datasource on another box (again
using ODBC).
I'm having problems with SQL queries that have parameters. Using
Access I get nowhere at all:
Warning: SQL error: [Microsoft][ODBC Microsoft Access Driver]COUNT
field incorrect , SQL state 07001 in SQLExecute in
C:\InetPub\wwwroot\testsql.php on line 79
using MySQL I get a different error:
FATAL: emalloc(): Unable to allocate 268665117 bytes
(followed by a dump of locations where it allocated memory)
(the number of bytes is always the same)
The test script is very simple, I've attached it to this message.
I think there are problems in ext/odbc/php_odbc.c - there is no return
checking in the function PHP_FUNCTION(odbc_execute).
Here is the problem (as I see it). It helps if you look at this ODBC
dump from the NT box:
[snip]
editclient c4:ab ENTER SQLDescribeParam
HSTMT 0x00a61a50
UWORD 1
SWORD * 0x0012ec2c
UDWORD * 0x0012ec28
SWORD * 0x0012ec24
SWORD * 0x0012ec32
editclient c4:ab EXIT SQLDescribeParam with return code -1 (SQL_ERROR)
HSTMT 0x00a61a50
UWORD 1
SWORD * 0x0012ec2c
UDWORD * 0x0012ec28
SWORD * 0x0012ec24
SWORD * 0x0012ec32
DIAG [IM001] [Microsoft][ODBC Driver Manager] Driver does not support this function (0)
editclient c4:ab ENTER SQLBindParameter
HSTMT 0x00a61a50
UWORD 1
SWORD 1 <SQL_PARAM_INPUT>
SWORD 1 <SQL_C_CHAR>
SWORD 30592 <unknown>
UDWORD 108
SWORD 0
PTR 0x00569ff0
SDWORD 0
SDWORD * 0x0056f6a0
editclient c4:ab EXIT SQLBindParameter with return code -1 (SQL_ERROR)
HSTMT 0x00a61a50
UWORD 1
SWORD 1 <SQL_PARAM_INPUT>
SWORD 1 <SQL_C_CHAR>
SWORD 30592 <unknown>
UDWORD 108
SWORD 0
PTR 0x00569ff0
SDWORD 0
SDWORD * 0x0056f6a0
DIAG [S1004] [Microsoft][ODBC Microsoft Access Driver]Invalid SQL data type (67)
[snip]
I think that the code ignores the error from SQLDescribeParam, then
passes uninitialised variables to SQLBindParameter. If this is the
case, the following should work around this error:
--- php_odbc.c.orig Wed Aug 9 12:07:51 2000
+++ php_odbc.c Wed Aug 9 12:13:03 2000
@@ -825,8 +825,13 @@
RETURN_FALSE;
}
- SQLDescribeParam(result->stmt, (UWORD)i, &sqltype, &precision,
+ rc = SQLDescribeParam(result->stmt, (UWORD)i, &sqltype, &precision,
&scale, &nullable);
+ if (rc=SQL_ERROR){
+ /* work around servers that don't support SQLDescribeParam */
+ scale=0;
+ sqltype=SQL_CHAR; //assume char
+ }
params[i-1].vallen = (*tmp)->value.str.len;
params[i-1].fp = -1;
I would like to do more testing myself, but I can't get the source to
compile on NT. Is there an NT compilation HOWTO anywhere?
I could work with someone who can compile it if that would be alright,
I could debug and code, someone else could code and compile.
The error message I got from odbc->mysql leads me to believe that a
similar thing is happening there, an error return is being ignored and
an emalloc is attempted with a huge bogus size due to an uninitialised
variable, or an error code.
Jon.
--
Jon Evans / Red Internet Ltd. / +44 1869 337977
<?php // SQL server test script // Jon Evans <jevans@red-net.co.uk> // creates a table called redtesttable // adds some data to it // reads the data back from it // deletes the table // The name of the ODBC data source for the SQL server: $sqlserver = "test"; // The username and password to access the above server: $username = ""; $password = ""; // The name of the test table to create // WARNING the table will be dropped when the script finishes running $testtablename = "redtesttable"; // =================================== echo "<p>making connection to ODBC data source '$sqlserver':"; $conn = odbc_connect($sqlserver, $username, $password); if (!$conn){ echo "<p>failed to connect!"; exit; } //======================================= // Errors ignored for this one as it will fail // anyway if the table doesn't already exist echo "<p>dropping table '$testtablename'"; $sql = "drop table $testtablename;"; @$ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to drop test table, carrying on anyway..."; } //======================================= echo "<p>creating a table '$testtablename'"; $sql = "create table $testtablename (ID int not null, "; $sql .= "test_a varchar(255), test_b varchar(255), test_c int);"; $ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to make test table!"; exit; } //======================================= echo "<p>inserting data into table '$testtablename'<p>"; $sql = "insert into $testtablename (test_a, test_b, test_c) values (?, ?, ?);"; $sth = odbc_prepare($conn, $sql); if (!$sth) { echo "<p>failed to prepare query!"; exit; } for ($i =0; $i<30; $i++){ $a = "Test data $i"; $b = "$i test data"; $array = array($a, $b, $i); $ret = odbc_execute($sth, &$array); if (!$ret) { echo "<p>failed to insert data!"; exit; } } //======================================= echo "<p>reading data from table '$testtablename'"; $sql = "select * from $testtablename;"; $sth = odbc_exec($conn, $sql); if (!$sth) { echo "<p>failed to execute sql!"; exit; } echo odbc_result_all($sth); //======================================= echo "<p>dropping table '$testtablename'"; $sql = "drop table $testtablename;"; $ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to drop test table!"; exit; }
<?php // SQL server test script // Jon Evans <jevans@red-net.co.uk> // creates a table called redtesttable // adds some data to it // reads the data back from it // deletes the table // The name of the ODBC data source for the SQL server: $sqlserver = "test"; // The username and password to access the above server: $username = ""; $password = ""; // The name of the test table to create // WARNING the table will be dropped when the script finishes running $testtablename = "redtesttable"; // =================================== echo "<p>making connection to ODBC data source '$sqlserver':"; $conn = odbc_connect($sqlserver, $username, $password); if (!$conn){ echo "<p>failed to connect!"; exit; } //======================================= // Errors ignored for this one as it will fail // anyway if the table doesn't already exist echo "<p>dropping table '$testtablename'"; $sql = "drop table $testtablename;"; @$ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to drop test table, carrying on anyway..."; } //======================================= echo "<p>creating a table '$testtablename'"; $sql = "create table $testtablename (ID int not null, "; $sql .= "test_a varchar(255), test_b varchar(255), test_c int);"; $ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to make test table!"; exit; } //======================================= echo "<p>inserting data into table '$testtablename'<p>"; $sql = "insert into $testtablename (test_a, test_b, test_c) values (?, ?, ?);"; $sth = odbc_prepare($conn, $sql); if (!$sth) { echo "<p>failed to prepare query!"; exit; } for ($i =0; $i<30; $i++){ $a = "Test data $i"; $b = "$i test data"; $array = array($a, $b, $i); $ret = odbc_execute($sth, &$array); if (!$ret) { echo "<p>failed to insert data!"; exit; } } //======================================= echo "<p>reading data from table '$testtablename'"; $sql = "select * from $testtablename;"; $sth = odbc_exec($conn, $sql); if (!$sth) { echo "<p>failed to execute sql!"; exit; } echo odbc_result_all($sth); //======================================= echo "<p>dropping table '$testtablename'"; $sql = "drop table $testtablename;"; $ret = odbc_exec($conn, $sql); if (!$ret) { echo "<p>failed to drop test table!"; exit; }