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"
Related
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'];
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"
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
I have view similar to this one
CREATE OR REPLACE VIEW regexp_test AS (
SELECT regexp_matches(decode('NTB4', 'base64')::text, '(\d+)x')
)
When I query view from pgAdmin, array with single value of 50 is returned, as expected.
SELECT * FROM regexp_test
But when I call the very same query from within PHP via pg_query('SELECT * FROM regexp_test'), nothing is returned.
postgres version 9.5.3,
php version 7.0.3 (same result with 5.6.14)
PHP code is very plain:
<?php
$link = pg_connect('host=localhost port=5432 dbname=test user=postgres password=postgres');
$qry = "SELECT * FROM regexp_test";
$res = pg_query($link, $qry);
while ($row = pg_fetch_row($res)) {
print_r($row);
}
The same query
select e'\\x353078'::bytea;
gives results in different formats in psql:
bytea
----------
\x353078
and in PgAdmin III:
bytea
----------
50x
For the documentation:
The bytea type supports two external formats for input and output: PostgreSQL's historical "escape" format, and "hex" format. Both of these are always accepted on input. The output format depends on the configuration parameter bytea_output; the default is hex. (Note that the hex format was introduced in PostgreSQL 9.0; earlier versions and some tools don't understand it.)
PgAdmin III (and also PgAdmin4) probably for historical reasons sets the value of bytea_output to escape while the default value of the parameter is hex. This can lead to confusion (and as you can see it leads). It seems that pgAdmin should not change the default value of the parameter.
You can change the parameter in your application to get the same behaviour like in PgAdmin:
set bytea_output to escape;
Of course, using encode() is also a good solution.
pg_query returns a result resource.
$result = pg_query('SELECT * FROM regexp_test');
while ($row = pg_fetch_row($result)) {
echo "$row";
}
pg_query returns false on error
If an error occurs, and FALSE is returned, details of the error can be retrieved using the pg_last_error() function if the connection is valid.
I figured out using encode(decode('NTB4', 'base64'), 'escape') instead of typecast decode('NTB4', 'base64')::text fixed problem.
So test view now looks like this:
CREATE OR REPLACE VIEW regexp_test AS (
SELECT regexp_matches(encode(decode('NTB4', 'base64'), 'escape'), '(\d+)x')
)
Calling pg_query('SELECT * FROM regexp_test') now returns expected result - single row/field with '{50}' in it.
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?