Nette\Database: Chybné escapování řetězce

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
nanuqcz
Člen | 822
+
0
-

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
+
0
-

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
+
0
-

enumag: Výsledek z toho dotazu cpu do datagridu, takže related použít nelze.

Myslím, že by stačilo rozšířit pravidlo pro escapování sloupečků z „Escapuj, pokud to není velkými písmeny“ na „Escapuj, pokud to není velkými písmeny, nebo v uvozovkách“.

enumag
Člen | 2118
+
0
-

Ano, předávání dat do datagridu je nejčastější případ proč lidi tvrdí že to potřebují. To je ale problém toho datagridu, ne NDB. Použij datagrid s lepším API, třeba ten @hrachův se mi libil.

nanuqcz
Člen | 822
+
0
-

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)

enumag
Člen | 2118
+
0
-
nanuqcz
Člen | 822
+
0
-

Na YetORM jsem se ještě nedíval, díky za tip ;-)

Každopádně si pořád stojím za tím, že vygenerování SQL kódu

`content_limitations` != '`Nejsou` `evidov`á`na` žá`dn`á `omezen`í.'

je chyba.

hrach
Člen | 1838
+
0
-

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)

hrach
Člen | 1838
+
0
-

nanuqcz napsal(a):
Každopádně si pořád stojím za tím, že vygenerování SQL kódu

`content_limitations` != '`Nejsou` `evidov`á`na` žá`dn`á `omezen`í.'

je chyba.

Neni, protoze to neni validni nazev sloupce.

nanuqcz
Člen | 822
+
0
-

hrach napsal(a):

Neni, protoze to neni validni nazev sloupce.

Není to název sloupce, je to řetězec (vždyť je přece v uvozovkách).

hrach
Člen | 1838
+
0
-

Jo, a cpeš ho do metody, ktera očekava na vstupu nazev sloupce.

ViPEr*CZ*
Člen | 817
+
0
-

Má ten grid nějakou stránku kam se dá napsat třeba nápad nebo nějakej přehled dalšího vývoje?

nanuqcz
Člen | 822
+
0
-

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)