PDO - Base table or view not found
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!
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.
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?
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!
Gewijzigd op 19/11/2011 11:58:48 door Sealand man
Quote:
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...
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:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
SET sql_mode = ANSI_QUOTES, PIPES_AS_CONCAT;
SELECT
"nAam" || ' ' || "achternaam" AS complete_naam
FROM
"Tabel"
WHERE
"nAam" = 'Bart';
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:
Code (php)
1
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.
Gewijzigd op 20/11/2011 10:26:15 door Bartje Jansen
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?
Gewijzigd op 20/11/2011 11:03:42 door Sealand man
Code (php)
1
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.
Toevoeging op 20/11/2011 11:30:59:
Of deze met dubbele quotes en zonder spaties:
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.
Toevoeging op 20/11/2011 13:24:18:
hmm, ik kan nog steeds geen hyphens gebruiken in tabelnamen wanneer ik geen backticks gebruik.
Irritant.
Sealand man op 20/11/2011 13:01:01:
sorry, ik bedoel het hyphen-teken '-'.
En dat is dus not done. Vergeet 1 keer de quotes " of evt. backticks ` en jouw SQL gaat met een beetje pech een berekening uitvoeren en jij zoekt je een ongeluk naar de bug in jouw code. Mocht je de bug toevallig al zien... En die quotes ga je op een dag vergeten, fouten maken is menselijk.
Zorg voor een naming convention en vermijdt tekens en namen die voor ongewenst gedrag kunnen zorgen. De + en - vallen in elk geval in die categorie, mogen nooit voorkomen in de namen van een variabele, kolom, tabel, database, etc. Met een simpel scriptje kun je checken of jouw database wel aan de namingconvention voldoet, het information_schema in MySQL kan hierbij helpen.
Of wil je echt zo graag bugs in je code?
Als ik daarmee van de backticks af kan zijn is dat mooi meegenomen.
Ik moet wel zeggen dat het gebruik van backticks voor mij verder niet echt verwarrend werkt, ik weet dat ik ze moet toepassen bij tabellen waar hyphens in zitten.
En nee, liever heb ik geen bugs in mijn code.
En op zich valt het ook wel mee verder, mijn CMS is nu ca 230Kb groot (aan eigen code) en alles werkt prima, afgezien van dit ene stukje dan. :-)
Gewijzigd op 20/11/2011 13:57:47 door Sealand man