SQLite

SQLite on yksinkertainen tekstitiedostopohjainen tietokanta, jota voidaan käyttää esim. PHP-ohjelmissa. SQLiteen liittyy muutamia erikoispiirteitä, joita käydään läpi tässä dokumentissa.

Virheilmoitukset

Erityisesti PHP 5.0:n ja PDO-rajapinnan kautta käytettäessä SQLite saattaa joskus antaa erikoisia virheilmoituksia:

Library routine called out of sequence

Yleensä näitä virheitä tulee yhden todellisen virheilmoituksen jälkeen, jos tietokantaoperaatioita tehdään nopeaan tahtiin. Luultavasti SQLite jostain kuvittelee, että tietokantayhteyttä yritetään käyttää eri prosessista kuin missä se on alunperin avattu ja hermostuu tästä. Ongelma on pyritty korjaamaan uudemmissa SQLiten versioissa (> 3.3.1).

Ongelman saa yleensä kierrettyä preparoimalla uudelleen käsittelyn alla olevan kyselyn. Monesti tämä poistaa preparoinnilla saadut hyödyt, mutta sallii kuitenkin sovelluksen toimimisen.

SQL logic error or missing database

Tämä virhe tulee yleensä kun tietokannan sisältöä yritetään muuttaa WWW-liittymän kautta eikä tietokantatiedostoon/kansioon ole annettu kirjoitusoikeutta.

Esimerkiksi users.jyu.fi:ssä on annettava hakemistolle kaikille w-oikeus ja tietokantatiedostoon kaikille w-oikeus, koska PHP-skriptin suorittaja on apache-tunnus.

Unable to open database file

Kts. yllä.

Call to a member function on a non-object

Tämä virhe saattaa tulla, kun virheellisen kyselyn preparointi ei onnistu ja PDO-kyselyoliota ei synny. Sama virhe voi tulla, jos tietokantayhteys ei muodostu. Muista aina varmistaa onnistuuko preparointi:

$sql = $db->prepare("SELECT * FROM example");
// huomaa, että virheilmoitus pitää tässä pyytää tietokantayhteys-oliolta
if(!$sql) { print_r($db->errorInfo()); }

Database table is locked

Varmista, että sinulla ei ole avattuna tietokantaan lukuyhteyttä silloin, kun yrität samaan aikaan päivittää tietokantaa. Luettaessa tietokantaa SQLite ei salli kantaan muutoksia ennen kuin lukeminen lopetetaan.

Virhe tulee, kun lukee riveittäin läpi tietokantataulua ja samalla muokkaa sen sisältöä. Virhe tulee myös, kun tekee SELECT-kyselyn, mutta ei käy vastausjoukkoa läpi. Ongelman voi kiertää hakemalla kaikki kyselyn vastaukset kaksiulotteiseen taulukkoon fetchAll()-metodilla. Esim.

$sql_select = $dbh->prepare("SELECT id, nimi FROM foobar");
$sql_delete = $dbh->prepare("DELETE FROM foobar WHERE id = :id");

if(!$sql_select->execute())
  print_r($sql_select->errorInfo());

$resultset = $sql_select->fetchAll();
foreach($resultset as $row) {
  if(...ehto...) {
    $id = $row['id'];
    $sql_delete->bindParam(":id",$id);
    $sql_delete->execute();
  } else {
    echo $row['nimi'];  
  }
}

Safe_mode/open_basedir prohibits opening

Viite tietokantatiedostoon users.jyu.fi:ssä pitäisi olla muotoa:

sqlite:/homeX/YZ/tunnus/html/polku/tietokantatiedosto

Hakemistopolun saa komennolla:

echo $HOME

Class 'PDO' not found

Testaaminen komentoriviltä ei toimi, koska jalavassa hakemistopolku on erilainen kuin varsinaisella users.jyu.fi-palvelimella. PDO-laajennusta ei ole myöskään Eclipse-PHP:n kanssa ja hakemistopolkukin olisi väärä, koska PHP-tulkki on tällöin lokaalilla koneella. Testaus täytyy siis tehdä suoraan selaimessa.

Tiedon lisäämisen mahdolliset ongelmat

PDO:n metodi fetch(PDO_FETCH_ASSOC) ei toimi

PHP:n uudemmissa versioissa vakiomuuttujat on muutettu PDO-nimiavaruuden alle. PHP 5.0.4 -versiossa on käytettävä ylläolevaa merkintää, mutta PHP 5.1:ssä muoto on PDO::FETCH_ASSOC.

rowCount() ei toimi

Tämä on jokin yhteensopivuusongelma vanhan PHP 5.0x ja SQLite3:n kanssa. PHP5.1:ssä $sql->rowCount(); toimii.

lastInsertId() ei palauta viiteavainta

Tietokanta palauttaa tiedon viimeksi lisätyn tietueen avaimesta $dbh->lastInsertId()-metodilla. Homma toimii OK, jos kentän tyyppi on INTEGER. Muutoin SQLite palauttaa perusavaimen sijaan sisäisen rivinumeron, jota vastaava varsinainen avain pitää hakea erikseen SELECT-lauseella:

$sql = $dbh->prepare("SELECT avain FROM taulu WHERE ROWID = :last_insert_rowid");
$last_insert_rowid = $dbh->lastInsertId();
$sql->bindParam(":last_insert_id", $last_insert_rowid);
$sql->execute();
$resultset = $sql->fetchAll();
$perusavain = $resultset[0]["avain"];

Viite-eheyttä ei tueta

Uusin versio SQlitestä osaa jo viite-eheydet mutta ne täytyy erikseen aktivoida jokaisessa tietokantayhteydessä komennolla:

 PRAGMA foreign_keys = ON;

Does SQLite support foreign keys?

SQLite ymmärtää viite-eheysmääritykset CREATE TABLE -lauseissa, mutta ei toteuta niitä. Haluttaessa viite-eheys toimintaan täytyy käyttää triggereitä seuraavaan tapaan:

CREATE TABLE Ruokalaji (
Nimi VARCHAR(100) NOT NULL,
Kuvaus VARCHAR(250) DEFAULT '-',
RuokalajiID INTEGER PRIMARY KEY AUTOINCREMENT,
CONSTRAINT Ruokalaji_Nimi_UQ   
        UNIQUE (Nimi)
);

CREATE TABLE Resepti (
Nimi VARCHAR(100) NOT NULL,
Kuvaus VARCHAR(250) DEFAULT '-',
Henkilomaara INTEGER DEFAULT 2,
ReseptiID INTEGER PRIMARY KEY AUTOINCREMENT,
RuokalajiID INTEGER NOT NULL,
CONSTRAINT Resepti_RuokalajiID 
	FOREIGN KEY (RuokalajiID)
	REFERENCES Ruokalaji (RuokalajiID)
);

-- Koska SQLite ei toteuta viite-eheyksiä toteutetaan ne itse
-- triggereiden avulla
CREATE TRIGGER fki_resepti_ruokalaji_id
BEFORE INSERT ON resepti
FOR EACH ROW BEGIN 
  SELECT CASE
     WHEN ((SELECT ruokalajiid FROM ruokalaji WHERE ruokalajiid = NEW.ruokalajiId) IS NULL)
     THEN RAISE(ABORT, 'insert on table "resepti" violates foreign key constraint "fk_resepti_ruokalaji_id"')
  END;
END;

CREATE TRIGGER fku_resepti_ruokalaji_id
BEFORE UPDATE ON resepti
FOR EACH ROW BEGIN 
   SELECT CASE
     WHEN ((SELECT ruokalajiid FROM ruokalaji WHERE ruokalajiid = NEW.ruokalajiId) IS NULL)
     THEN RAISE(ABORT, 'update on table "resepti" violates foreign key constraint "fk_resepti_ruokalaji_id"')
  END;
END;

-- estetään poistot jos viite-eheys rikkoontuu
CREATE TRIGGER fkd_resepti_ruokalaji_id
BEFORE DELETE ON ruokalaji
FOR EACH ROW BEGIN 
  SELECT CASE
     WHEN ((SELECT ruokalajiid FROM resepti WHERE ruokalajiid = OLD.ruokalajiId) IS NOT NULL)
     THEN RAISE(ABORT, 'delete on table "ruokalaji" violates foreign key constraint "fk_resepti_ruokalaji_id"')
  END;
END;

-- toinen vaihtoehto deletelle. vyörytetään poistot
CREATE TRIGGER fkd_resepti_ruokalaji_id
BEFORE DELETE ON ruokalaji
FOR EACH ROW BEGIN 
  DELETE from resepti WHERE ruokalajiID = OLD.ruokalajiid;
  END;
END;

Lisätietoja artikkelista: Enforce Foreign Key Integrity in SQLite with Triggers

Ulkoliitokset

SQLite tukee LEFT OUTER JOIN, mutta ei RIGHT tai FULL OUTER JOIN.

Autonumber/Autoincrement/serial

Automaattisesti numeroituja kenttiä voi luoda perusavaimen luonnin yhteydessä, mutta ei silloin, jos perusavain määritellään CONSTRAINT-lauseessa.

CREATE TABLE foo (
ID INTEGER PRIMARY KEY AUTOINCREMENT
);

Perusavaimet

Käytettäessä PHP:n PDO-rajapintaa voidaan tietokannalta kysyä viimeksi lisätyn tietueen perusavainta. Käytettäessä perusavaimena automaattisesti numeroitua kenttää tai tavallista INTEGER-tyyppistä kenttää palauttaa SQLite lisäysten yhteydessä aivan oikean perusavaimen arvon. Jos perusavain on jotain muuta tyyppiä, niin SQLite palauttaa oman sisäisen ROWID-kentän arvon (myös OID tai _ROWID_). Tässä tilanteessa voi joutua erikseen SELECT-lauseella etsimään ROWID-arvoa vastaavan perusavaimen arvon.

SQlite sallii merkkijonoja numeerisissa kentissä

SQLite sallii aivan vapaasti lisätä merkkijonoja numeerisiin kenttiin. SQLite yrittää kyllä muuttaa lisättävän tiedon numeroksi, mutta jos se ei onnistu, niin lisää kuitenkin.

Käyttäjien kommentit

Kommentoi Lisää kommentti
Kurssimateriaalien käyttäminen kaupallisiin tarkoituksiin tai opetusmateriaalina ilman lupaa on ehdottomasti kielletty!
http://appro.mit.jyu.fi/doc/sqlite/
© Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <http://hazor.iki.fi/>
2014-04-03 15:32:16
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta