]> granicus.if.org Git - php/commitdiff
Fix #79106: PDO may fetch wrong column indexes with PDO::FETCH_BOTH
authorChristoph M. Becker <cmbecker69@gmx.de>
Mon, 13 Jan 2020 12:34:06 +0000 (13:34 +0100)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 13 Jan 2020 17:47:54 +0000 (18:47 +0100)
Column names can be numeric strings, so we have to make sure to insert
the column values with the appropriate numeric keys, instead of adding
them.

NEWS
ext/pdo/pdo_stmt.c
ext/pdo/tests/bug_79106.phpt [new file with mode: 0644]
ext/pdo/tests/bug_79106_collision.phpt [new file with mode: 0644]
ext/pdo_mysql/tests/bug_61411.phpt
ext/pdo_mysql/tests/pdo_mysql_fetch_both.phpt

diff --git a/NEWS b/NEWS
index 8f20e992111de8e9055b3bf2202771a1bc7bf37c..bc018826952b091911302eb1b284d18df4c8912b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,10 @@ PHP                                                                        NEWS
   . Fixed bug #79084 (mysqlnd may fetch wrong column indexes with MYSQLI_BOTH).
     (cmb)
 
+- PDO:
+  . Fixed bug #79106 (PDO may fetch wrong column indexes with PDO::FETCH_BOTH).
+    (cmb)
+
 23 Jan 2020, PHP 7.3.14
 
 - Core
index 873f977fb6fc6b6f38e3b127dc81dcf9e4f35681..3aaf145c9f519379f54a25a7ea6f65286f98df43 100644 (file)
@@ -1028,10 +1028,9 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_
                                case PDO_FETCH_USE_DEFAULT:
                                case PDO_FETCH_BOTH:
                                        zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
-                                       if (Z_REFCOUNTED(val)) {
-                                               Z_ADDREF(val);
+                                       if (zend_hash_index_add(Z_ARRVAL_P(return_value), i, &val) != NULL) {
+                                               Z_TRY_ADDREF(val);
                                        }
-                                       zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val);
                                        break;
 
                                case PDO_FETCH_NAMED:
diff --git a/ext/pdo/tests/bug_79106.phpt b/ext/pdo/tests/bug_79106.phpt
new file mode 100644 (file)
index 0000000..06e36ae
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+Bug #79106 (PDO may fetch wrong column indexes with PDO::FETCH_BOTH)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo')) die('skip pdo extension not available');
+$dir = getenv('REDIR_TEST_DIR');
+if (!$dir) die('skip no driver');
+require_once $dir . 'pdo_test.inc';
+try {
+    $db = PDOTest::factory();
+} catch (PDOException $e) {
+    die('skip ' . $e->getMessage());
+}
+if ($db->query('SELECT 1 as "1"') === false) {
+    die('skip driver does not support quoted numeric identifiers');
+}
+?>
+--FILE--
+<?php
+if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR=' . dirname(__FILE__) . '/../../pdo/tests/');
+require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
+$db = PDOTest::factory();
+
+$stmt = $db->query('SELECT 0 as "2007", 0 as "2008", 0 as "2020"');
+var_dump($stmt->fetchAll());
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  array(6) {
+    [2007]=>
+    string(1) "0"
+    [0]=>
+    string(1) "0"
+    [2008]=>
+    string(1) "0"
+    [1]=>
+    string(1) "0"
+    [2020]=>
+    string(1) "0"
+    [2]=>
+    string(1) "0"
+  }
+}
diff --git a/ext/pdo/tests/bug_79106_collision.phpt b/ext/pdo/tests/bug_79106_collision.phpt
new file mode 100644 (file)
index 0000000..8dcb9d9
--- /dev/null
@@ -0,0 +1,38 @@
+--TEST--
+Bug #79106 (PDO may fetch wrong column indexes with PDO::FETCH_BOTH) - collision
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo')) die('skip pdo extension not available');
+$dir = getenv('REDIR_TEST_DIR');
+if (!$dir) die('skip no driver');
+require_once $dir . 'pdo_test.inc';
+try {
+    $db = PDOTest::factory();
+} catch (PDOException $e) {
+    die('skip ' . $e->getMessage());
+}
+if ($db->query('SELECT 1 as "1"') === false) {
+    die('skip driver does not support quoted numeric identifiers');
+}
+?>
+--FILE--
+<?php
+if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR=' . dirname(__FILE__) . '/../../pdo/tests/');
+require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
+$db = PDOTest::factory();
+
+$stmt = $db->query('SELECT 11111 as "1", 22222 as "2"');
+var_dump($stmt->fetchAll());
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  array(3) {
+    [1]=>
+    string(5) "11111"
+    [0]=>
+    string(5) "11111"
+    [2]=>
+    string(5) "22222"
+  }
+}
index d840f222f1a81f62682e266de52060d6c3131e8e..f84945242cfb1118bbb5cc9c84b8afac68a8f681 100644 (file)
@@ -47,7 +47,7 @@ print "done!";
 array(2) {
   [1]=>
   int(1)
-  [2]=>
+  [0]=>
   int(1)
 }
 done!
index 9303a3119da1a2e6131a26a58a0000dd1c72cb9d..8c0a14ffc08f6f7857d8dde2498277d26eaee74a 100644 (file)
@@ -59,30 +59,4 @@ $db = MySQLPDOTest::factory();
        print "done!";
 ?>
 --EXPECT--
-[002] Suspicious FETCH_BOTH result, dumping
-array(2) {
-  [0]=>
-  string(1) "1"
-  [1]=>
-  string(1) "1"
-}
-array(2) {
-  [1]=>
-  string(1) "1"
-  [2]=>
-  string(1) "1"
-}
-[002] Expected differes from returned data, dumping
-array(2) {
-  [0]=>
-  string(1) "1"
-  [1]=>
-  string(1) "1"
-}
-array(2) {
-  [1]=>
-  string(1) "1"
-  [2]=>
-  string(1) "1"
-}
 done!