php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #25550 PostgreSQL query gives "undefined offset" error
Submitted: 2003-09-15 16:49 UTC Modified: 2003-09-18 08:23 UTC
From: chris at statgen dot ncsu dot edu Assigned: cox (profile)
Status: Closed Package: PEAR related
PHP Version: 4.3.1 OS: Win XP
Private report: No CVE-ID: None
 [2003-09-15 16:49 UTC] chris at statgen dot ncsu dot edu
Description:
------------
On trying to fetch the next row from a PostgreSQL query using Pear DB you get an "undefined offset" notice from within "fetchInto" in DB_pgsql in pgsql.php.

I believe that this is caused by these two lines in DB_pgsql::freeResult:         

        unset($this->row[(int)$result]);
        unset($this->num_rows[(int)$result]);

The problem is that the prepare used by getOne (eventually) returns the same resource number that a real query query gets (the initial query in my example code). So the row number and count for the initial query are removed from the array.

Reproduce code:
---------------
$dbuser = "postgres";
$dbpass = 'xxxxxxxx';
require_once 'DB.php';
$dsn = "pgsql://$dbuser:$dbpass@localhost/dbname";
$db = DB::connect($dsn, FALSE);
if (!DB::isError($db)) $db->setFetchMode(DB_FETCHMODE_OBJECT);

// Now execute the same query but don't fetch the result row yet.
$res1 = $db->query("SELECT 'query 1' AS col1");
if (DB::isError($res1)) exit("First query failed");

// Now issue a bunch more queries using "getOne".
for ($i = 0; $i < 20; ++$i)
{
  $parms = array($i);
  $val = $db->getOne("SELECT ?", $parms);
} 

// Try to get the row from the query issued before the "getOne" calls.
$row = $res1->fetchRow();
if (DB::isError($row)) exit("Fetch failed for query 1 row.");
print $row->col1;

Expected result:
----------------
Running with the CLI - expect to see "query 1" get printed on the console.

Actual result:
--------------
Notice: Undefined offset:  11 in c:\www\php\DB\pgsql.php on line 289

Notice: Undefined offset:  11 in c:\www\php\DB\pgsql.php on line 290

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-09-16 03:32 UTC] nicos@php.net
That should fix the problem:

Need feedback.

Index: pgsql.php
===================================================================
RCS file: /repository/pear/DB/DB/pgsql.php,v
retrieving revision 1.26
diff -u -u -r1.26 pgsql.php
--- pgsql.php   28 Aug 2003 14:37:36 -0000      1.26
+++ pgsql.php   16 Sep 2003 07:32:17 -0000
@@ -306,9 +306,15 @@
             return false;
         }
         unset($this->prepare_tokens[(int)$result]);
-        unset($this->prepare_types[(int)$result]);
-        unset($this->row[(int)$result]);
-        unset($this->num_rows[(int)$result]);
+        if(isset($this->prepare_types[(int)$result])) {
+            unset($this->prepare_types[(int)$result]);
+        }
+        if(isset($this->row[(int)$result])) {
+            unset($this->row[(int)$result]);
+        }
+        if(isset($this->num_rows[(int)$result])) {
+            unset($this->num_rows[(int)$result]);
+        }
         $this->affected = 0;
         return true;
     }
 [2003-09-16 09:42 UTC] chris at statgen dot ncsu dot edu
No, that won't work - the undefined offset error isn't coming from the freeResult member function but from the fetchInto function (when a fetchRow is called). This happens after freeResult has freed an element in the array "row" that it should not have freed.

I'll see if I can be more clear....

In the reproduce code the initial query is given resource id #15. The current row number and the row count for this query is put into $this->row[15] and $this->num_rows[15].

Then I issue the other queries using getOne. The prepare used by getOne returns integer ids 1,2,3,....,15 and immediately calls freeResult on each of these ids. So when the integer value returned is 15 the "unset" lines in freeResult remove $this->row[15] and $this->num_rows[15] from the arrays - but this shouldn't be done because the initial query is still active.

I then try to fetch the next (only) row of my initial query - fetchInto tries to use $this->row[15] which has already been removed and so can't get the row even though it should be able to.

It's really a confusion between resource ids and integer ids.

I think that the correct solution may be this something like what I've shown below. But I'm not familiar enough with this code to be sure.

    function freeResult($result)
    {
        if (is_resource($result)) {
            unset($this->row[(int)$result]); // Moved
            unset($this->num_rows[(int)$result]); // Moved
            return @pg_freeresult($result);
        }
        if (!isset($this->prepare_tokens[(int)$result])) {
            return false;
        }
        unset($this->prepare_tokens[(int)$result]);
        unset($this->prepare_types[(int)$result]);
//Removed        unset($this->row[(int)$result]);
//Removed        unset($this->num_rows[(int)$result]);
        $this->affected = 0;
        return true;
    }
 [2003-09-18 08:23 UTC] cox@php.net
Fixed in CVS. Please reopen the bug if you find some problem with the patch.

Thanks for the report,

Tomas V.V.Cox
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Thu Aug 05 02:01:23 2021 UTC