php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69752 PDOStatement::execute() leaks memory with DML Statements when closeCuror() is u
Submitted: 2015-06-03 11:59 UTC Modified: 2015-06-10 15:48 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: phofstetter at sensational dot ch Assigned: ab
Status: Closed Package: PDO PgSQL
PHP Version: 5.6.9 OS: MacOS X and Linux
Private report: No CVE-ID:
 [2015-06-03 11:59 UTC] phofstetter at sensational dot ch
Description:
------------
using PDO PGSql, when calling closeCursor() on a statement before re-using it in a future execute(), some amount of memory is leaked every time the statement is executed.

This only happens on DML statements (insert or update) and it only happens when closeCursor() is being called.

Test script:
---------------
<?php

printf("I'm pid %d\n", getmypid());


$pdo = new PDO('pgsql:dbname=somedb;user=someuser;host=127.0.0.1');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->exec('discard all');
$pdo->exec("set client_encoding to 'utf-8'");

$pdo->beginTransaction();
$pdo->exec("
    create table foo (
        id bigserial not null primary key,
        field1 text not null,
        field2 text not null,
        field3 text not null,
        field4 int4 not null
    )
");

// leaks
$stmt = $pdo->prepare("insert into foo (field1, field2, field3, field4) values (:field1, :field2, :field3, :field4)");

// also leaks
//$stmt = $pdo->prepare("update foo set field1 = :field1, field2 = :field2, field3 = :field3 where field4 = :field4");

// doesn't leak
//$stmt = $pdo->prepare("select * from foo where field1 = :field1 and field2 = :field2 and field3 = :field3 and field4 = :field4");


$max = 10000;
for($i = 0; $i < $max; $i++) {
    $data = array(
        'field1' => "field1: $i",
        'field2' => "field2: $i",
        'field3' => "field3: $i",
        'field4' => $i
    );
    $stmt->execute($data);
    $stmt->closeCursor(); // leak goes away if closeCursor() is removed
    printf("\r%d", memory_get_usage(true)/1024);
}

$pdo->rollBack();


Expected result:
----------------
printed memory usage should stay constant.

Actual result:
--------------
printed memory usage is continuously increasing.

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-06-03 12:43 UTC] phofstetter at sensational dot ch
I'm not 100% sure and I'd like confirmation by somebody familiar with PDO, but I think this is what happens:

Once you call closeCursor(), PDOStatement will first call the driver specific closing function (which in case of postgres is a noop). Then, the parent implementation will set the executed member to 0. Here: https://github.com/php/php-src/blob/master/ext/pdo/pdo_stmt.c#L2098

The postgres PDO driver calls ecealloc() depending on the state of that executed flag though. Like here:

https://github.com/php/php-src/blob/master/ext/pdo_pgsql/pgsql_statement.c#L227

But that memory is never freed if the `executed` member gets reset outside.

I'll try to find a clean solution and propose a pull request (with tests, of course)
 [2015-06-03 13:05 UTC] phofstetter at sensational dot ch
Based on the previous analysis, I came up with

https://github.com/pilif/php-src/commit/c34f70dd0dc42c007352940a7af562d89bddc6bf

which fixes the leak. I'll add tests and then create an official pull request.

Just giving a heads-up here so nobody starts working on this :-)
 [2015-06-05 07:02 UTC] phofstetter at sensational dot ch
With the attached pull request, all tests (including the new one that checks for the leak) pass now. Is there anything else you need from me?
 [2015-06-10 15:48 UTC] ab@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: ab
 [2015-06-10 15:48 UTC] ab@php.net
The PR is merged in 5.5+.

Thanks.
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Fri Feb 24 01:01:37 2017 UTC