Linux odbc Fatal error: Allowed memory size

2020-02-06 13:50发布

问题:

I currently have some problems to set up an intranet with an odbc link beetween an AS400 (iseries V6R1) and a Debian I use the iseriesAccess7.1 odbc driver 64bits, unixODBC2.3.1 and php5.4 with unixODBC support.

My link seems to be good because I can connect to my DataBase using the isql command (which is part of unixODBC) and execute some SQL queries but it's impossible to read records in the database using a php script. When I try to launch a little script on my intranet I get the following error :

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 493921239296 bytes) in /home/www/imypdo/imypdo.php on line 122

that's more than 450 Gb !! and nothing in /var/log/messages and in /etc/httpd/logs/error_log

A simple sql query (with only 1 row in the select) will return some strange characters (see below) and as soon as I select 1 or 2 more rows the memory size error occurs.

[0] => Array ( [ADHMAR] => AAAAAAA a@YÿŒ4–X 0!ÿŒ4làÿŒ4làÿŒ4! )

I'm almost sure it's a 64bit driver related problem because I already have another Debian linked with this iseries but with the 32 bit driver and it works perfectly. What is weird is that isql command is working and ther is nothing in the log files...

if it really is a 64 bit driver problem, how can I prove it to IBM ?

any help will be appreciated

thanks

--------------------------- Class to connect ----------------------------

private $_bdd = "DSN=db2;",
        $_user = "USERNAME",
        $_pwd = "Password";

private $_con,
        $_isConnected;


public function open_connection(){
    $this->_con = odbc_connect ($this->_bdd, $this->_user, $this->_pwd ) or die("Error Connection") ;
    $this->_isConnected = true;
}

public function close_connection(){
     odbc_close($this->_con);
     $this->_isConnected = false;
}

public function execute($sql){

    if(!($this->_isConnected))
        $this->open_connection();

    #execute sql
    $res = odbc_exec($this->_con, $sql);

    return $res;
}

public function fetchRow($res){

    $row = odbc_fetch_array($res);
    return $row;
}

}

--------------------------------- Query Script ------------------------------

public function getPhoneLogsByDate($startDate, $endDate) {

    $startDate  = date('Ymd', strtotime($startDate));
    $endDate    = date('Ymd', strtotime($endDate));

    $rr = new As400_Model_as400query();

    $rr->open_connection();

    $sql = "select trim(tluser) as USER, trim(tlacct) as CLIENT, trim(concat(concat(concat(concat(concat(concat(substr(trim(tldate),1,4),'-'),substr(trim(tldate),5,2)),'-'),substr(trim(tldate),7,2)),' '), concat(concat(concat(concat(substr( substr(trim(tltime+1000000),2,6),1,2),':'),substr(substr(trim(tltime+1000000),2,6),3,2)),':'), substr(substr(trim(tltime+1000000),2,6),5,2)))) as DATETIME 
            ,trim(concat(concat(concat(concat(concat(concat(concat(concat(concat(concat(trreas,'|'),trsr01),'|'),trsr02),'|'),trsr03),'|'),trsr04),'|'),trsr05)) as REASONS
            ,trim(concat(concat(concat(tnnot1,tnnot2),tnnot3),tnnot4)) as NOTES

            from cabledta.tlogmstr left join cabledta.tlogreas on trnum#=tlnum#  left join cabledta.tlognote on tnnum#=tlnum#
            where tldate>='".$startDate."' and tldate <='".$endDate."'";



    $res = $rr->execute($sql);

    $response = array();


    while ($row = $rr->fetchRow($res)){

        $response[] = array(

                                'userName'      => $row['USER'],
                                'clientNumber'  => $row['CLIENT'],
                                'logDateTime'   => $row['DATETIME'],
                                'logReasons'    => $row['REASONS'],
                                'logNotes'      => utf8_encode($row['NOTES'])

                            );

    }
    $rr->close_connection();

return $response;
}

回答1:

The iSeriesAccess for Linux 7.1 ODBC driver was compiled against an older version of unixODBC which specified a 32-bit SQLLEN. When your app or middleware (php in this case) is compiled against a newer version of unixODBC (since 2.2.14), by default will use a 64-bit SQLLEN. Since the driver only overwrites the top 32-bits of the data, the results of any API call which takes an SQLLEN pointer as a parameter is undefined in this scenario.

You have two options: recompile php with BUILD_LEGACY_64_BIT_MODE set (-DBUILD_LEGACY_64_BIT_MODE) or use the new IBM i ODBC driver which uses 64-bit SQLLENs. You can find more about the new ODBC driver included with the IBM i Access Client Solutions Linux Application Package in this article I wrote: http://www.ibm.com/developerworks/ibmi/library/i-ibmi-access-client-solutions-linux that Nick referenced in an above comment. You can find out how to download the driver here: http://iprodeveloper.com/blog/how-do-you-actually-get-access-client-solutions



回答2:

I would check (given its a 64 bit driver) that the driver manager, application and driver agree on the sizeof( SQLLEN ). The choices are 32 and 64 bit. I believe that the IBM drivers let you set this via the config file. unixODBC 2.3 will by default build with sizeof( SQLLEN ) = 64 bit. How your app is built I cant say.



回答3:

Sounds to me like you're reading a table whose text fields have an incorrect CCSID, most likely they have 65535 assigned which means hex/binary.

So the system isn't trying to translate the EBCDIC text to ASCII for you.

The right answer, is to get the CCSID change to the correct value for the data in the file. 37=US EBCDIC. While you are at it, You can have them check/change the default CCSID of the system. WRKSYSVAL QCCSID

However, the Windows ODBC driver does offer a setting, ForceTranslation (of CCISD 65535).

Charles



回答4:

I figure it out.

In the 64bit version the odbc crashs when one of the return fields are NULL. So the work around is to replace all the null fields when retriving the query.

example :

select ifnull(tluser,'') as USER from database.table