PDO_mysql: неправильные результаты выборок.

crocodile2u

http://vbolshov.org.ru
PDO_mysql: неправильные результаты выборок.

PHP Version => 5.1.2RC1
PHP:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'vitek');
$sql_create = "CREATE TABLE `test1` (num int not null)";
$sql_insert = "INSERT INTO `test1` VALUES (1), (2)";
$sql_drop = "DROP TABLE `test1`";
$sql_select = "SELECT * FROM test1";
$sql_select1 = "SELECT * FROM test1 WHERE num = 1";
$sql_select2 = "SELECT * FROM test1 WHERE num = 2";
$pdo->exec($sql_create);
$pdo->exec($sql_insert);
$stmt = $pdo->query($sql_select);
echo "<pre>";

echo "All rows:\n";
while($row = $stmt->fetch())
{
	echo $row['num']."\n";
}

//unset($stmt);
$stmt = $pdo->query($sql_select1);
echo "num = 1:\n";
while($row = $stmt->fetch())
{
	echo $row['num']."\n";
}

//unset($stmt);
$stmt = $pdo->query($sql_select2);
echo "num = 2:\n";
while($row = $stmt->fetch())
{
	echo $row['num']."\n";
}
$pdo->exec($sql_drop);
echo "</pre>";
Выполняя этот код, я ожидаю получить следующий результат:
PHP:
All:
1
2
num = 1:
1
num = 2:
2
На самом деле же получаю:
PHP:
All:
1
2
num = 1:
num = 2:
Ожидаемый же результат получаю лишь если раскомментирую строки unset($stmt);

Я считаю, это некорректное поведение. Что думает многоуважаемый all?

Собственно, подозреваю, что это может относиться не только к PDO...
 

Profic

just Profic (PHP5 BetaTeam)
E:\>php test22.php
All rows:
1
2
num = 1:
1
num = 2:
2

E:\>php -v
PHP 5.1.2-dev (cli) (built: Dec 14 2005 13:43:23)
Copyright (c) 1997-2005 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2005 Zend Technologies
with Xdebug v2.0.0beta5-dev, Copyright (c) 2002, 2003, 2004, 2005, by Derick Rethans

E:\>

Смутное подозрение, что код который выполняется не есть тот, который представлен: 'All' != 'All rows'.
 

crocodile2u

http://vbolshov.org.ru
Нет, код тот же, это я зачем-то для форума поправил "All:" на "All rows:". Только что проверил еще раз, уже скопировав код из форума - результат тот же.

Возможно, в CVS уже что-то поменялось - я ставил ПХП раньше, чем 14 декабря. Попробую скачать новую версию.

-~{}~ 05.02.06 15:25:

5.1.3-dev - та же фигня.
 

si

Administrator
странный этот PDO, простой код

PHP:
<?php

error_reporting(E_ALL);

define('DB_PASS','****');
define('DB_USER','Test');

echo "<pre>\n";

try
{
	$pdo = new PDO('mysql:host=mysql;dbname=test', DB_USER, DB_PASS, array());

	$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

	var_dump($pdo);

	$pdo->exec('select * from aaa');

	echo "Finish\n";

}
catch( PDOException $e )
{
	die( $e->getMessage() );
}

echo "</pre>\n";

?>
таблички aaa нету, exception тоже. php5.1.2
 

crocodile2u

http://vbolshov.org.ru
si
Да... Пожалуй, я все же подожду, пока PDO не встанет на ноги - прежде чем начну его использовать.
 

Develar

Новичок
crocodile2u
Проблема в вашем коде или в настройках, а не в PDO.

PHP Version 5.1.2. Windows NT 5.1 build 2600.
Результат одинаков что с unset, что без unset.

Так что надо копать в своем коде и настройках.

PDO по умолчанию использует, в отличии от mysql_*, unbuffered query. Даже если вы выбрали все записи из результата, PDO не поймет что запрос кончился - надо вызвать unset(PDOStatement) чтобы при следующем запросе не возникла ошибка с сообщением о том, что предыдущий запрос не обработан. Выход описан в руководстве - PDO_MYSQL_ATTR_USE_BUFFERED_QUERY. Там же рекомендуется использовать PDOStatement::fetchAll().

Я сейчас использую исключительно PDO (гибкость выдачи результата путем комбинирования атрибутов fetch_style практически избавила меня от дополнительных while и foreach) и никаких багов не замечаю - все что было непонятного разрешалось в течении чтения руководства до прояснения.
 

Profic

just Profic (PHP5 BetaTeam)
si
E:\>php test23.php
object(PDO)#1 (0) {
}
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.aaa' doesn't exist
E:\>

Оптимайзер?
 

crocodile2u

http://vbolshov.org.ru
Develar
Весь свой код, который приводит к странным, по моему мнению, результатам, я уже привел. php-5.1.2RC и php-5.1.3-dev (Fedora Core 4, Apache 1.3.31). Другое дело - считать ли это поведение некорректным? Возможно, кому-то все это кажется вполне закономерным и, возможно, даже удобным. Лично мне это не кажется очевидным. Кроме того, я заметил еще один случай весьма странного поведения PDO - постараюсь оформить небольшой кусок кода и, наверное, выложу для начала здесь же.
 

mani13

Новичок
странно, но:
Код:
$ php -f /home/http/dev.tty.l1557.ru/htdocs/test.php
<pre>All rows:
1
2
num = 1:
num = 2:
</pre>
НО, если использовать prepare/execute:
Код:
$ php -f /home/http/dev.tty.l1557.ru/htdocs/test2.php
<pre>All rows:
1
2
num = 1:
1
num = 2:
</pre>
diff -urN:
Код:
...
-$sql_select1 = "SELECT * FROM test1 WHERE num = 1";
+$sql_select1 = "SELECT * FROM test1 WHERE num = ?";
...
-$stmt = $pdo->query($sql_select1);
+$stmt = $pdo->prepare($sql_select1);
+$stmt->execute(array(1));
...
phpinfo: http://dev.tty.l1557.ru/phpinfo.php

P.S.: второй example как и положено сыпется на exception
 

Develar

Новичок
crocodile2u
Хм. Результат либо есть, либо нет.
у меня что при unset, что без unset выдает
All rows:
1
2
num = 1:
1
num = 2:
2

то есть тот результат что и должен быть. Причем как на NT 5.1 так на FreeBSD 4.11 при php 5.1.2
Про разницу между unset и без unset я уже писал - но в твоем случае ты делаешь while до победного false, то есть обрабатываешь весь результат.

Мне кажется, что дело тут как в анекдоте - на белом экране белую картинку не видно... значит скрипт не работает.

Может все-таки не тот код ты привел? У Profic все как и у меня работает, что твой пример, что si.

-~{}~ 06.02.06 01:27:

Кстати, никто не написал о версии MySQL. У mani13 у которого результат некорректный версия 5.0.18, а меня 4.1.7 (NT 5.1) и 4.0.26 ( FreeBSD 4.11) и корректный результат.
 

mani13

Новичок
Хм, посмотрел, странность в том, что:
Код:
060206  7:23:55       2 Connect     root@localhost on test
                      2 Query       SET AUTOCOMMIT=1
                      2 Query       CREATE TABLE `test1` (num int not null)
                      2 Query       INSERT INTO `test1` VALUES (1), (2)
                      2 Prepare     [1]
                      2 Execute     [1] SELECT * FROM test1
                      2 Prepare     [2]
                      2 Execute     [2] SELECT * FROM test1 WHERE num = 1
                      2 Prepare     [3]
                      2 Execute     [3] SELECT * FROM test1 WHERE num = 2
                      2 Query       DROP TABLE `test1`
                      2 Quit
НО:
Код:
mysql> CREATE TABLE `test1` (num int not null);
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO `test1` VALUES (1), (2);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM test1;
+-----+
| num |
+-----+
|   1 |
|   2 |
+-----+
2 rows in set (0.00 sec)

mysql> SELECT * FROM test1 WHERE num = 1;
+-----+
| num |
+-----+
|   1 |
+-----+
1 row in set (0.00 sec)

mysql> SELECT * FROM test1 WHERE num = 2;
+-----+
| num |
+-----+
|   2 |
+-----+
1 row in set (0.00 sec)

mysql> DROP TABLE `test1`;
Query OK, 0 rows affected (0.00 sec)
P.S.: мне можно и не возмущаться
Код:
Driver name	Supported databases
PDO_MYSQL	MySQL 3.x/4.x
Тогда странно, что prepare/execute работает...
[off]
P.P.S.: что за рефлексы у народа -- лезть в рут, потом в рут домена и т.д.
[/off]
 

crocodile2u

http://vbolshov.org.ru
Вот еще один пример.

PHP:
function query($query) {
	static $pdo;
	if ($pdo === null)
		$pdo = new PDO('mysql:host=localhost;dbname=test', 'root');
	
	return $pdo->query($query);
}

$sql_create = "CREATE TABLE `test1` (num int not null)"; 
$sql_insert = "INSERT INTO `test1` VALUES (1), (2)"; 
$sql_drop = "DROP TABLE `test1`"; 
$sql_select = "SELECT * FROM test1"; 
$sql_select1 = "SELECT * FROM test1 WHERE num = 1"; 
$sql_select2 = "SELECT * FROM test1 WHERE num = 2";

query($sql_create);
query($sql_insert);
echo "<pre>";
foreach (query($sql_select) as $row) {
	var_dump($row);
}

foreach (query($sql_select1) as $row1) {
	foreach (query($sql_select2) as $row2) {
		// Вложенный цикл НИЧЕГО НЕ ВЫВОДИТ
		var_dump($row2);
	}
	var_dump($row1);
}
echo "</pre>";
query($sql_drop);
Ожидаемый вывод:
array(2) {
["num"]=>
string(1) "1"
[0]=>
string(1) "1"
}
array(2) {
["num"]=>
string(1) "2"
[0]=>
string(1) "2"
}
array(2) {
["num"]=>
string(1) "2"
[0]=>
string(1) "2"
}
array(2) {
["num"]=>
string(1) "1"
[0]=>
string(1) "1"
}

Реальный вывод:
array(2) {
["num"]=>
string(1) "1"
[0]=>
string(1) "1"
}
array(2) {
["num"]=>
string(1) "2"
[0]=>
string(1) "2"
}
array(2) {
["num"]=>
string(1) "1"
[0]=>
string(1) "1"
}

Этого я уж совсем не понимаю...
 

Develar

Новичок
crocodile2u
Обратите внимание на 2 сообщения последних. То есть ответьте что у Вас за MySQL.
 

Develar

Новичок
RTFM до прояснения

-~{}~ 06.02.06 12:50:

Напишите после

$pdo = new PDO('mysql:host=localhost;dbname=test', 'root');

вот это

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);

и будет Вам счастье...
 

crocodile2u

http://vbolshov.org.ru
Develar
Это я уже понял. Я не понимаю, какие механизмы приводят к тем результатам, которые получаются без этой "магической" :) строки - раз. И два - мне кажется, результаты выполнения приведеного мной кода нелогичные. С удовольствием выслушаю аргументы в пользу обратной точки зрения.
 

Develar

Новичок
Я уже объяснил в моем сообщении выше, но из уважения напишу ещё раз.
1) PDO по умолчанию использует, в отличии от mysql_*, unbuffered query. Пока Вы не обработали предыдущий результат Вы не можете отправлять другой результат - одновременно пить, есть и говорить у вас не получится.
PDO в ответ на query($sql_select2) выдает false, а PHP, соответственно, что не может использовать тип bool как array в foreach.
Когда же Вы при соединении или при подготовке выражения устанавливаете атрибут PDO::MYSQL_ATTR_USE_BUFFERED_QUERY это означает, что он будет результат буферизовать - вы сначала выпьете, потом скушаете, потом начнете говорить - и все это скроете от зрителя - для него это одновременно. Понятно, что эта хитрость оборачивается накладными расходами.
2) проистекает из первого пункта, при максимальном уровне error report ваш код не выполнится.

Что делать написано тут http://ru2.php.net/pdo-mysql
 

Profic

just Profic (PHP5 BetaTeam)
crocodile2u
Поставь в начало, после $pdo = new PDO(...)
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
и поймешь, что код ведет себя вполне логично - обработки ошибок то нет.

Но вот почему в одном случае используется buffered api, а в другом - нет, остается загадкой.
 
Сверху