Николай Ланец
28 нояб. 2013 г., 2:43

Баг в xPDOQuery::set(). Округление десятичных.

Сегодня наткнулся на жесточайшую багу…
У кого развернута сборка магазина или хотя бы стоит пакет shopModx, попробуйте в консоли выполнить вот такой код:
<?php print '<pre>'; $q = $this->modx->newQuery('ShopmodxProduct'); $q->command('update'); $q->set(array( 'sm_price' => 154.99, )); if($q->prepare()){ print $q->toSQL(); }
Результат: UPDATE `modx_shopmodx_products` SET `sm_price` = 154 Не 154.99, а просто 154… Думаю, каждый понимает печальность последствий, особенно когда полно товаров с ценниками типа 0.99 Но что тут самое интересное? А то, что если просто выполнить вот так, то все будет ОК:
$o = $modx->getObject('ShopmodxProduct', $id); $o->set('sm_price', 154.99); $o->save();
Первое: такое переменчивое поведение связано с тем, что через разные методы используются различные механизмы формирования запросов. К примеру, при вызове xPDOObject::save() у нас вполне явный SQL формируется. А вот при использовании xPDOQuery::set() уже механизм другой. И здесь самая западня вот в этих строчках:
elseif (!in_array($fieldMeta[$key]['phptype'], $this->_quotable)) { $type= PDO::PARAM_INT; }
А какие типы у нас quotable? 'string', 'password', 'date', 'datetime', 'timestamp', 'time'
Как видим, там нет ни double, ни float. В итоге все приводится к INT-типу, а это, как известно, не подразумевает дробные числа. При этом в PDO в принципе нет ничего типа FLOAT или типа того. В сети полно вопросов PDO::PARAM_FLOAT does not exist, why?. В итоге, видится только один вариант — определить float и double как строковые. А там сама база данных пусть занимается вопросами конвертации, она это обязательно сделает. Отправил PR.
Честно говоря, бага на столько скрытая, что я даже уже почти забил… Но в последний момент чутье вывело меня к нужной строчке. *немного похвастаюсь)))*

Добавить комментарий