PHP ODBC SELECT returns incomplete field - php

I've migrated a database from MS SQL 2005 (Windows Server 2003) to MS SQL 2012 (windows Server 2012 R2). Everything is working well except one thing.
Whenever I try in PHP with ODBC to get a specific column (unfortunately named "text") and the field has a big content, the result is incomplete. some of the Content's missing, just not existent. If I try the SQL command in SQL Management Studio, the result is correct.
I've tried it with the following two methods (same result):
$query = odbc_exec ($dbh, "SELECT * FROM artikel WHERE id = '$page'");
if ($data = odbc_fetch_array($query)) {
extract($data);
echo $text;
}
//Method 2 is near the same but with the following SQL command and with odbc_result instead of extract();
SET textsize 2147483647 SELECT text FROM artikel WHERE id = '$page'

Found the solution (thanks to #Álvaro González)
I added the following two lines to the code (after odbc_exec())
odbc_binmode($query, ODBC_BINMODE_PASSTHRU);
odbc_longreadlen($query, 16384); //Set the length of the displayed value to 16384
Thanks everyone for help

Related

ODBC array fetch of VARBINARY column causes server timeout

I have an app that fetches a VARBINARY(max) data from SQL Server database. On my local environment the app connects via SQL Driver. Connection string of odbc_connect contains:
DRIVER={SQL Server}
I am fetching the VARBINARY data like this:
// Hexadecimal data of attachment
$query = 'SELECT * FROM attachments WHERE LOC_BLOB_ID = ' . $blob_id;
$attach_result = odbc_exec($connection, $query);
odbc_binmode($attach_result, ODBC_BINMODE_CONVERT);
odbc_longreadlen ($attach_result, 262144);
$attach_row = odbc_fetch_array($attach_result);
$hex_data = $attach_row['attachment_value'];
$binary = hex2bin($hex_data);
It works well. Now I need to run this app on a server where my only option is to use the ODBC driver 17 for SQL Server. Connection string contains:
DRIVER={ODBC Driver 17 for SQL Server}
And it doesn't work. It fails on line number 6 of the preview above (on odbc_fetch_array). I've tried commenting out the odbc_binmode and odbc_longreadlen lines (I assumed this driver might handle those data natively), but no luck, same result: Service unavailable timeout error.
Is there a different approach to this width ODBC Driver 17?
Edit: I found out it hangs on ODBC_BINMODE_CONVERT. If I change it to ODBC_BINMODE_RETURN, it runs within few seconds - however the output is wrong. The ODBC_BINMODE_CONVERT is indeed what I need, but it doesn't process the entire data in time (the timeout is 30 seconds), which is strange, because the VARBINARY field in the database is only 65K characters long, and it runs extremely fast on my local environment.
*Edit2: I've tried to convert the incomplete binary data fetched from the database to hexadecimal and then to PNG and it displays half of the image. So I am positive it is fetching the correct data, it just takes incredibly long to fetch that column, resulting in timeouts in almost every case.
OK. Finaly figured it out. What ended up working for me was using ODBC_BINMODE_RETURN flag instead of ODBC_BINMODE_CONVERT, and NOT using hex2bin() conversion at the end.
The code in my original question worked fine with {SQL Server}, and the following code works with {ODBC Driver 17 for SQL Server}:
$query = 'SELECT * FROM attachments WHERE LOC_BLOB_ID = ' . $blob_id;
$attach_result = odbc_exec($connection, $query);
odbc_binmode($attach_result, ODBC_BINMODE_RETURN);
odbc_longreadlen ($attach_result, 262144);
$attach_row = odbc_fetch_array($attach_result);
$binary = $attach_row['attachment_value'];

PHP Associative array keys get shortened [duplicate]

Here is the relevant code:
function connect(){
// DB credentials and info defined here....
$connection = odbc_connect("DRIVER={SQL Server Native Client 11.0}; Server=$server; Database=$db;", $loginname, $loginpass);
return $connection;
}
function odbc_fetch_results($stmt, &$results) {
$numrows = odbc_num_rows($stmt);
$row = odbc_fetch_array($stmt);
print_r($row); // Prints: Array ( [MEASUREMENT_UNI] => kg)
if($row){
$results = array ($row);
while( $row = odbc_fetch_array($stmt)){
array_push($results, $row);
}
}
return $numrows;
}
$sql = "select * from measurements where ID=$id";
$stmt = executeSQL($conn,$sql);
$nrows = odbc_fetch_results($stmt, $results);
odbc_free_result($stmt);
print_r($result[0]); // Prints: Array ( [0] => Array ( [MEASUREMENT_UNI] => kg) )
The result should contain a columnn called MEASUREMENT_UNIT which (when I do a print_r I can verify) is truncated to MEASUREMENT_UNI which is only 15 characters. The last letter T is cut off.
I also tried a query with a different table and a different column on the SQL Server database as a test to make sure it wasn't any strange setup with the particular table or column that I'm working with. I verified the same thing occurs with a different table/column: column names are truncated to 15 characters max when I run a select query as above.
I have also tried a select which specifies the field name like select MEASUREMENT_UNIT from from measurements where ID=$id instead of select * but that doesn't solve the problem either.
I've seen other similar posts here about this but they all seem to indicate that I should be able to get at least 30 characters, not the 15 character limit that I'm seeing.
Why is the column name being truncated to 15 characters?
Edit: Connecting to a MySQL server database did not seem to result in the same problem. DB column names from the MySQL tables were NOT truncated which leads me to believe that this is not a problem with the ODBC plugin.
$connection = odbc_connect("DRIVER={MySQL};Server=$server; Database=$db;", $loginname, $loginpass);
$sql = "select * from measurements where ID=$id";
$stmt = executeSQL($conn,$sql);
$nrows = odbc_fetch_results($stmt, $results);
odbc_free_result($stmt);
print_r($result[0]); // Prints CORRECTLY: Array ( [0] => Array ( [MEASUREMENT_UNIT] => kg) )
Note that both of the above code sections were tested in the same file on the same server with the same PHP + ODBC installation.
Apparently the problem is with ODBC. There's a bug in PHP where column names are truncated at 31 characters, and the only way to fix this is recompiling PHP, changing the array length of name in /ext/odbc/php_odbc_includes.h (usually 32 but apparently it was 16 in your case), but this is not proven to be either safe or unsafe. Go here and here to see more information about this.
The problem is definitely with the Microsoft ODBC drivers version 11, and are fixed in ODBC Driver 13 Preview for SQL Server.
I discovered this as my development machine running ubuntu 14.04 with the Driver 13 Preview installed works fine, but then when I deployed to our production server running RHEL 7 with the Driver 11, all kinds of havoc ensued as column names were truncated at 15 chars.
Microsoft's documentation is lackluster for Linux support, so if you're running ubuntu, then here's the gist of getting it installed.
The permanent solution is to recompile your PHP as suggested in PHP bug threads or try updating to newer PHP version.
You can work around the problem by selectively renaming columns in your select to shorter ones:
$sql = "SELECT measurement_unit AS measure_unit, * FROM measurements WHERE ID=$id";
// now in your array you will have new index called "measure_unit"

PHP & SQL Server - field names truncated

Here is the relevant code:
function connect(){
// DB credentials and info defined here....
$connection = odbc_connect("DRIVER={SQL Server Native Client 11.0}; Server=$server; Database=$db;", $loginname, $loginpass);
return $connection;
}
function odbc_fetch_results($stmt, &$results) {
$numrows = odbc_num_rows($stmt);
$row = odbc_fetch_array($stmt);
print_r($row); // Prints: Array ( [MEASUREMENT_UNI] => kg)
if($row){
$results = array ($row);
while( $row = odbc_fetch_array($stmt)){
array_push($results, $row);
}
}
return $numrows;
}
$sql = "select * from measurements where ID=$id";
$stmt = executeSQL($conn,$sql);
$nrows = odbc_fetch_results($stmt, $results);
odbc_free_result($stmt);
print_r($result[0]); // Prints: Array ( [0] => Array ( [MEASUREMENT_UNI] => kg) )
The result should contain a columnn called MEASUREMENT_UNIT which (when I do a print_r I can verify) is truncated to MEASUREMENT_UNI which is only 15 characters. The last letter T is cut off.
I also tried a query with a different table and a different column on the SQL Server database as a test to make sure it wasn't any strange setup with the particular table or column that I'm working with. I verified the same thing occurs with a different table/column: column names are truncated to 15 characters max when I run a select query as above.
I have also tried a select which specifies the field name like select MEASUREMENT_UNIT from from measurements where ID=$id instead of select * but that doesn't solve the problem either.
I've seen other similar posts here about this but they all seem to indicate that I should be able to get at least 30 characters, not the 15 character limit that I'm seeing.
Why is the column name being truncated to 15 characters?
Edit: Connecting to a MySQL server database did not seem to result in the same problem. DB column names from the MySQL tables were NOT truncated which leads me to believe that this is not a problem with the ODBC plugin.
$connection = odbc_connect("DRIVER={MySQL};Server=$server; Database=$db;", $loginname, $loginpass);
$sql = "select * from measurements where ID=$id";
$stmt = executeSQL($conn,$sql);
$nrows = odbc_fetch_results($stmt, $results);
odbc_free_result($stmt);
print_r($result[0]); // Prints CORRECTLY: Array ( [0] => Array ( [MEASUREMENT_UNIT] => kg) )
Note that both of the above code sections were tested in the same file on the same server with the same PHP + ODBC installation.
Apparently the problem is with ODBC. There's a bug in PHP where column names are truncated at 31 characters, and the only way to fix this is recompiling PHP, changing the array length of name in /ext/odbc/php_odbc_includes.h (usually 32 but apparently it was 16 in your case), but this is not proven to be either safe or unsafe. Go here and here to see more information about this.
The problem is definitely with the Microsoft ODBC drivers version 11, and are fixed in ODBC Driver 13 Preview for SQL Server.
I discovered this as my development machine running ubuntu 14.04 with the Driver 13 Preview installed works fine, but then when I deployed to our production server running RHEL 7 with the Driver 11, all kinds of havoc ensued as column names were truncated at 15 chars.
Microsoft's documentation is lackluster for Linux support, so if you're running ubuntu, then here's the gist of getting it installed.
The permanent solution is to recompile your PHP as suggested in PHP bug threads or try updating to newer PHP version.
You can work around the problem by selectively renaming columns in your select to shorter ones:
$sql = "SELECT measurement_unit AS measure_unit, * FROM measurements WHERE ID=$id";
// now in your array you will have new index called "measure_unit"

How to display 'text' data correctly with Microsoft ODBC driver for Linux? Currently displaying as random characters

I am trying to display the contents of a field in a MS SQL database, the data type of which is 'text'. When I connect to the database with MS Excel on a PC the value looks something like:
Shipped on the: 18/10/12 Toll IPEC Connote Number: XXX XXX XXXX
When I connect to the database with PHP using the Microsoft ODBC driver for Linux, the output of the text field will display random characters, which are slightly different each time I run the exact same script. Here is what it output the last four times:
Xisep)!ØwXment.class.php))
5isep)!ment.class.php))
µ}isep)!Ø}ment.class.php))
t)!!Owner_IDt)Ø©Ø8
Not sure where it's getting the 'ment.class.php' bit from. That looks like the end of the name of one of my classes, but not one that is included in this particular script. Is the driver broken or what? All other data types (int, varchar, datetime etc) seem to display correctly, the problem only seems to happen with this one text field.
Here is the code:
<?php
require('ConnectWise.inc.php');
$config = new CW_Config;
// Connect to database
$dbcnx = odbc_connect("ConnectWise", $config->db_username, $config->db_password);
// Query database
$query = "select * from Billing_Log where Invoice_Number = '24011'";
$result = odbc_exec($dbcnx, $query);
while ($row = odbc_fetch_array($result)) {
echo $row['Top_Comment'] . "\n";
}
odbc_close($dbcnx);
Here is the output of my last few attempts:
\3ȶä!!¶äY!
öÈö§!!ö§Y!
&Èö×!!ö×Y!
Looks like you're getting an overflow. For some reason SQL is passing the length of your text field as 0, which to SQL means "long" but to PHP means "I dont know" and so PHP is showing whatever is in its memory at the location its expecting data (or something like that - any PHP experts care to explain?).
I've seen this with varchar(max) before but not text. (Converting to) Text is normally the way to fix it. So maybe I'm wrong.
Hope this is of some help. Im not a PHP developer, I just have to use it occasionally, and this sounds much like a painful experience I've gone through before :)

PHP/json My field names are being truncated to 30 characters. Can I stop this?

Ok so I got this piece of vendor software that they said should be run on an apache php server and MySql database.
I didn't have either of those so I put it on a PHP IIS server and I converted the code to work on SQL server.
ex.
mysql_select_db -> mssql_select_db
(among other things)
So I have the following code in a php file
$query = "SELECT * FROM TableName WHERE KEY_FIELD = '".$keyField."';";
$result = mssql_query($query);
$arr = array();
while ( $obj = mssql_fetch_object($result) )
{
$arr[] = $obj;
}
echo '{"results":'.json_encode($arr).'}';
and my results look something like this (captured with fiddler 2)
{"results":[{"KEY_FIELD":"57", "My30characterlongfieldthatiscu":"GoodValue"}]}
"My30characterlongfieldthatiscu" should be "My30characterlongfieldthatiscutoff"
Kinda weird, no?
The vendor claims that the app works perfectly on their end.
I'm thinking this is some sort of IIS PHP limit, is there a way around it or can I expand it?
I found this solution
http://www.php.net/manual/en/ref.mssql.php#74834
but I don't understand it.
Thanks!
See http://bugs.php.net/bug.php?id=33060 - this is what's causing your issue.
You might also want to consider changing the column names - more than 30 characters sounds excessively long.
http://docs.php.net/manual/en/mssql.requirements says:
Note: On Windows, the DBLIB from Microsoft is used. Functions that return a column name are based on the dbcolname() function in DBLIB. DBLIB was developed for SQL Server 6.x where the max identifier length is 30. For this reason, the maximum column length is 30 characters. On platforms where FreeTDS is used (Linux), this is not a problem.
Is using the sqlsrv extension instead of mssql an option?

Categories