Nette\Database: Chybné escapování řetězce
- nanuqcz
- Člen | 822
Ahoj, mám kód:
$db->table('property')
->select('property.*')
->select("
COUNT((
SELECT `id`
FROM `propertyHistory`
WHERE `property_id` = `property`.`id`
AND `limitations` != 'Nejsou evidována žádná omezení.' // zde je problém
)) AS historyLimitationsCount
")
->group('property.id');
který ale Nette\Database přeloží do SQL:
SELECT
`property`.*,
COUNT((
SELECT `id`
FROM `propertyHistory`
WHERE `property_id` = `property`.`id`
AND `content_limitations` != '`Nejsou` `evidov`á`na` žá`dn`á `omezen`í.' // zde je problém
)) AS `historyLimitationsCount`
FROM `property`
GROUP BY `property`.`id`
Proč se snaží escapovat řetězec v uvozovkách jako název sloupečku?
Díky
P.S. Dokonce i když to obalím do new SqlLiteral(...)
, tak je
výsledek stejný.
Editoval nanuqcz (20. 3. 2013 11:27)
- enumag
- Člen | 2118
SqlLiteral zatím není podporován v klauzuli select. Viz pull request.
EDIT: Myslím že tohle bys měl řešit přes
$row->related(...)->count(...)
.
Editoval enumag (20. 3. 2013 12:21)
- nanuqcz
- Člen | 822
Dalším důvodem může být filosofie MVC. Není správné mít v presenteru, nebo dokonce v šabloně toho:
Počet omezení: {$property->related('propertyHistory')->where('propertyHistory:limitations != ?', 'Nejsou evidována žádná omezení.')->count()}
Tady pomůže buď použít v modelu $db->query()
, anebo
postavit si nad Nette databází masivní ORM, které všechny podobné
případy bude řešit. Anebo opravit NDB, aby umožňovala programátorům si
trochu pomoct, jako to dělám já v příkladu výše.
Editoval nanuqcz (20. 3. 2013 12:36)
- hrach
- Člen | 1838
nanuqcz napsal(a):
Výsledek z toho dotazu cpu do datagridu, takže related použít nelze.
Lol, takže že je špatně navržen datagrid je důvod, proč ohýbat
databázovou vrstvu. To je přesně důvod, proč vznikl nextras/datagrid.
(Ted pri psani koukam, ze to tu uz padlo).
Obecne:
- jedinym spravnym (nefunkcnim) resenim je pouzit SqlLiteral.
- nefunkcnim proto, ze to neni naprogramovane, zatim
- dany pull request to resi spatne
- chci to udelat, az mi David mergne #945.
- SqlLiteral je vhodne reseni:
- nebude se z neho provadet join
- jedna se o pripraveny sql dotaz (vyraz, → literal), nemel by se na nej uz sahat
Editoval hrach (20. 3. 2013 13:03)
- nanuqcz
- Člen | 822
Právě jsem narazil na další případ potřeby předávat do
->select()
klasický string:
$db->table('person')
->select("id, name, AES_DECRYPT(secured_note, '$aesPassword') AS secured_note");
vygeneruje:
SELECT `id`, `name`, AES_DECRYPT(`secured_note`, '`tajne_heslo`') AS `secured_note`
FROM `person`
EDIT: Prozatím jsem byl tedy nucen zasáhnout přímo do zdrojáků Nette.
Kdyby někdo řešil stejný problém, tak hotfix může být upravení metody
tryDelimite()
ve třídě SqlBuilder
:
protected function tryDelimite($s)
{
$driver = $this->driver;
$return = preg_replace_callback('#(?<=[^\w`"\[]|^)[a-z0-9_]*(?=[^\w`"(\]]|\z)#i', create_function('$m', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('driver'=>$driver)).'-1], EXTR_REFS);
return strtoupper($m[0]) === $m[0] ? $m[0] : $driver->delimite($m[0]);
'), $s);
return preg_replace("#'`([^']+)`'#", "'\\1'", $return); // hotfix for https://forum.nette.org/cs/13962-nette-database-chybne-escapovani-retezce#p100243
}
Editoval nanuqcz (22. 3. 2013 13:01)