Hallo,

Ik zit met een bug in mijn zelf geschreven CMS waar ik geen oplossing voor kan vinden.
Eerst even de foutmelding in de Apache Error Log.

[Sat Nov 19 10:32:37 2011] [error] [client 127.0.0.1] PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'wz_cms.nl_images' doesn't exist' in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php:442\nStack trace:\n#0 C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php(442): PDO->query('SELECT * FROM `...')\n#1 C:\\xampp\\htdocs\\testsite\\index.php(7): include('C:\\xampp\\htdocs...')\n#2 {main}\n thrown in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php on line 442, referer: http://www.testsite.nl/css/site.css
[Sat Nov 19 10:32:37 2011] [error] [client 127.0.0.1] PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'wz_cms.nl_css' doesn't exist' in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php:442\nStack trace:\n#0 C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php(442): PDO->query('SELECT * FROM `...')\n#1 C:\\xampp\\htdocs\\testsite\\index.php(7): include('C:\\xampp\\htdocs...')\n#2 {main}\n thrown in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php on line 442, referer: http://www.testsite.nl/css/site.css
[Sat Nov 19 10:32:37 2011] [error] [client 127.0.0.1] PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'wz_cms.nl_css' doesn't exist' in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php:442\nStack trace:\n#0 C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php(442): PDO->query('SELECT * FROM `...')\n#1 C:\\xampp\\htdocs\\testsite\\index.php(7): include('C:\\xampp\\htdocs...')\n#2 {main}\n thrown in C:\\xampp\\htdocs\\testsite\\cms\\load_site_V3.php on line 442, referer: http://www.testsite.nl/css/site.css

Ok, wat er gebeurt...

Ik doe een query in mijn code, hier maak ik gebruik van PDO.

dit is de query :

$sql = "SELECT * FROM `$itemInDatabase` WHERE active='yes'";
$results = $db->query($sql);

Het ding is, dat ik precies weet wat de variabele $itemInDatabase bevat, maar toch wordt er volgens de error log op deze plaats gezocht naar : nl_images en nl_css

Deze tabellen bestaan niet, en zullen ook nooit bestaan, deze zoekopdracht resulteert er in dat ik de webserver van mijn webhoster plat trek. Dus, ik kan de code daar niet draaien.

Ik weet ook hoe mijn script min of meer aan nl_images en nl_css komt,
Ik gebruik nl_ als prefix voor tabellen die gegevens bevaten, het is een meertalig CMS wat ik gebruik, vandaar nl_

Images en CSS zijn mappen op de webserver, de enige manier hoe mijn script kan weten dat deze mappen bestaan, is, omdat in mijn template een verwijzing zit naar een css bestand in de map /css en in mijn css bestand zit een verwijzing naar de map /images.

Op geen enkel punt in mijn script noem ik deze mappen.

Als mijn script de gegevens uit de database heeft verzameld, assigned het alles met de Smarty Template engine en wordt de pagina opgebouwd op basis van de template.

Nu, als ik IN de template, de verwijzing naar mijn CSS file verwijder, dan heb ik nergens last van, krijg ik geen foutmelding in mijn error log.
Echter, dan heb ik ook geen normale vormgeving.

Als ik de verwijzing in mijn template naar het CSS bestand aanzet, dan komen op de een of andere manier de 2 mappen die ik noemde terecht in de query waar de foutmelding door onstaat.

Het lijkt er dus op dat Smarty de CSS file lekt naar mijn script zodat mijn script de 2 mappen die hij daar uit weet te halen in mijn query stopt.

Het vreemde is ook, als ik de variabele $itemInDatabase check op de waarde, de namen van deze mappen er NOOIT in voorkomen.

Ik wordt onderhand echt helemaal gek, ben nu meer dan 20 uur bezig met deze bug op te lossen, maar ik heb het idee dat mijn script wel goed is maar er een andere bug plaats vind waar ik geen weet van heb en die buiten mijn code zich voortdoet.

Iemand die begrijpt wat er gebeurt of er wat over zeggen kan?

Alvast bedankt!
Wel een een zwam verhaal. De essentie is echter dat je al de fout in gaat om op deze manier sql statements in elkaar te flansen:

<?php
$sql = "SELECT * FROM `$itemInDatabase` WHERE active='yes'";
?>


1. het gebruik van backticks
2. een SQL statement kan je dan wel dynamisch opbouwen, maar het is niet slim om dat ook te doen met tables en en/of views in de FROM-clause
3. het grootste euvel met dit is dat je nu niet eenvoudig de zaak kunt oplossen, maar je hele code zal moeten doorspitten met wat er allemaal gebeurt met $itemInDatabase.

Conlcusie: bouw geen dynamische queries als je het af kan met "constante" sql statements. Constant is alles behalve de WHERE-clause.
Je bent dus totaal de verkeerde weg ingeslagen bij het opzetten van je cms-framework code.
Dynamische tabel namen is wel tricky inderdaad, om het hele CMS af te schrijven dat zou ik niet doen want ik heb de rest nog niet gezien. Vertalen met databases heb je hele mooie andere methodes voor als je ORM gebruikt.

Mocht je nou niet kunnen vinden waar die nl_css vandaan komt zou ik je code doorlopen met xdebug regel voor regel. Ik weet niet of je lokaal ontwikkeld?
Dank voor de reacties mannen, ik heb zojuist de oplossing gevonden.

Wat betreft de dynamische tabelnamen, ik heb een statische tabel waarin ik de dynamische namen van de overige tabellen in bij hou.
Hier heb ik verder ook geen problemen mee, er wordt altijd eerst in de statische tabel gekeken of een tabel aanwezig is, daarna wordt de betreffende tabel pas opgevraagd.

Ik ben het er mee eens dat ik dit beter anders aan had kunnen pakken, maar het CMS is reeds klaar en ik stuitte op deze bug.

De oorzaak heb ik gevonden, want hoewel het renderen van een pagina normaal leek te werken, werd mijn script niet afgesloten als de variabele $itemInDatabase niet voldeed aan een voorwaarde.

De bedoeling was, dat als iemand een item opvraagt via een url, deze gecontroleerd wordt op aanwezigheid, zoja, wordt weergegeven, zo nee, het script wordt opnieuw uitgevoerd met standaard gegevens.

Dit deed ik als volgt :

header ('Location: http://'.$site.'/'.$item.'/'.$language.'/1/'.$title);

Echter, ik had geen : exit; achter deze redir gezet, waardoor het script nog doorliep, en er op een of andere manier iets lekte naar het script toe.

Nu ik exit; achter het doorverwijzen naar een geldige link heb gezet wordt de query met de foutieve tabellen ook niet meer uitgevoerd.

Bedankt voor het meedenken!

Deze tabellen bestaan niet, en zullen ook nooit bestaan

Waarom sta je het dan wel toe om hier queries op uit te voeren? Jij weet exact welke tabellen er in de database bestaan, sta dus onder geen enkele voorwaarde toe dat er iets anders als tabelnaam KAN worden gebruikt in de SQL. Je vraagt hier om grote problemen, ik ken de rest van de code niet, maar hier loert het gevaar op SQL injection.

Backticks ` in SQL zijn al helemaal not done, dat duidt op brakke SQL en is al helemaal niet compatible met andere databases. Zelfs in MySQL kan dit problemen opleveren, afhankelijk van de configuratie. Of heb je nog nooit van sql_mode ANSI gehoord? Zo niet, ga de rest van de SQL dan ook maar eens debuggen...
Op je vraag waarom ik toesta dat er een query op een tabel wordt uitgevoerd waarom dat niet zo mogen is het antwoord, ik teste de variabele $itemInDatabase wel op een geldige inhoud, indien deze niet geldig was werd er geredirect naar een geldige url.
Echter, ik vergat het script op dat punt te beeindigen met exit;

Waardoor het script doorliep met een ongeldige waarde.

Op ieder punt in mijn CMS controleer ik of zoekopdrachten die via de url binnenkomen geldig zijn of niet, op ieder punt waar iets niet geldig is wordt het script afgebroken en wordt de bezoeker doorgestuurd naar een geldige url.

Wat betreft het gebruik van backticks, ik zie daar niet echt het probleem van in, MySQL kan daar goed mee overweg, ik ben ook niet van plan om iets anders te gaan gebruiken dan MySQL, als ik dit verkeerd zie, dan hoor ik graag waarom.

Ook het gebruik van dynamische tabellen, daar zie ik ook niet echt het probleem van in, ik controleer alle invoer van de gebruiker en bied alleen valide links aan waar gebruikers op kunnen klikken.

Maar ik wil wel uitleggen wat ik heb gedaan.

Een gebruiker kan een taal aanmaken in het cms waarin hij gegevens wil beheren.

Tabellen krijgen namen als

nl_over-ons-bedrijf

Waarbij, over ons bedrijf dan een hoofd menu is, in deze tabel zitten dan artikelen opgeslagen die een onderdeel zijn van 'over-ons-bedrijf'

Beschouw 'over-ons-bedrijf' als een item, deze items zitten opgeslagen in een tabel

nl_items

In nl_items zitten de seo friendly urls opgeslagen, de namen waarmee menu's worden weergegeven (en nog andere gegevens)

Een gebruiker kan dus een menu aan maken, deze wordt opgeslagen in nl_items
en het menu krijgt de naam 'Over ons bedrijf' en in de url krijgt dit menu 'over-ons-bedrijf'.

Na het invoeren van deze url :

www.testsite.nl/over-ons-bedrijf/nl/1/lokatie/

Wordt een in tabel nl_items gecontroleerd of over-ons-bedrijf een tabel kent die zo heet, als dat zo is, dan wordt artikel met ID 1 opgehaald.

Bestaat de tabel niet, dan wordt automatisch naar een default pagina doorgestuurd.

Het is misschien niet de meest efficienste manier van werken, maar het werkt goed.
Als ik maar wel na het redirecten na een foutieve invoer het script beeindig met exit;
Want anders krijg ik dus problemen zoals in mijn startpost.

Graag hoor ik hoe ik wat ik wil bereiken op een betere manier kan aanpakken als men nog steeds van mening is dat wat ik doe niet juist is. :-)
Gee aaa op 19/11/2011 20:09:04

Wat betreft het gebruik van backticks, ik zie daar niet echt het probleem van in, MySQL kan daar goed mee overweg, ik ben ook niet van plan om iets anders te gaan gebruiken dan MySQL, als ik dit verkeerd zie, dan hoor ik graag waarom.


Backticks zijn geen onderdeel van de SQL standaarden en werken in geen enkele database, behalve MySQL. Maar omdat MySQL diverse sql_mode's kent, is het zelfs niet zeker dat backticks in iedere sql_mode werken.

Wanneer jij standaard SQL wilt schrijven en ook de mogelijkheid wilt hebben om kolom- en tabelnamen tussen "iets" te zetten, gebruik dan dubbele quotes ". Die zijn namelijk wél standaard in SQL, dat werkt in Oracle, SQL Server, DB2, PostgreSQL, SQLite, etc. En dus ook in MySQL, mits je dit aangeeft in de sql_mode die jij gebruikt.

Simpel voorbeeldje:

SET sql_mode = ANSI_QUOTES, PIPES_AS_CONCAT;

SELECT 
  "nAam" || ' ' || "achternaam" AS complete_naam
FROM 
  "Tabel" 
WHERE 
  "nAam" = 'Bart';

Dit SELECT statement werkt op vrijwel ieder merk database, mits de tabel- en kolomnamen aanwezig zijn... :-)

En omdat je toch al geen bugs in de SQL wilt hebben, MySQL is berucht om onmogelijke constructies, moet je dit toch al instellen nadat je een database connectie hebt opgezet:

SET sql_mode = PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION;


Op http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_traditional kun je lezen wat deze settings precies doen.
Ok thanks,

Ik kan dit alleen niet zo instellen zoals jij aangeeft, ik krijg dan deze foutmelding in mijn browser :

Foutmelding: SQLSTATE[42000]: Syntax error or access violation: 1231 Variable 'sql_mode' can't be set to the value of ' ANSI_QUOTES'

Heeft de MySQL verbindingscollatie hier ook mee te maken?
Probeer het eens tussen quotes:

SET sql_mode = 'PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION'; 


Ik heb geen MySQL tot mijn beschikking en kan het dus niet testen.


[size=xsmall]Toevoeging op 20/11/2011 11:30:59:[/size]

Of deze met dubbele quotes en zonder spaties:

SET sql_mode = "PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"; 
Zo, Thanks!

Ik heb het nu zo gedaan :

$db->query("SET SESSION sql_mode = 'PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'");

Tabellen die een underscore bevatten, kunnen nu zonder backticks in de query worden opgevraagd!

Thanks man! :)
Een underscore is nog nooit een probleem geweest in de naamgeving, dat heeft echt niks met de sql_mode of backticks te maken. Denk dus niet dat jouw probleem met underscores is opgelost doordat je nu een andere sql_mode gebruikt, jouw probleem is per ongeluk opgelost of bestaat nog steeds.

Reageren