summaryrefslogtreecommitdiff
path: root/libs/picodb/lib/PicoDb/Driver
diff options
context:
space:
mode:
Diffstat (limited to 'libs/picodb/lib/PicoDb/Driver')
-rw-r--r--libs/picodb/lib/PicoDb/Driver/Base.php234
-rw-r--r--libs/picodb/lib/PicoDb/Driver/Mssql.php178
-rw-r--r--libs/picodb/lib/PicoDb/Driver/Mysql.php268
-rw-r--r--libs/picodb/lib/PicoDb/Driver/Postgres.php212
-rw-r--r--libs/picodb/lib/PicoDb/Driver/Sqlite.php199
5 files changed, 1091 insertions, 0 deletions
diff --git a/libs/picodb/lib/PicoDb/Driver/Base.php b/libs/picodb/lib/PicoDb/Driver/Base.php
new file mode 100644
index 00000000..790cd623
--- /dev/null
+++ b/libs/picodb/lib/PicoDb/Driver/Base.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use LogicException;
+use PDOException;
+
+/**
+ * Base Driver class
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+abstract class Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array();
+
+ /**
+ * PDO connection
+ *
+ * @access protected
+ * @var PDO
+ */
+ protected $pdo = null;
+
+ /**
+ * Create a new PDO connection
+ *
+ * @abstract
+ * @access public
+ * @param array $settings
+ */
+ abstract public function createConnection(array $settings);
+
+ /**
+ * Enable foreign keys
+ *
+ * @abstract
+ * @access public
+ */
+ abstract public function enableForeignKeys();
+
+ /**
+ * Disable foreign keys
+ *
+ * @abstract
+ * @access public
+ */
+ abstract public function disableForeignKeys();
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @abstract
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ abstract public function isDuplicateKeyError($code);
+
+ /**
+ * Escape identifier
+ *
+ * @abstract
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ abstract public function escape($identifier);
+
+ /**
+ * Get non standard operator
+ *
+ * @abstract
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ abstract public function getOperator($operator);
+
+ /**
+ * Get last inserted id
+ *
+ * @abstract
+ * @access public
+ * @return integer
+ */
+ abstract public function getLastId();
+
+ /**
+ * Get current schema version
+ *
+ * @abstract
+ * @access public
+ * @return integer
+ */
+ abstract public function getSchemaVersion();
+
+ /**
+ * Set current schema version
+ *
+ * @abstract
+ * @access public
+ * @param integer $version
+ */
+ abstract public function setSchemaVersion($version);
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function __construct(array $settings)
+ {
+ foreach ($this->requiredAttributes as $attribute) {
+ if (! isset($settings[$attribute])) {
+ throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
+ }
+ }
+
+ $this->createConnection($settings);
+ $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ }
+
+ /**
+ * Get the PDO connection
+ *
+ * @access public
+ * @return PDO
+ */
+ public function getConnection()
+ {
+ return $this->pdo;
+ }
+
+ /**
+ * Release the PDO connection
+ *
+ * @access public
+ */
+ public function closeConnection()
+ {
+ $this->pdo = null;
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+ $this->pdo->beginTransaction();
+
+ foreach ($dictionary as $key => $value) {
+
+ $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?');
+ $rq->execute(array($key));
+
+ if ($rq->fetchColumn()) {
+ $rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?');
+ $rq->execute(array($value, $key));
+ }
+ else {
+ $rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)');
+ $rq->execute(array($key, $value));
+ }
+ }
+
+ $this->pdo->commit();
+
+ return true;
+ }
+ catch (PDOException $e) {
+ $this->pdo->rollBack();
+ return false;
+ }
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @access public
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Replace placeholder with values in prepared statement
+ *
+ * @access protected
+ * @param string $sql
+ * @param array $values
+ * @return string
+ */
+ protected function getSqlFromPreparedStatement($sql, array $values)
+ {
+ foreach ($values as $value) {
+ $sql = substr_replace($sql, "'$value'", strpos($sql, '?'), 1);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SELECT VERSION()')->fetchColumn();
+ }
+}
diff --git a/libs/picodb/lib/PicoDb/Driver/Mssql.php b/libs/picodb/lib/PicoDb/Driver/Mssql.php
new file mode 100644
index 00000000..83e75af2
--- /dev/null
+++ b/libs/picodb/lib/PicoDb/Driver/Mssql.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+
+/**
+ * Microsoft SQL Server Driver
+ *
+ * @package PicoDb\Driver
+ * @author Algy Taylor <thomas.taylor@cmft.nhs.uk>
+ */
+class Mssql extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $dsn = 'sqlsrv:Server=' . $settings['hostname'] . ';Database=' . $settings['database'];
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port=' . $settings['port'];
+ }
+
+ $this->pdo = new PDO($dsn, $settings['username'], $settings['password']);
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"; GO;');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"; GO;');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 2601;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * https://msdn.microsoft.com/en-us/library/ms175874.aspx
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '['.$identifier.']';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE' || $operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS [".$this->schemaTable."] ([version] INT DEFAULT '0')");
+
+ $rq = $this->pdo->prepare('SELECT [version] FROM ['.$this->schemaTable.']');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO ['.$this->schemaTable.'] VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE ['.$this->schemaTable.'] SET [version]=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ $this->getConnection()->exec('SET SHOWPLAN_ALL ON');
+ return $this->getConnection()->query($this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+}
diff --git a/libs/picodb/lib/PicoDb/Driver/Mysql.php b/libs/picodb/lib/PicoDb/Driver/Mysql.php
new file mode 100644
index 00000000..7e5cce0a
--- /dev/null
+++ b/libs/picodb/lib/PicoDb/Driver/Mysql.php
@@ -0,0 +1,268 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Mysql Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Mysql extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $this->pdo = new PDO(
+ $this->buildDsn($settings),
+ $settings['username'],
+ $settings['password'],
+ $this->buildOptions($settings)
+ );
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Build connection DSN
+ *
+ * @access protected
+ * @param array $settings
+ * @return string
+ */
+ protected function buildDsn(array $settings)
+ {
+ $charset = empty($settings['charset']) ? 'utf8' : $settings['charset'];
+ $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset;
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port='.$settings['port'];
+ }
+
+ return $dsn;
+ }
+
+ /**
+ * Build connection options
+ *
+ * @access protected
+ * @param array $settings
+ * @return array
+ */
+ protected function buildOptions(array $settings)
+ {
+ $options = array(
+ PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
+ );
+
+ if (! empty($settings['ssl_key'])) {
+ $options[PDO::MYSQL_ATTR_SSL_KEY] = $settings['ssl_key'];
+ }
+
+ if (! empty($settings['ssl_cert'])) {
+ $options[PDO::MYSQL_ATTR_SSL_CERT] = $settings['ssl_cert'];
+ }
+
+ if (! empty($settings['ssl_ca'])) {
+ $options[PDO::MYSQL_ATTR_SSL_CA] = $settings['ssl_ca'];
+ }
+
+ if (! empty($settings['persistent'])) {
+ $options[PDO::ATTR_PERSISTENT] = $settings['persistent'];
+ }
+
+ if (! empty($settings['timeout'])) {
+ $options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
+ }
+
+ if (isset($settings['verify_server_cert'])) {
+ $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $settings['verify_server_cert'];
+ }
+
+ if (! empty($settings['case'])) {
+ $options[PDO::ATTR_CASE] = $settings['case'];
+ }
+
+ return $options;
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23000;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '`'.$identifier.'`';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE') {
+ return 'LIKE BINARY';
+ }
+ else if ($operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0') ENGINE=InnoDB CHARSET=utf8");
+
+ $rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+
+ $sql = sprintf(
+ 'REPLACE INTO %s (%s, %s) VALUES %s',
+ $this->escape($table),
+ $this->escape($keyColumn),
+ $this->escape($valueColumn),
+ implode(', ', array_fill(0, count($dictionary), '(?, ?)'))
+ );
+
+ $values = array();
+
+ foreach ($dictionary as $key => $value) {
+ $values[] = $key;
+ $values[] = $value;
+ }
+
+ $rq = $this->pdo->prepare($sql);
+ $rq->execute($values);
+
+ return true;
+ }
+ catch (PDOException $e) {
+ return false;
+ }
+ }
+}
diff --git a/libs/picodb/lib/PicoDb/Driver/Postgres.php b/libs/picodb/lib/PicoDb/Driver/Postgres.php
new file mode 100644
index 00000000..86036839
--- /dev/null
+++ b/libs/picodb/lib/PicoDb/Driver/Postgres.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Postgres Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Postgres extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $dsn = 'pgsql:dbname='.$settings['database'];
+ $username = null;
+ $password = null;
+ $options = array();
+
+ if (! empty($settings['username'])) {
+ $username = $settings['username'];
+ }
+
+ if (! empty($settings['password'])) {
+ $password = $settings['password'];
+ }
+
+ if (! empty($settings['hostname'])) {
+ $dsn .= ';host='.$settings['hostname'];
+ }
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port='.$settings['port'];
+ }
+
+ if (! empty($settings['timeout'])) {
+ $options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
+ }
+
+ $this->pdo = new PDO($dsn, $username, $password, $options);
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23505 || $code == 23503;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '"'.$identifier.'"';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE') {
+ return 'LIKE';
+ }
+ else if ($operator === 'ILIKE') {
+ return 'ILIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ try {
+ $rq = $this->pdo->prepare('SELECT LASTVAL()');
+ $rq->execute();
+
+ return $rq->fetchColumn();
+ }
+ catch (PDOException $e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)");
+
+ $rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN (FORMAT YAML) '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SHOW server_version')->fetchColumn();
+ }
+}
diff --git a/libs/picodb/lib/PicoDb/Driver/Sqlite.php b/libs/picodb/lib/PicoDb/Driver/Sqlite.php
new file mode 100644
index 00000000..0503d336
--- /dev/null
+++ b/libs/picodb/lib/PicoDb/Driver/Sqlite.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Sqlite Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Sqlite extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array('filename');
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $options = array();
+
+ if (! empty($settings['timeout'])) {
+ $options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
+ }
+
+ $this->pdo = new PDO('sqlite:'.$settings['filename'], null, null, $options);
+ $this->enableForeignKeys();
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('PRAGMA foreign_keys = ON');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('PRAGMA foreign_keys = OFF');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23000;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '"'.$identifier.'"';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE' || $operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $rq = $this->pdo->prepare('PRAGMA user_version');
+ $rq->execute();
+
+ return (int) $rq->fetchColumn();
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $this->pdo->exec('PRAGMA user_version='.$version);
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+ $this->pdo->beginTransaction();
+
+ foreach ($dictionary as $key => $value) {
+
+ $sql = sprintf(
+ 'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)',
+ $this->escape($table),
+ $this->escape($keyColumn),
+ $this->escape($valueColumn)
+ );
+
+ $rq = $this->pdo->prepare($sql);
+ $rq->execute(array($key, $value));
+ }
+
+ $this->pdo->commit();
+
+ return true;
+ }
+ catch (PDOException $e) {
+ $this->pdo->rollBack();
+ return false;
+ }
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @access public
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN QUERY PLAN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SELECT sqlite_version()')->fetchColumn();
+ }
+}