<?php
/**
* @file DatajarEngine.php
*
* @brief Implements a datajar driver for sqlite.
*
* Copyright © 2012 Guillaume Pasquet
*
* This file is part of Datajar.
*
* Datajar is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Datajar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Datajar. If not, see <http://www.gnu.org/licenses/>.
*/
class DatajarEngineSqlite extends DatajarEngineBase
{
protected $db;
// Loading config and attempting to connect.
public function __construct($conn = "")
{
$args = func_get_args();
if($conn != "") {
$this->init($conn);
}
}
public function init($conn_string)
{
$conn_details = $this->parse_conn_string($conn_string);
$db_file = $conn_details['database'];
// Checking the file can be accessed.
if($db_file == "") {
throw new DatajarException(t("The database file must be specified."));
}
// OK, trying to open the file.
$this->db = new SQLite3($db_file);
if(!$this->db) {
throw new DatajarException(t("The database %s couldn't be opened.", $db_file));
}
$this->db->busyTimeout(30000); // 30s lock timeout.
}
public static function escape($data) {
return SQLite3::escapeString($data);
}
public function __destruct()
{
//$this->close();
}
public function close()
{
if($this->db) {
$this->db->close();
$this->db = NULL;
}
}
/**
* Checks SQLite errors. Throws a DatajarException if there was an error
* during the last query.
*/
protected function errors()
{
$error = $this->db->lastErrorCode();
if($error != 0) {
throw new DatajarException(
t(" `%s'", $db_file),
$this->db->lastErrorMsg(),
$error);
}
}
/**
* SQLite-specific routine with error check included.
*/
protected function query($statement)
{
$ret = null;
$this->log($statement);
if(strtoupper(substr(trim($statement), 0, 6)) == "SELECT") {
$res = $this->db->query($statement);
$this->errors();
$table = array();
while($row = $res->fetchArray(SQLITE3_ASSOC))
{
$table[] = $row;
}
return $table;
} else {
$answer = $this->db->exec($statement);
$this->errors();
return $answer;
}
}
protected function lastId()
{
return $this->db->lastInsertRowId();
}
/**
* Generates the creation statement.
*/
protected function create_stmt(&$type)
{
$typename = $type['val']->gettype();
$def = $type['name'] . ' ';
switch($typename) {
case 'DatajarTypeBigInt':
$def.= 'BIGINT';
break;
case 'DatajarTypeBool':
$def.= 'BOOLEAN';
break;
case 'DatajarTypeVarChar':
$def.= 'VARCHAR('.$type['val']->length.')';
break;
case 'DatajarTypeDate':
$def.= 'DATE';
break;
case 'DatajarTypeDateTime':
$def.= 'DATETIME';
break;
case 'DatajarTypeDecimal':
$def.= 'DECIMAL('.$type['val']->length.', '.$type['val']->decimal_places.')';
break;
case 'DatajarTypeFloat':
$def.= 'FLOAT';
break;
case 'DatajarTypeInt':
$def.= 'INTEGER';
break;
case 'DatajarTypeText':
$def.= 'TEXT';
break;
case 'DatajarTypeBlob':
$def.= 'BLOB';
break;
case 'DatajarTypeForeignKey':
$def.= 'INTEGER NOT NULL REFERENCES '.$type['val']->model.'('.$type['val']->field.')';
break;
}
return $def;
}
public function create($object)
{
$this->require_datajar($object);
$proto = $object->prototype();
$stmt = 'CREATE TABLE IF NOT EXISTS "'.$this->obj_name($object).'" ('.
'"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ';
foreach($proto as $prop) {
$stmt .= $this->create_stmt($prop) . ', ';
}
// Stripping the extra ', ' and closing the statement.
$stmt = substr($stmt, 0, -2) . ');';
return $this->query($stmt);
}
public function save($object)
{
$this->require_datajar($object);
$stmt = "";
$props = $object->prototype();
if(!$object->id) {
$stmt = "INSERT INTO " . $this->obj_name($object);
$cols = "";
$vals = "";
foreach($props as $prop) {
$cols.= $prop['name'] . ', ';
if(DatajarEngineBase::does_extend($prop['val'], "DatajarBase")) {
$vals.= '"' . $prop['val']->id . '", ';
} else {
$vals.= '"' . $prop['val']->getval() . '", ';
}
}
$stmt.= '(' . substr($cols, 0, -2) . ')';
$stmt.= ' VALUES(' . substr($vals, 0, -2) . ');';
$this->query($stmt);
$object->setid($this->lastId());
} else {
$stmt = "UPDATE " . $this->obj_name($object) . " SET ";
$cols = "";
$vals = "";
foreach($props as $prop) {
$stmt.= $prop['name'] . '=';
if(DatajarEngineBase::does_extend($prop['val'], "DatajarBase")) {
$stmt.= '"' . $prop['val']->id . '", ';
} else {
$stmt.= '"' . $prop['val']->getval() . '", ';
}
}
$stmt = substr($stmt, 0, -2) . ' WHERE id="' . $object->id . '"';
return $this->query($stmt);
}
}
public function delete($object)
{
$this->require_datajar($object);
// Does the object exist in the datajar?
if($object->id) {
$stmt = "DELETE FROM " . $this->obj_name($object) . " WHERE id=\"" . $object->id . "\";";
$result = $this->query($stmt);
if($result) {
$object->clearid();
}
return $result;
}
}
public function drop($object)
{
$this->require_datajar($object);
$stmt = 'DROP TABLE IF EXISTS '.$this->obj_name($object).';';
$result = $this->query($stmt);
if($result) {
$object->clearid();
}
return $result;
}
/**
* Returns data relative to an object as an array.
*/
public function load($object, array $cond)
{
$stmt = "SELECT * FROM " . $this->obj_name($object);
if(count($cond) > 0) {
$stmt.= " WHERE ";
foreach($cond as $col => $val) {
$stmt.= "$col=\"$val\" AND ";
}
// Stripping the extra " AND "
$stmt = substr($stmt, 0, -5) . ';';
}
$this->log($stmt);
$data = $this->query($stmt);
if(count($data) < 1) {
return false;
}
$data = $data[0];
// Populating the object.
$props = $object->prototype();
// Setting the id first.
$object->setid($data['id']);
foreach($props as $prop) {
if(isset($data[$prop['name']])) {
$object->__set($prop['name'], $data[$prop['name']]);
}
}
return true;
}
public function run($query)
{
return $this->select($query->get_object(),
$query->get_cond(),
$query->get_orderby(),
$query->get_desc(),
$query->get_limit());
}
/**
* Loads a bunch of objects of a given type.
*/
public function select($objecttype, array $cond, $order = false,
$desc = false, array $limit = null)
{
$stmt = "SELECT * FROM " . $objecttype;
if(is_array($where) && count($where) > 0) {
$stmt = ' WHERE ' . Datajar::gen_where($where, "'", "", array($this, 'escape'));
}
if($order) {
$stmt .= " ORDER BY " . $order;
if($desc) {
$stmt .= " DESC";
}
}
if(is_array($limit) && count($limit) == 2) {
$stmt .= " LIMIT " . $limit[0] . ", " . $limit[1];
}
$this->log($stmt);
$data = $this->query($stmt);
if(count($data) < 1) {
return false;
}
$objs = array();
foreach($data as $row) {
$object = new $objecttype();
// Populating the object.
$props = $object->prototype();
$object->setid($row['id']);
foreach($props as $prop) {
if(isset($row[$prop['name']])) {
$object->__set($prop['name'], $row[$prop['name']]);
}
}
$objs[] = $object;
}
return $objs;
}
function test_backend()
{
return class_exists('SQLite3');
}
}
?>