|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2006-09-29 14:26 UTC] jeff at badtz-maru dot com
Description: ------------ If there are multiple piecewise (LONG) columns in a query's return, php_oci_statement_fetch will misbehave. This seems to be caused when OCIStmtSetPieceInfo is called again on a column that has already had all of its pieces retrieved. In this scenario, OCIStmtSetPieceInfo seems to return the length that were requested even though it has actually not retrieved anything (you'd think it would return 0). This causes column->retlen4 to be incremented by PHP_OCI_PIECE_SIZE, mangling the actual length of the fetched data I believe that if OCIStmtGetPieceInfo is used to determine whether the piece about to be fetched is OCI_LAST_PIECE or OCI_ONE_PIECE, then a flag could be set to indicate when each piecewise column is fully retrieved. Reproduce code: --------------- Fetch from two tables that both contain LONG datatypes. PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Nov 06 07:00:01 2025 UTC |
The issue is in oci8_statement.c, in the part of php_oci_statement_fetch shown below: while (statement->errcode == OCI_NEED_DATA) { for (i = 0; i < statement->ncolumns; i++) { column = php_oci_statement_get_column(statement, i + 1, NULL, 0 TSRMLS_CC); if (column->piecewise) { if (!column->data) { column->data = (text *) emalloc(PHP_OCI_PIECE_SIZE); } else { column->data = erealloc(column->data, column->retlen4 + PHP_OCI_PIECE_SIZE); } column->cb_retlen = PHP_OCI_PIECE_SIZE; PHP_OCI_CALL(OCIStmtSetPieceInfo, ( (void *) column->oci_define, OCI_HTYPE_DEFINE, statement->err, ((char*)column->data) + column->retlen4, &(column->cb_retlen), OCI_NEXT_PIECE, &column->indicator, &column->retcode ) ); } } PHP_OCI_CALL_RETURN(statement->errcode, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT)); for (i = 0; i < statement->ncolumns; i++) { column = php_oci_statement_get_column(statement, i + 1, NULL, 0 TSRMLS_CC); if (column && column->piecewise && (column->cb_retlen<PHP_OCI_PIECE_SIZE) ) { column->retlen4 += column->cb_retlen; } } As we can see, OCIStmtSetPieceInfo is called for every piecewise column, every time OCI_NEED_DATA is true. That doesn't seem to be correct. If OCIStmtSetPieceInfo is called for a column that does not have any pieces left to retrieve, the call will fail and return the same column->cb_retlen that was passed in (instead of the 0 that the code above seems to have expected). That means that if there are two piecewise columns and the first has had all of its pieces retrieved, each subsequent OCI_NEED_DATA will return a bogus PHP_OCI_PIECE_SIZE buffer. I believe that the correct way to implement the piecewise retrieval is to check OCIStmtGetPieceInfo immediately before the call to OCIStmtSetPieceInfo. If a value of OCI_LAST_PIECE or OCI_ONE_PIECE is returned, then set a flag to prevent that column loop from trying to set the piece info for that column on any subsequent iterations. thanks, jeff$db = oci_connect('user', 'pass', 'devel'); $sql1 = " SELECT a.id, a.longfield, b.longfield FROM TEST1 A, TEST2 B WHERE a.id = b.id "; $sth = oci_parse($db, $sql1); oci_execute($sth); $resarr = oci_fetch_array($sth); In this case, a.longfield (as held in $resarr) will contain the actual contents of a.longfield plus PHP_OCI_PIECE_SIZE bytes of "garbage" data. The query of a single long field does work properly, such as "select a.longfield from test1 a", which is also understandable after examining the php_oci_statement_fetch code. thanks, jeffCannot reproduce. SQL> insert into test1 values (123456789); SQL> insert into test2 values (987654321); Results in: array(4) { [0]=> string(65544) "123456789" ["L1"]=> string(65544) "123456789" [1]=> string(65544) "987654321" ["L2"]=> string(65544) "987654321" }The web heads I am testing this against are running kernel 2.6.9-42 and OCI client library 10.2 . The database servers are running 10g (which I believe is 10.2) also on the same linux kernel (RH Enterprise). create table JEFFTEST1 {LONGFIELD LONG{ create table JEFFTEST2 {LONGFIELD LONG} insert into jefftest1 (longfield) values ('123456789') insert into jefftest2 (longfield) values ('987654321') SELECT a.longfield, b.longfield FROM JEFFTEST1 A, JEFFTEST2 B I've attached the output at http://www.cmainteractive.com/jeff/dump.txt . I can reproduce this 100% of the time and we do have a test environment where we can make changes to the OCI8 source and test the results or add debugging code or whatever, if that can be of help. This is how we were able to observe that OCIStmtSetPieceInfo is returning 65535 in cb_retlen when it is called after it has already returned the last piece. I wasn't able to find any documentation to supp