Well well.. Took me a while (http://damjan.cvetko.org/post/95985934678/tracing-php-errors) to get around to this.
I was forced to look again at the issue where we use PHP, unixODBC and FreeTDS to connect to a MSSQL server. At first, PHP would just seem to randomly crash, when turning it around a bit it would try to allocate obscene amounts of memory:
PHP Fatal error: Â Allowed memory size of 536870912 bytes exhausted (tried to allocate 4294967293 bytes) in /home/test/crash.php on line 11
With some poking around, I was able to reliable reproduce the issue specific rows and columns so that the issue would happen right away with one one-row query. The column in question was a varchar and it had unicode characters (ÄĹ Ĺ˝...).
Since I was riding a new and current SLES and had all packages from either official or Leap repos, I could get my hands on some debuginfo and sources.
Some very nice SLES commands.
zypper mr -e SLE-Module-Web-Scripting12-Debuginfo-Updates
zypper install php5-debuginfo-5.5.14-109.8.2.x86_64
zypper install --oldpackage php5-odbc-debuginfo-5.5.14-109.8.2.x86_64
zypper lr -u (To get Repo links and Tokens for manual SRC downloads)
Armed with some source and debug symbols, I could figure out that on fetch of such particular columns the vallen was -4, even though the value content seemd ok. Naturaly, the poor php5-odbc would crash and burn (function odbc_result):
if ((int)result->values[field_ind].vallen == (int)SQL_NULL_DATA) {
 RETURN_NULL();
} else {
 RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen, 1);
}
Hint: RETURN_STRINGL does a estrndup with a -4 that is unsigned (you guessed it)Â 4294967292 (+1 for and extra \0, but thatâs not the point).
Ok, where does -4 come from?
/etc/freetds.conf dump file = /tmp/freetds.log dhowd had some raw network dumps, which all looked OK, but it did however give an important clue.Â
01:56:22.124959 6458 (iconv.c:330):tds_iconv_open(0x55c2a9511180, CP1250)
01:56:22.125024 6458 (iconv.c:187):local name for ISO-8859-1 is ISO-8859-1
01:56:22.125031 6458 (iconv.c:187):local name for UTF-8 is UTF-8
01:56:22.125033 6458 (iconv.c:187):local name for UCS-2LE is UCS-2LE
01:56:22.125035 6458 (iconv.c:187):local name for UCS-2BE is UCS-2BE
01:56:22.125037 6458 (iconv.c:349):setting up conversions for client charset "CP1250"
01:56:22.125039 6458 (iconv.c:351):preparing iconv for "CP1250" <-> "UCS-2LE" conversion
01:56:22.125089 6458 (iconv.c:391):preparing iconv for "ISO-8859-1" <-> "UCS-2LE" conversion
FreeTDS performs iconv conversions and since I suspected our lovely Slavic script to be at the heart of the issue, I went digging into FreeTDS next. Behold, freetds/convert.h:
#define TDS_CONVERT_NOMEM -4 /* insufficient memory */
The darn thing returns its internal errors in the length of the field when calling SQLFetch (length is bound with SQLBindCol).
I did some poking around with gdb, but in the end couldnât pinpoint exactly why the allocation failed. Not enough memory, surely not.
I did try a different approach. Can I skip iconv translation completely? I went back to freetds.conf and set the charset to the native one, in my case CP1250.
Crashes were no more. I could now do my own iconv call to translate the encoded data to UTF-8 in PHP. Worked like a charm.
In the very end I also tried PDO, since all other SQL methods are becoming extinct and the process indeed didnât crash, but it also returned an empty string in this cases. I would need to try the MS SQL driver for PHP/unixODBC...