Nedokonalé zjišťování zdroje SQL dotazu

kminekmatej
Generous Backer | 9
+
0
-

Hola hej,
mám v Nette projekt, kde na vstupu používám Presenter, logiku následně obstarávám v zanořených managerech. Manager pracuje s DB vrstvou a pokládá dotazy. Výsledek vrátí zpět Presenteru a ten jej zobrazí jako JSON (ano, api).

Když mi pak ale v následném běhu presenteru vypadne jiný Exception, tak mi správně vyleze laděnka s panelem SQL dotazů. Nicméně u každého SQL dotazu je jako zdroj uvedený soubor index.php, line 13 – což je run(). Laděnka totiž zjišťuje soubor, který vyvolal SQL příkaz takto:

nette\database\src\Bridges\DatabaseTracy\ConnectionPanel.php:68

$trace = $result instanceof \PDOException
			? $result->getTrace()
			: debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

foreach ($trace as $row) {
	if (
		(isset($row['file']) && is_file($row['file']) && !Tracy\Debugger::getBluescreen()->isCollapsed($row['file']))
		&& ($row['class'] ?? '') !== self::class
		&& !is_a($row['class'] ?? '', Connection::class, true)
	) {
		$source = [$row['file'], (int) $row['line']];
		break;
	}
}

Problematické je podle mě právě podmínka rozbalení: !Tracy\Debugger::getBluescreen()->isCollapsed($row['file']). Soubory, které proběhly v manageru předtím totiž nejsou v Tracy rozbalené a tak jsou přeskočeny. Ve výsledku teď můj výpis příkazů vypadá například takto:

SELECT `cs`
FROM `v_translation`
WHERE (`name` = 'A200') AND (`module` = 'core')
.../boost.kminet.eu/api/www/index.php:12

Funkce která zjišťuje zdrojový soubor je tedy nedokonalá. Napadají mne dvě možný řešení, jedno blbější než druhé:

  1. Najít každou “vnitřní” Nette funkci, která se volá bezprostředně po vyvolání dotazu. Tedy fetch(), fetchAll(), update(), insert(), get() apod. – a v každé z nich (pokud je zapnutý Debugger) si vyhodit backtrace a vzít jako source předchozí soubor. Ale u toho mi vadí mísení logiky balíčků Nette/Database a Nette/Tracy.
  2. Upravit detektor zdrojového souboru tak, aby byl nějak šikovnější. Úprava co mne napadla (a u mne funguje) je, že zdrojový soubor je první soubor v backtrace dump, který není součástí Nette namespace (to by samozřejmě nefungovalo pro dotazy ze samotného Nette. Vyvíjená aplikace by samozřejmě nesměla být v namespacu Nette. Má úprava:
if (
	(isset($row['file']) && is_file($row['file']))
	&& ($row['class'] ?? '') !== self::class
	&& strpos($row['class'] ?? '', "Nette\\") === false
) {
	$source = [$row['file'], (int) $row['line']];
	break;
}

Jsem si téměř jist že řešení, které pomůže mne, naopak rozbije nějaké jiné usecasy, které teď prostě nevidím. Možná by to šlo nějak zkombinovat? Typu použij první řešení, pokud dojedeš až na index.php, použij druhé řešení. Díky za jakékoliv náměty v diskusi.

David Grudl
Nette Core | 7505
+
0
-

Jak vypadá ten stacktrace?

kminekmatej
Generous Backer | 9
+
0
-

Samotné volání je v souboru TranslationManager:93:

return $db->fetchField($lang) ?: $key;

Jak tak studuji ten stacktrace, tak i ta má úprava není úplně dokonalá, neboť by vrátila Responder.php:112 (tedy o jeden trace dříve) – můžu ji o ten jeden upravit, ale je to spíš k diskusi.

Stacktrace:

[
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/utils\/src\/Utils\/Arrays.php",
      "line":361,
      "function":"logQuery",
      "class":"Nette\\Bridges\\DatabaseTracy\\ConnectionPanel",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Connection.php",
      "line":193,
      "function":"invoke",
      "class":"Nette\\Utils\\Arrays",
      "type":"::"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Explorer.php",
      "line":93,
      "function":"query",
      "class":"Nette\\Database\\Connection",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Table\/Selection.php",
      "line":597,
      "function":"queryArgs",
      "class":"Nette\\Database\\Explorer",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Table\/Selection.php",
      "line":547,
      "function":"query",
      "class":"Nette\\Database\\Table\\Selection",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Table\/Selection.php",
      "line":210,
      "function":"execute",
      "class":"Nette\\Database\\Table\\Selection",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/database\/src\/Database\/Table\/Selection.php",
      "line":228,
      "function":"fetch",
      "class":"Nette\\Database\\Table\\Selection",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/translation\/manager\/TranslationManager.php",
      "line":93,
      "function":"fetchField",
      "class":"Nette\\Database\\Table\\Selection",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/core\/manager\/Responder.php",
      "line":112,
      "function":"getByKey",
      "class":"BoostSpace\\Module\\Translation\\Manager\\TranslationManager",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/core\/manager\/Responder.php",
      "line":67,
      "function":"composeMessage",
      "class":"BoostSpace\\Core\\Manager\\Responder",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/core\/manager\/Responder.php",
      "line":173,
      "function":"respond",
      "class":"BoostSpace\\Core\\Manager\\Responder",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/core\/presenters\/BaseRestPresenter.php",
      "line":161,
      "function":"A200_OK",
      "class":"BoostSpace\\Core\\Manager\\Responder",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/user\/presenter\/DefaultPresenter.php",
      "line":64,
      "function":"respondOk",
      "class":"BoostSpace\\Core\\Presenter\\BaseRestPresenter",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/boost.space\/user\/presenter\/DefaultPresenter.php",
      "line":46,
      "function":"requestGet",
      "class":"BoostSpace\\Module\\User\\Presenter\\DefaultPresenter",
      "type":"->"
   },
   {
      "function":"actionDefault",
      "class":"BoostSpace\\Module\\User\\Presenter\\DefaultPresenter",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/application\/src\/Application\/UI\/Component.php",
      "line":102,
      "function":"invokeArgs",
      "class":"ReflectionMethod",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/application\/src\/Application\/UI\/Presenter.php",
      "line":213,
      "function":"tryCall",
      "class":"Nette\\Application\\UI\\Component",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/application\/src\/Application\/Application.php",
      "line":160,
      "function":"run",
      "class":"Nette\\Application\\UI\\Presenter",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/vendor\/nette\/application\/src\/Application\/Application.php",
      "line":89,
      "function":"processRequest",
      "class":"Nette\\Application\\Application",
      "type":"->"
   },
   {
      "file":"\/var\/www\/vhosts\/kminet.eu\/subdomains\/boost.kminet.eu\/api\/www\/index.php",
      "line":12,
      "function":"run",
      "class":"Nette\\Application\\Application",
      "type":"->"
   }
]
David Grudl
Nette Core | 7505
+
0
-

Asi by se mohlo do collapsePaths místo celého vendor zahrnout jen vendor/nette a vendor/tracy. Zkus master.

kminekmatej
Generous Backer | 9
+
0
-

David Grudl napsal(a):

Asi by se mohlo do collapsePaths místo celého vendor zahrnout jen vendor/nette a vendor/tracy. Zkus master.

Funguje to skvěle, dík! Takže celý problém byl vlastně způsoben tím že pracuji ve složce vendor