From 871f1d1d6410403599c774ad9aa20e55c77162fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Mon, 15 Sep 2014 13:23:55 +0200 Subject: [PATCH] Update PicoDb and improve schema migration error handling --- common.php | 18 ++++++- vendor/PicoDb/Database.php | 12 ++++- vendor/PicoDb/Drivers/Mysql.php | 76 ++++++++++++++++++++++++++++++ vendor/PicoDb/Drivers/Postgres.php | 73 ++++++++++++++++++++++++++++ vendor/PicoDb/Drivers/Sqlite.php | 17 +++++-- vendor/PicoDb/Schema.php | 8 +--- vendor/PicoDb/Table.php | 32 ++++++++----- 7 files changed, 210 insertions(+), 26 deletions(-) create mode 100644 vendor/PicoDb/Drivers/Mysql.php create mode 100644 vendor/PicoDb/Drivers/Postgres.php diff --git a/common.php b/common.php index c0ce756..9ee3f11 100644 --- a/common.php +++ b/common.php @@ -67,6 +67,22 @@ PicoDb\Database::bootstrap('db', function() { return $db; } else { - die('Unable to migrate database schema.'); + $errors = $db->getLogMessages(); + + $pdo = new \PDO('sqlite::memory:'); + $result = $pdo->query('select sqlite_version()', PDO::FETCH_COLUMN, 0); + $sqlite_version = $result ? $result->fetch() : '?'; + + $html = 'Unable to migrate the database schema, please copy and paste this message and create a bug report:
'; + $html .= '
';
+        $html .= (isset($errors[0]) ? $errors[0] : 'Unknown SQL error').PHP_EOL.PHP_EOL;
+        $html .= '- PHP version: '.phpversion().PHP_EOL;
+        $html .= '- SAPI: '.php_sapi_name().PHP_EOL;
+        $html .= '- PDO Sqlite version: '.phpversion('pdo_sqlite').PHP_EOL;
+        $html .= '- Sqlite version: '.$sqlite_version.PHP_EOL;
+        $html .= '- OS: '.php_uname();
+        $html .= '
'; + + die($html); } }); diff --git a/vendor/PicoDb/Database.php b/vendor/PicoDb/Database.php index 86f5fe3..4d7b703 100644 --- a/vendor/PicoDb/Database.php +++ b/vendor/PicoDb/Database.php @@ -19,7 +19,17 @@ class Database case 'sqlite': require_once __DIR__.'/Drivers/Sqlite.php'; - $this->pdo = new Sqlite($settings['filename']); + $this->pdo = new Sqlite($settings); + break; + + case 'mysql': + require_once __DIR__.'/Drivers/Mysql.php'; + $this->pdo = new Mysql($settings); + break; + + case 'postgres': + require_once __DIR__.'/Drivers/Postgres.php'; + $this->pdo = new Postgres($settings); break; default: diff --git a/vendor/PicoDb/Drivers/Mysql.php b/vendor/PicoDb/Drivers/Mysql.php new file mode 100644 index 0000000..22277a0 --- /dev/null +++ b/vendor/PicoDb/Drivers/Mysql.php @@ -0,0 +1,76 @@ + 'SET NAMES '.$settings['charset'] + ); + + parent::__construct($dsn, $settings['username'], $settings['password'], $options); + + if (isset($settings['schema_table'])) { + $this->schema_table = $settings['schema_table']; + } + } + + + public function getSchemaVersion() + { + $this->exec("CREATE TABLE IF NOT EXISTS `".$this->schema_table."` (`version` INT DEFAULT '0')"); + + $rq = $this->prepare('SELECT `version` FROM `'.$this->schema_table.'`'); + $rq->execute(); + $result = $rq->fetch(\PDO::FETCH_ASSOC); + + if (isset($result['version'])) { + return (int) $result['version']; + } + else { + $this->exec('INSERT INTO `'.$this->schema_table.'` VALUES(0)'); + } + + return 0; + } + + + public function setSchemaVersion($version) + { + $rq = $this->prepare('UPDATE `'.$this->schema_table.'` SET `version`=?'); + $rq->execute(array($version)); + } + + + public function getLastId() + { + return $this->lastInsertId(); + } + + + public function escapeIdentifier($value) + { + if (strpos($value, '.') !== false) return $value; + return '`'.$value.'`'; + } +} \ No newline at end of file diff --git a/vendor/PicoDb/Drivers/Postgres.php b/vendor/PicoDb/Drivers/Postgres.php new file mode 100644 index 0000000..641727f --- /dev/null +++ b/vendor/PicoDb/Drivers/Postgres.php @@ -0,0 +1,73 @@ +schema_table = $settings['schema_table']; + } + } + + + public function getSchemaVersion() + { + $this->exec("CREATE TABLE IF NOT EXISTS ".$this->schema_table." (version SMALLINT DEFAULT 0)"); + + $rq = $this->prepare('SELECT version FROM '.$this->schema_table.''); + $rq->execute(); + $result = $rq->fetch(\PDO::FETCH_ASSOC); + + if (isset($result['version'])) { + return (int) $result['version']; + } + else { + $this->exec('INSERT INTO '.$this->schema_table.' VALUES(0)'); + } + + return 0; + } + + + public function setSchemaVersion($version) + { + $rq = $this->prepare('UPDATE '.$this->schema_table.' SET version=?'); + $rq->execute(array($version)); + } + + + public function getLastId() + { + $rq = $this->prepare('SELECT LASTVAL()'); + $rq->execute(); + return $rq->fetchColumn(); + } + + + public function escapeIdentifier($value) + { + return $value; + } +} \ No newline at end of file diff --git a/vendor/PicoDb/Drivers/Sqlite.php b/vendor/PicoDb/Drivers/Sqlite.php index 6555e73..83b61c4 100644 --- a/vendor/PicoDb/Drivers/Sqlite.php +++ b/vendor/PicoDb/Drivers/Sqlite.php @@ -5,9 +5,19 @@ namespace PicoDb; class Sqlite extends \PDO { - public function __construct($filename) + public function __construct(array $settings) { - parent::__construct('sqlite:'.$filename); + $required_atttributes = array( + 'filename', + ); + + foreach ($required_atttributes as $attribute) { + if (! isset($settings[$attribute])) { + throw new \LogicException('This configuration parameter is missing: "'.$attribute.'"'); + } + } + + parent::__construct('sqlite:'.$settings['filename']); $this->exec('PRAGMA foreign_keys = ON'); } @@ -20,8 +30,7 @@ class Sqlite extends \PDO { $result = $rq->fetch(\PDO::FETCH_ASSOC); if (isset($result['user_version'])) { - - return $result['user_version']; + return (int) $result['user_version']; } return 0; diff --git a/vendor/PicoDb/Schema.php b/vendor/PicoDb/Schema.php index 2f52b84..a054ac0 100644 --- a/vendor/PicoDb/Schema.php +++ b/vendor/PicoDb/Schema.php @@ -18,7 +18,6 @@ class Schema $current_version = $this->db->getConnection()->getSchemaVersion(); if ($current_version < $last_version) { - return $this->migrateTo($current_version, $last_version); } @@ -37,20 +36,15 @@ class Schema $function_name = '\Schema\version_'.$i; if (function_exists($function_name)) { - call_user_func($function_name, $this->db->getConnection()); $this->db->getConnection()->setSchemaVersion($i); } - else { - - throw new \LogicException('To execute a database migration, you need to create this function: "'.$function_name.'".'); - } } $this->db->closeTransaction(); } catch (\PDOException $e) { - + $this->db->setLogMessage($function_name.' => '.$e->getMessage()); $this->db->cancelTransaction(); return false; } diff --git a/vendor/PicoDb/Table.php b/vendor/PicoDb/Table.php index 4cdf35f..b333feb 100644 --- a/vendor/PicoDb/Table.php +++ b/vendor/PicoDb/Table.php @@ -4,6 +4,9 @@ namespace PicoDb; class Table { + const SORT_ASC = 'ASC'; + const SORT_DESC = 'DESC'; + private $table_name = ''; private $sql_limit = ''; private $sql_offset = ''; @@ -67,12 +70,7 @@ class Table $result = $this->db->execute($sql, $values); - if ($result !== false && $result->rowCount() > 0) { - - return true; - } - - return false; + return $result !== false && $result->rowCount() > 0; } @@ -104,7 +102,9 @@ class Table $this->conditions() ); - return false !== $this->db->execute($sql, $this->values); + $result = $this->db->execute($sql, $this->values); + + return $result !== false && $result->rowCount() > 0; } @@ -260,10 +260,10 @@ class Table } - public function orderBy($column, $order = 'ASC') + public function orderBy($column, $order = self::SORT_ASC) { $order = strtoupper($order); - $order = $order === 'ASC' || $order === 'DESC' ? $order : 'ASC'; + $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC; if ($this->sql_order === '') { $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; @@ -279,10 +279,10 @@ class Table public function asc($column) { if ($this->sql_order === '') { - $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' ASC'; + $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC; } else { - $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' ASC'; + $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC; } return $this; @@ -292,10 +292,10 @@ class Table public function desc($column) { if ($this->sql_order === '') { - $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' DESC'; + $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC; } else { - $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' DESC'; + $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC; } return $this; @@ -377,6 +377,12 @@ class Table $sql = sprintf('%s = ?', $this->db->escapeIdentifier($column)); break; + case 'neq': + case 'notequal': + case 'notequals': + $sql = sprintf('%s != ?', $this->db->escapeIdentifier($column)); + break; + case 'gt': case 'greaterthan': $sql = sprintf('%s > ?', $this->db->escapeIdentifier($column));