diff --git a/common.php b/common.php index 13f5d26..2422645 100644 --- a/common.php +++ b/common.php @@ -61,17 +61,13 @@ PicoDb\Database::setInstance('db', function() { else { $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 .= (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 .= '- Sqlite version: '.$db->getDriver()->getDatabaseVersion().PHP_EOL;
$html .= '- OS: '.php_uname();
$html .= '
';
diff --git a/composer.json b/composer.json
index a8e8c98..2749692 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,7 @@
"require": {
"fguillot/simple-validator": "v1.0.0",
"fguillot/json-rpc": "v1.1.0",
- "fguillot/picodb": "v1.0.2",
+ "fguillot/picodb": "v1.0.14 ",
"fguillot/picofeed": "v0.1.24",
"pda/pheanstalk": "v3.1.0"
},
diff --git a/models/item.php b/models/item.php
index d07ae8e..ece9d6a 100644
--- a/models/item.php
+++ b/models/item.php
@@ -546,7 +546,7 @@ function cleanup($feed_id, array $items_in_feed)
$removed_items = $db
->table('items')
->columns('id')
- ->notin('id', $items_in_feed)
+ ->notIn('id', $items_in_feed)
->eq('status', 'removed')
->eq('feed_id', $feed_id)
->desc('updated')
diff --git a/models/remember_me.php b/models/remember_me.php
index 717e400..42ded92 100644
--- a/models/remember_me.php
+++ b/models/remember_me.php
@@ -14,6 +14,8 @@ const EXPIRATION = 5184000;
* Get a remember me record
*
* @access public
+ * @param string $token
+ * @param string $sequence
* @return mixed
*/
function find($token, $sequence)
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 55ca5d8..98da40a 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -72,16 +72,25 @@ return array(
'Pheanstalk\\Socket\\StreamFunctions' => $vendorDir . '/pda/pheanstalk/src/Socket/StreamFunctions.php',
'Pheanstalk\\Socket\\WriteHistory' => $vendorDir . '/pda/pheanstalk/src/Socket/WriteHistory.php',
'Pheanstalk\\YamlResponseParser' => $vendorDir . '/pda/pheanstalk/src/YamlResponseParser.php',
- 'PicoDb\\Condition' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Condition.php',
+ 'PicoDb\\Builder\\BaseBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php',
+ 'PicoDb\\Builder\\ConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php',
+ 'PicoDb\\Builder\\InsertBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php',
+ 'PicoDb\\Builder\\OrConditionBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php',
+ 'PicoDb\\Builder\\UpdateBuilder' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php',
'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php',
+ 'PicoDb\\DriverFactory' => $vendorDir . '/fguillot/picodb/lib/PicoDb/DriverFactory.php',
'PicoDb\\Driver\\Base' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Base.php',
+ 'PicoDb\\Driver\\Mssql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php',
'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php',
'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php',
'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php',
'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.php',
+ 'PicoDb\\LargeObject' => $vendorDir . '/fguillot/picodb/lib/PicoDb/LargeObject.php',
'PicoDb\\SQLException' => $vendorDir . '/fguillot/picodb/lib/PicoDb/SQLException.php',
'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php',
+ 'PicoDb\\StatementHandler' => $vendorDir . '/fguillot/picodb/lib/PicoDb/StatementHandler.php',
'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php',
+ 'PicoDb\\UrlParser' => $vendorDir . '/fguillot/picodb/lib/PicoDb/UrlParser.php',
'PicoFeed\\Base' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Base.php',
'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php',
'PicoFeed\\Client\\ClientException' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/ClientException.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index e928ae2..055e64b 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -144,16 +144,25 @@ class ComposerStaticInitfd7e8d436e1dc450edc3153ac8bc31b4
'Pheanstalk\\Socket\\StreamFunctions' => __DIR__ . '/..' . '/pda/pheanstalk/src/Socket/StreamFunctions.php',
'Pheanstalk\\Socket\\WriteHistory' => __DIR__ . '/..' . '/pda/pheanstalk/src/Socket/WriteHistory.php',
'Pheanstalk\\YamlResponseParser' => __DIR__ . '/..' . '/pda/pheanstalk/src/YamlResponseParser.php',
- 'PicoDb\\Condition' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Condition.php',
+ 'PicoDb\\Builder\\BaseBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php',
+ 'PicoDb\\Builder\\ConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php',
+ 'PicoDb\\Builder\\InsertBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php',
+ 'PicoDb\\Builder\\OrConditionBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php',
+ 'PicoDb\\Builder\\UpdateBuilder' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php',
'PicoDb\\Database' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Database.php',
+ 'PicoDb\\DriverFactory' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/DriverFactory.php',
'PicoDb\\Driver\\Base' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Base.php',
+ 'PicoDb\\Driver\\Mssql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mssql.php',
'PicoDb\\Driver\\Mysql' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php',
'PicoDb\\Driver\\Postgres' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php',
'PicoDb\\Driver\\Sqlite' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php',
'PicoDb\\Hashtable' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Hashtable.php',
+ 'PicoDb\\LargeObject' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/LargeObject.php',
'PicoDb\\SQLException' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/SQLException.php',
'PicoDb\\Schema' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Schema.php',
+ 'PicoDb\\StatementHandler' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/StatementHandler.php',
'PicoDb\\Table' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/Table.php',
+ 'PicoDb\\UrlParser' => __DIR__ . '/..' . '/fguillot/picodb/lib/PicoDb/UrlParser.php',
'PicoFeed\\Base' => __DIR__ . '/..' . '/fguillot/picofeed/lib/PicoFeed/Base.php',
'PicoFeed\\Client\\Client' => __DIR__ . '/..' . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php',
'PicoFeed\\Client\\ClientException' => __DIR__ . '/..' . '/fguillot/picofeed/lib/PicoFeed/Client/ClientException.php',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index fb7b7c6..e4579fd 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -37,45 +37,6 @@
"description": "Simple validator library",
"homepage": "https://github.com/fguillot/simpleValidator"
},
- {
- "name": "fguillot/picodb",
- "version": "v1.0.2",
- "version_normalized": "1.0.2.0",
- "source": {
- "type": "git",
- "url": "https://github.com/fguillot/picoDb.git",
- "reference": "61f492c125d9195ce869447e2b2450adeb3b01d6"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/fguillot/picoDb/zipball/61f492c125d9195ce869447e2b2450adeb3b01d6",
- "reference": "61f492c125d9195ce869447e2b2450adeb3b01d6",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0"
- },
- "time": "2015-08-27 23:33:16",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "psr-0": {
- "PicoDb": "lib/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Frédéric Guillot",
- "homepage": "http://fredericguillot.com"
- }
- ],
- "description": "Minimalist database query builder",
- "homepage": "https://github.com/fguillot/picoDb"
- },
{
"name": "zendframework/zendxml",
"version": "1.0.2",
@@ -262,5 +223,47 @@
],
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed"
+ },
+ {
+ "name": "fguillot/picodb",
+ "version": "v1.0.14",
+ "version_normalized": "1.0.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/fguillot/picoDb.git",
+ "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/fguillot/picoDb/zipball/86a831302ab10af800c83dbe4b3b01c88d5433f1",
+ "reference": "86a831302ab10af800c83dbe4b3b01c88d5433f1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.8.*"
+ },
+ "time": "2016-07-16 22:59:59",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "PicoDb": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frédéric Guillot",
+ "homepage": "https://github.com/fguillot/"
+ }
+ ],
+ "description": "Minimalist database query builder",
+ "homepage": "https://github.com/fguillot/picoDb"
}
]
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php
new file mode 100644
index 0000000..e075ae3
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php
@@ -0,0 +1,86 @@
+db = $db;
+ $this->conditionBuilder = $condition;
+ }
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param Database $db
+ * @param ConditionBuilder $condition
+ * @return static
+ */
+ public static function getInstance(Database $db, ConditionBuilder $condition)
+ {
+ return new static($db, $condition);
+ }
+
+ /**
+ * Set table name
+ *
+ * @access public
+ * @param string $table
+ * @return $this
+ */
+ public function withTable($table)
+ {
+ $this->table = $table;
+ return $this;
+ }
+
+ /**
+ * Set columns name
+ *
+ * @access public
+ * @param string[] $columns
+ * @return $this
+ */
+ public function withColumns(array $columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Condition.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php
similarity index 60%
rename from vendor/fguillot/picodb/lib/PicoDb/Condition.php
rename to vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php
index b8c6de4..b0465b6 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Condition.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php
@@ -1,13 +1,17 @@
beginOr) {
- $this->or[] = $sql;
+ if ($this->orConditionOffset > 0) {
+ $this->orConditions[$this->orConditionOffset]->withCondition($sql);
}
else {
$this->conditions[] = $sql;
@@ -116,9 +120,10 @@ class Condition
*/
public function beginOr()
{
- $this->beginOr = true;
- $this->or = array();
+ $this->orConditionOffset++;
+ $this->orConditions[$this->orConditionOffset] = new OrConditionBuilder();
}
+
/**
* Close OR condition
*
@@ -126,10 +131,13 @@ class Condition
*/
public function closeOr()
{
- $this->beginOr = false;
+ $condition = $this->orConditions[$this->orConditionOffset]->build();
+ $this->orConditionOffset--;
- if (! empty($this->or)) {
- $this->conditions[] = '('.implode(' OR ', $this->or).')';
+ if ($this->orConditionOffset > 0) {
+ $this->orConditions[$this->orConditionOffset]->withCondition($condition);
+ } else {
+ $this->conditions[] = $condition;
}
}
@@ -174,6 +182,19 @@ class Condition
}
}
+ /**
+ * IN condition with a subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function inSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' IN ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* NOT IN condition
*
@@ -181,7 +202,7 @@ class Condition
* @param string $column
* @param array $values
*/
- public function notin($column, array $values)
+ public function notIn($column, array $values)
{
if (! empty($values)) {
$this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')');
@@ -189,6 +210,19 @@ class Condition
}
}
+ /**
+ * NOT IN condition with a subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function notInSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* LIKE condition
*
@@ -228,6 +262,19 @@ class Condition
$this->values[] = $value;
}
+ /**
+ * Greater than condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function gtSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' > ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* Lower than condition
*
@@ -241,6 +288,19 @@ class Condition
$this->values[] = $value;
}
+ /**
+ * Lower than condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function ltSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' < ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* Greater than or equals condition
*
@@ -254,6 +314,19 @@ class Condition
$this->values[] = $value;
}
+ /**
+ * Greater than or equal condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function gteSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' >= ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* Lower than or equals condition
*
@@ -267,6 +340,19 @@ class Condition
$this->values[] = $value;
}
+ /**
+ * Lower than or equal condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function lteSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' <= ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
/**
* IS NULL condition
*
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php
new file mode 100644
index 0000000..9d06c40
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php
@@ -0,0 +1,36 @@
+columns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column);
+ $placeholders[] = ':'.$column;
+ }
+
+ return sprintf(
+ 'INSERT INTO %s (%s) VALUES (%s)',
+ $this->db->escapeIdentifier($this->table),
+ implode(', ', $columns),
+ implode(', ', $placeholders)
+ );
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
new file mode 100644
index 0000000..0defeaf
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
@@ -0,0 +1,43 @@
+conditions[] = $condition;
+ return $this;
+ }
+
+ /**
+ * Build SQL
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ return '('.implode(' OR ', $this->conditions).')';
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php
new file mode 100644
index 0000000..300ea9b
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php
@@ -0,0 +1,56 @@
+sumColumns = $columns;
+ return $this;
+ }
+
+ /**
+ * Build SQL
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ $columns = array();
+
+ foreach ($this->columns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column).'=?';
+ }
+
+ foreach ($this->sumColumns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?';
+ }
+
+ return sprintf(
+ 'UPDATE %s SET %s %s',
+ $this->db->escapeIdentifier($this->table),
+ implode(', ', $columns),
+ $this->conditionBuilder->build()
+ );
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Database.php b/vendor/fguillot/picodb/lib/PicoDb/Database.php
index d3e4ab9..22c9d2f 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Database.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Database.php
@@ -5,6 +5,7 @@ namespace PicoDb;
use Closure;
use PDOException;
use LogicException;
+use PicoDb\Driver\Mssql;
use PicoDb\Driver\Sqlite;
use PicoDb\Driver\Mysql;
use PicoDb\Driver\Postgres;
@@ -12,7 +13,8 @@ use PicoDb\Driver\Postgres;
/**
* Database
*
- * @author Frederic Guillot
+ * @package PicoDb
+ * @author Frederic Guillot
*/
class Database
{
@@ -25,6 +27,14 @@ class Database
*/
private static $instances = array();
+ /**
+ * Statement object
+ *
+ * @access protected
+ * @var StatementHandler
+ */
+ protected $statementHandler;
+
/**
* Queries logs
*
@@ -40,55 +50,16 @@ class Database
*/
private $driver;
- /**
- * Flag to calculate query time
- *
- * @access public
- * @var boolean
- */
- public $stopwatch = false;
-
- /**
- * Flag to log generated SQL queries
- *
- * @access public
- * @var boolean
- */
- public $logQueries = false;
-
- /**
- * Number of SQL queries executed
- *
- * @access public
- * @var integer
- */
- public $nbQueries = 0;
-
/**
* Initialize the driver
*
* @access public
- * @param array $settings Connection settings
+ * @param array $settings
*/
- public function __construct(array $settings)
+ public function __construct(array $settings = array())
{
- if (! isset($settings['driver'])) {
- throw new LogicException('You must define a database driver');
- }
-
- switch ($settings['driver']) {
- case 'sqlite':
- $this->driver = new Sqlite($settings);
- break;
- case 'mysql':
- $this->driver = new Mysql($settings);
- break;
- case 'postgres':
- $this->driver = new Postgres($settings);
- break;
- default:
- throw new LogicException('This database driver is not supported');
- }
+ $this->driver = DriverFactory::getDriver($settings);
+ $this->statementHandler = new StatementHandler($this);
}
/**
@@ -139,11 +110,29 @@ class Database
* Add a log message
*
* @access public
- * @param string $message Message
+ * @param mixed $message
+ * @return Database
*/
public function setLogMessage($message)
{
- $this->logs[] = $message;
+ $this->logs[] = is_array($message) ? var_export($message, true) : $message;
+ return $this;
+ }
+
+ /**
+ * Add many log messages
+ *
+ * @access public
+ * @param array $messages
+ * @return Database
+ */
+ public function setLogMessages(array $messages)
+ {
+ foreach ($messages as $message) {
+ $this->setLogMessage($message);
+ }
+
+ return $this;
}
/**
@@ -172,7 +161,7 @@ class Database
* Get the Driver instance
*
* @access public
- * @return Sqlite|Postgres|Mysql
+ * @return Mssql|Sqlite|Postgres|Mysql
*/
public function getDriver()
{
@@ -187,7 +176,18 @@ class Database
*/
public function getLastId()
{
- return $this->driver->getLastId();
+ return (int) $this->driver->getLastId();
+ }
+
+ /**
+ * Get statement object
+ *
+ * @access public
+ * @return StatementHandler
+ */
+ public function getStatementHandler()
+ {
+ return $this->statementHandler;
}
/**
@@ -251,30 +251,10 @@ class Database
*/
public function execute($sql, array $values = array())
{
- try {
-
- if ($this->logQueries) {
- $this->setLogMessage($sql);
- }
-
- if ($this->stopwatch) {
- $start = microtime(true);
- }
-
- $rq = $this->getConnection()->prepare($sql);
- $rq->execute($values);
-
- if ($this->stopwatch) {
- $this->setLogMessage('DURATION='.(microtime(true) - $start));
- }
-
- $this->nbQueries++;
-
- return $rq;
- }
- catch (PDOException $e) {
- return $this->handleSqlError($e);
- }
+ return $this->statementHandler
+ ->withSql($sql)
+ ->withPositionalParams($values)
+ ->execute();
}
/**
@@ -293,30 +273,9 @@ class Database
$this->closeTransaction();
return $result === null ? true : $result;
+ } catch (PDOException $e) {
+ return $this->statementHandler->handleSqlError($e);
}
- catch (PDOException $e) {
- return $this->handleSqlError($e);
- }
- }
-
- /**
- * Handle PDOException
- *
- * @access private
- * @param PDOException $e
- * @return bool
- * @throws SQLException
- */
- private function handleSqlError(PDOException $e)
- {
- $this->cancelTransaction();
- $this->setLogMessage($e->getMessage());
-
- if ($this->driver->isDuplicateKeyError($e->getCode())) {
- return false;
- }
-
- throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : ''));
}
/**
@@ -351,42 +310,61 @@ class Database
public function cancelTransaction()
{
if ($this->getConnection()->inTransaction()) {
- $this->getConnection()->rollback();
+ $this->getConnection()->rollBack();
}
}
/**
- * Get a table instance
+ * Get a table object
*
* @access public
- * @param string $table_name
+ * @param string $table
* @return Table
*/
- public function table($table_name)
+ public function table($table)
{
- return new Table($this, $table_name);
+ return new Table($this, $table);
}
/**
- * Get a hashtable instance
+ * Get a hashtable object
*
* @access public
- * @param string $table_name
+ * @param string $table
* @return Hashtable
*/
- public function hashtable($table_name)
+ public function hashtable($table)
{
- return new Hashtable($this, $table_name);
+ return new Hashtable($this, $table);
}
/**
- * Get a schema instance
+ * Get a LOB object
*
* @access public
+ * @param string $table
+ * @return LargeObject
+ */
+ public function largeObject($table)
+ {
+ return new LargeObject($this, $table);
+ }
+
+ /**
+ * Get a schema object
+ *
+ * @access public
+ * @param string $namespace
* @return Schema
*/
- public function schema()
+ public function schema($namespace = null)
{
- return new Schema($this);
+ $schema = new Schema($this);
+
+ if ($namespace !== null) {
+ $schema->setNamespace($namespace);
+ }
+
+ return $schema;
}
-}
\ No newline at end of file
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php
index fd6aa40..790cd62 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php
@@ -9,7 +9,8 @@ use PDOException;
/**
* Base Driver class
*
- * @author Frederic Guillot
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
*/
abstract class Base
{
@@ -19,7 +20,7 @@ abstract class Base
* @access protected
* @var array
*/
- protected $requiredAtttributes = array();
+ protected $requiredAttributes = array();
/**
* PDO connection
@@ -119,7 +120,7 @@ abstract class Base
*/
public function __construct(array $settings)
{
- foreach ($this->requiredAtttributes as $attribute) {
+ foreach ($this->requiredAttributes as $attribute) {
if (! isset($settings[$attribute])) {
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
}
@@ -185,8 +186,49 @@ abstract class Base
return true;
}
catch (PDOException $e) {
- $this->pdo->rollback();
+ $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/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php
new file mode 100644
index 0000000..83e75af
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php
@@ -0,0 +1,178 @@
+
+ */
+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/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php
index e1ae73b..4f3ca64 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php
@@ -8,7 +8,8 @@ use PDOException;
/**
* Mysql Driver
*
- * @author Frederic Guillot
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
*/
class Mysql extends Base
{
@@ -18,7 +19,7 @@ class Mysql extends Base
* @access protected
* @var array
*/
- protected $requiredAtttributes = array(
+ protected $requiredAttributes = array(
'hostname',
'username',
'password',
@@ -40,6 +41,27 @@ class Mysql extends Base
* @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;
@@ -48,15 +70,35 @@ class Mysql extends Base
$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',
);
- $this->pdo = new PDO($dsn, $settings['username'], $settings['password'], $options);
-
- if (isset($settings['schema_table'])) {
- $this->schemaTable = $settings['schema_table'];
+ 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'];
+ }
+
+ return $options;
}
/**
@@ -141,7 +183,7 @@ class Mysql extends Base
*/
public function getSchemaVersion()
{
- $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0')");
+ $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();
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php
index 4259101..a494cdc 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php
@@ -8,7 +8,8 @@ use PDOException;
/**
* Postgres Driver
*
- * @author Frederic Guillot
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
*/
class Postgres extends Base
{
@@ -18,7 +19,7 @@ class Postgres extends Base
* @access protected
* @var array
*/
- protected $requiredAtttributes = array(
+ protected $requiredAttributes = array(
'hostname',
'username',
'password',
@@ -169,4 +170,27 @@ class Postgres extends Base
$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/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php
index 67881ee..ea39f00 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php
@@ -8,7 +8,8 @@ use PDOException;
/**
* Sqlite Driver
*
- * @author Frederic Guillot
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
*/
class Sqlite extends Base
{
@@ -18,7 +19,7 @@ class Sqlite extends Base
* @access protected
* @var array
*/
- protected $requiredAtttributes = array('filename');
+ protected $requiredAttributes = array('filename');
/**
* Create a new PDO connection
@@ -161,8 +162,32 @@ class Sqlite extends Base
return true;
}
catch (PDOException $e) {
- $this->pdo->rollback();
+ $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();
+ }
}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php
new file mode 100644
index 0000000..13151ba
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php
@@ -0,0 +1,45 @@
+columns($this->keyColumn, $this->valueColumn);
- $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
$rows = $rq->fetchAll(PDO::FETCH_NUM);
foreach ($rows as $row) {
diff --git a/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php
new file mode 100644
index 0000000..ba5e3b9
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php
@@ -0,0 +1,167 @@
+limit(1);
+ $this->columns($column);
+
+ $rq = $this->db->getStatementHandler()
+ ->withSql($this->buildSelectQuery())
+ ->withPositionalParams($this->conditionBuilder->getValues())
+ ->execute();
+
+ $rq->bindColumn($column, $fd, PDO::PARAM_LOB);
+ $rq->fetch(PDO::FETCH_BOUND);
+
+ return $fd;
+ }
+
+ /**
+ * Fetch large object as string
+ *
+ * @access public
+ * @param string $column
+ * @return string
+ */
+ public function findOneColumnAsString($column)
+ {
+ $fd = $this->findOneColumnAsStream($column);
+
+ if (is_string($fd)) {
+ return $fd;
+ }
+
+ return stream_get_contents($fd);
+ }
+
+ /**
+ * Insert large object from stream
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param resource|string $blobDescriptor
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromStream($blobColumn, &$blobDescriptor, array $data = array())
+ {
+ $columns = array_merge(array($blobColumn), array_keys($data));
+ $this->db->startTransaction();
+
+ $result = $this->db->getStatementHandler()
+ ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns($columns)
+ ->build()
+ )
+ ->withNamedParams($data)
+ ->withLobParam($blobColumn, $blobDescriptor)
+ ->execute();
+
+ $this->db->closeTransaction();
+
+ return $result !== false;
+ }
+
+ /**
+ * Insert large object from file
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $filename
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromFile($blobColumn, $filename, array $data = array())
+ {
+ $fp = fopen($filename, 'rb');
+ $result = $this->insertFromStream($blobColumn, $fp, $data);
+ fclose($fp);
+ return $result;
+ }
+
+ /**
+ * Insert large object from string
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $blobData
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromString($blobColumn, &$blobData, array $data = array())
+ {
+ return $this->insertFromStream($blobColumn, $blobData, $data);
+ }
+
+ /**
+ * Update large object from stream
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param resource $blobDescriptor
+ * @param array $data
+ * @return bool
+ */
+ public function updateFromStream($blobColumn, &$blobDescriptor, array $data = array())
+ {
+ $values = array_merge(array_values($data), $this->conditionBuilder->getValues());
+ $columns = array_merge(array($blobColumn), array_keys($data));
+
+ $this->db->startTransaction();
+
+ $result = $this->db->getStatementHandler()
+ ->withSql(UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns($columns)
+ ->build()
+ )
+ ->withPositionalParams($values)
+ ->withLobParam($blobColumn, $blobDescriptor)
+ ->execute();
+
+ $this->db->closeTransaction();
+
+ return $result !== false;
+ }
+
+ /**
+ * Update large object from file
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $filename
+ * @param array $data
+ * @return bool
+ */
+ public function updateFromFile($blobColumn, $filename, array $data = array())
+ {
+ $fp = fopen($filename, 'r');
+ $result = $this->updateFromStream($blobColumn, $fp, $data);
+ fclose($fp);
+ return $result;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php
index 4d5cd68..7e57083 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php
@@ -7,7 +7,8 @@ use Exception;
/**
* SQLException
*
- * @author Frederic Guillot
+ * @package PicoDb
+ * @author Frederic Guillot
*/
class SQLException extends Exception
{
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Schema.php b/vendor/fguillot/picodb/lib/PicoDb/Schema.php
index d0ba1c7..a028036 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Schema.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Schema.php
@@ -7,7 +7,8 @@ use PDOException;
/**
* Schema migration class
*
- * @author Frederic Guillot
+ * @package PicoDb
+ * @author Frederic Guillot
*/
class Schema
{
@@ -19,6 +20,14 @@ class Schema
*/
protected $db = null;
+ /**
+ * Schema namespace
+ *
+ * @access protected
+ * @var string
+ */
+ protected $namespace = '\Schema';
+
/**
* Constructor
*
@@ -30,6 +39,30 @@ class Schema
$this->db = $db;
}
+ /**
+ * Set another namespace
+ *
+ * @access public
+ * @param string $namespace
+ * @return Schema
+ */
+ public function setNamespace($namespace)
+ {
+ $this->namespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * Get schema namespace
+ *
+ * @access public
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->namespace;
+ }
+
/**
* Check the schema version and run the migrations
*
@@ -59,25 +92,23 @@ class Schema
public function migrateTo($current_version, $next_version)
{
try {
-
- $this->db->startTransaction();
- $this->db->getDriver()->disableForeignKeys();
-
for ($i = $current_version + 1; $i <= $next_version; $i++) {
+ $this->db->startTransaction();
+ $this->db->getDriver()->disableForeignKeys();
- $function_name = '\Schema\version_'.$i;
+ $function_name = $this->getNamespace().'\version_'.$i;
if (function_exists($function_name)) {
+ $this->db->setLogMessage('Running migration '.$function_name);
call_user_func($function_name, $this->db->getConnection());
}
- }
- $this->db->getDriver()->setSchemaVersion($i - 1);
- $this->db->getDriver()->enableForeignKeys();
- $this->db->closeTransaction();
- }
- catch (PDOException $e) {
- $this->db->setLogMessage($function_name.' => '.$e->getMessage());
+ $this->db->getDriver()->setSchemaVersion($i);
+ $this->db->getDriver()->enableForeignKeys();
+ $this->db->closeTransaction();
+ }
+ } catch (PDOException $e) {
+ $this->db->setLogMessage($e->getMessage());
$this->db->cancelTransaction();
$this->db->getDriver()->enableForeignKeys();
return false;
diff --git a/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php
new file mode 100644
index 0000000..a7021b3
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php
@@ -0,0 +1,353 @@
+db = $db;
+ }
+
+ /**
+ * Enable query logging
+ *
+ * @access public
+ * @return $this
+ */
+ public function withLogging()
+ {
+ $this->logQueries = true;
+ return $this;
+ }
+
+ /**
+ * Record query execution time
+ *
+ * @access public
+ * @return $this
+ */
+ public function withStopWatch()
+ {
+ $this->stopwatch = true;
+ return $this;
+ }
+
+ /**
+ * Execute explain command on query
+ *
+ * @access public
+ * @return $this
+ */
+ public function withExplain()
+ {
+ $this->explain = true;
+ return $this;
+ }
+
+ /**
+ * Set SQL query
+ *
+ * @access public
+ * @param string $sql
+ * @return $this
+ */
+ public function withSql($sql)
+ {
+ $this->sql = $sql;
+ return $this;
+ }
+
+ /**
+ * Set positional parameters
+ *
+ * @access public
+ * @param array $params
+ * @return $this
+ */
+ public function withPositionalParams(array $params)
+ {
+ $this->positionalParams = $params;
+ return $this;
+ }
+
+ /**
+ * Set named parameters
+ *
+ * @access public
+ * @param array $params
+ * @return $this
+ */
+ public function withNamedParams(array $params)
+ {
+ $this->namedParams = $params;
+ $this->useNamedParams = true;
+ return $this;
+ }
+
+ /**
+ * Bind large object parameter
+ *
+ * @access public
+ * @param $name
+ * @param $fp
+ * @return $this
+ */
+ public function withLobParam($name, &$fp)
+ {
+ $this->lobParams[$name] =& $fp;
+ return $this;
+ }
+
+ /**
+ * Get number of queries executed
+ *
+ * @access public
+ * @return int
+ */
+ public function getNbQueries()
+ {
+ return $this->nbQueries;
+ }
+
+ /**
+ * Execute a prepared statement
+ *
+ * Note: returns false on duplicate keys instead of SQLException
+ *
+ * @access public
+ * @return PDOStatement|false
+ */
+ public function execute()
+ {
+ try {
+ $this->beforeExecute();
+
+ $pdoStatement = $this->db->getConnection()->prepare($this->sql);
+ $this->bindParams($pdoStatement);
+ $pdoStatement->execute();
+
+ $this->afterExecute();
+ return $pdoStatement;
+ } catch (PDOException $e) {
+ return $this->handleSqlError($e);
+ }
+ }
+
+ /**
+ * Bind parameters to PDOStatement
+ *
+ * @access protected
+ * @param PDOStatement $pdoStatement
+ */
+ protected function bindParams(PDOStatement $pdoStatement)
+ {
+ $i = 1;
+
+ foreach ($this->lobParams as $name => $variable) {
+ if (! $this->useNamedParams) {
+ $parameter = $i;
+ $i++;
+ } else {
+ $parameter = $name;
+ }
+
+ $pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB);
+ }
+
+ foreach ($this->positionalParams as $value) {
+ $pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
+ $i++;
+ }
+
+ foreach ($this->namedParams as $name => $value) {
+ $pdoStatement->bindValue($name, $value, PDO::PARAM_STR);
+ }
+ }
+
+ /**
+ * Method executed before query execution
+ *
+ * @access protected
+ */
+ protected function beforeExecute()
+ {
+ if ($this->logQueries) {
+ $this->db->setLogMessage($this->sql);
+ }
+
+ if ($this->stopwatch) {
+ $this->startTime = microtime(true);
+ }
+ }
+
+ /**
+ * Method executed after query execution
+ *
+ * @access protected
+ */
+ protected function afterExecute()
+ {
+ if ($this->stopwatch) {
+ $duration = microtime(true) - $this->startTime;
+ $this->executionTime += $duration;
+ $this->db->setLogMessage('query_duration='.$duration);
+ $this->db->setLogMessage('total_execution_time='.$this->executionTime);
+ }
+
+ if ($this->explain) {
+ $this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams));
+ }
+
+ $this->nbQueries++;
+ $this->cleanup();
+ }
+
+ /**
+ * Reset internal properties after execution
+ * The same object instance is used
+ *
+ * @access protected
+ */
+ protected function cleanup()
+ {
+ $this->sql = '';
+ $this->useNamedParams = false;
+ $this->positionalParams = array();
+ $this->namedParams = array();
+ $this->lobParams = array();
+ }
+
+ /**
+ * Handle PDOException
+ *
+ * @access public
+ * @param PDOException $e
+ * @return bool
+ * @throws SQLException
+ */
+ public function handleSqlError(PDOException $e)
+ {
+ $this->cleanup();
+ $this->db->cancelTransaction();
+ $this->db->setLogMessage($e->getMessage());
+
+ if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) {
+ return false;
+ }
+
+ throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : ''));
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Table.php b/vendor/fguillot/picodb/lib/PicoDb/Table.php
index ca8dd69..09eb928 100644
--- a/vendor/fguillot/picodb/lib/PicoDb/Table.php
+++ b/vendor/fguillot/picodb/lib/PicoDb/Table.php
@@ -4,27 +4,37 @@ namespace PicoDb;
use PDO;
use Closure;
+use PicoDb\Builder\ConditionBuilder;
+use PicoDb\Builder\InsertBuilder;
+use PicoDb\Builder\UpdateBuilder;
/**
* Table
*
- * @author Frederic Guillot
+ * @package PicoDb
+ * @author Frederic Guillot
*
- * @method Table addCondition($sql)
- * @method Table beginOr()
- * @method Table closeOr()
- * @method Table eq($column, $value)
- * @method Table neq($column, $value)
- * @method Table in($column, array $values)
- * @method Table notin($column, array $values)
- * @method Table like($column, $value)
- * @method Table ilike($column, $value)
- * @method Table gt($column, $value)
- * @method Table lt($column, $value)
- * @method Table gte($column, $value)
- * @method Table lte($column, $value)
- * @method Table isNull($column)
- * @method Table notNull($column)
+ * @method $this addCondition($sql)
+ * @method $this beginOr()
+ * @method $this closeOr()
+ * @method $this eq($column, $value)
+ * @method $this neq($column, $value)
+ * @method $this in($column, array $values)
+ * @method $this inSubquery($column, Table $subquery)
+ * @method $this notIn($column, array $values)
+ * @method $this notInSubquery($column, Table $subquery)
+ * @method $this like($column, $value)
+ * @method $this ilike($column, $value)
+ * @method $this gt($column, $value)
+ * @method $this gtSubquery($column, Table $subquery)
+ * @method $this lt($column, $value)
+ * @method $this ltSubquery($column, Table $subquery)
+ * @method $this gte($column, $value)
+ * @method $this gteSubquery($column, Table $subquery)
+ * @method $this lte($column, $value)
+ * @method $this lteSubquery($column, Table $subquery)
+ * @method $this isNull($column)
+ * @method $this notNull($column)
*/
class Table
{
@@ -40,10 +50,10 @@ class Table
/**
* Condition instance
*
- * @access public
- * @var Condition
+ * @access protected
+ * @var ConditionBuilder
*/
- public $condition;
+ protected $conditionBuilder;
/**
* Database instance
@@ -152,7 +162,7 @@ class Table
{
$this->db = $db;
$this->name = $name;
- $this->condition = new Condition($db);
+ $this->conditionBuilder = new ConditionBuilder($db);
}
/**
@@ -166,6 +176,17 @@ class Table
return $this->name;
}
+ /**
+ * Return ConditionBuilder object
+ *
+ * @access public
+ * @return ConditionBuilder
+ */
+ public function getConditionBuilder()
+ {
+ return $this->conditionBuilder;
+ }
+
/**
* Insert or update
*
@@ -175,47 +196,24 @@ class Table
*/
public function save(array $data)
{
- return $this->condition->hasCondition() ? $this->update($data) : $this->insert($data);
+ return $this->conditionBuilder->hasCondition() ? $this->update($data) : $this->insert($data);
}
/**
* Update
*
- * Note: Do not use `rowCount()` for update the behaviour is different across drivers
- *
* @access public
* @param array $data
* @return boolean
*/
public function update(array $data = array())
{
- $columns = array();
- $values = array();
-
- // Split columns and values
- foreach ($data as $column => $value) {
- $columns[] = $this->db->escapeIdentifier($column).'=?';
- $values[] = $value;
- }
-
- // Sum columns
- foreach ($this->sumColumns as $column => $value) {
- $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?';
- $values[] = $value;
- }
-
- // Append condition values
- foreach ($this->condition->getValues() as $value) {
- $values[] = $value;
- }
-
- // Build SQL query
- $sql = sprintf(
- 'UPDATE %s SET %s %s',
- $this->db->escapeIdentifier($this->name),
- implode(', ', $columns),
- $this->condition->build()
- );
+ $values = array_merge(array_values($data), array_values($this->sumColumns), $this->conditionBuilder->getValues());
+ $sql = UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns(array_keys($data))
+ ->withSumColumns(array_keys($this->sumColumns))
+ ->build();
return $this->db->execute($sql, $values) !== false;
}
@@ -229,20 +227,30 @@ class Table
*/
public function insert(array $data)
{
- $columns = array();
+ return $this->db->getStatementHandler()
+ ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns(array_keys($data))
+ ->build()
+ )
+ ->withNamedParams($data)
+ ->execute() !== false;
+ }
- foreach ($data as $column => $value) {
- $columns[] = $this->db->escapeIdentifier($column);
+ /**
+ * Insert a new row and return the ID of the primary key
+ *
+ * @access public
+ * @param array $data
+ * @return bool|int
+ */
+ public function persist(array $data)
+ {
+ if ($this->insert($data)) {
+ return $this->db->getLastId();
}
- $sql = sprintf(
- 'INSERT INTO %s (%s) VALUES (%s)',
- $this->db->escapeIdentifier($this->name),
- implode(', ', $columns),
- implode(', ', array_fill(0, count($data), '?'))
- );
-
- return $this->db->execute($sql, array_values($data)) !== false;
+ return false;
}
/**
@@ -256,10 +264,10 @@ class Table
$sql = sprintf(
'DELETE FROM %s %s',
$this->db->escapeIdentifier($this->name),
- $this->condition->build()
+ $this->conditionBuilder->build()
);
- $result = $this->db->execute($sql, $this->condition->getValues());
+ $result = $this->db->execute($sql, $this->conditionBuilder->getValues());
return $result->rowCount() > 0;
}
@@ -271,7 +279,7 @@ class Table
*/
public function findAll()
{
- $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
$results = $rq->fetchAll(PDO::FETCH_ASSOC);
if (is_callable($this->callback) && ! empty($results)) {
@@ -291,7 +299,7 @@ class Table
public function findAllByColumn($column)
{
$this->columns = array($column);
- $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
return $rq->fetchAll(PDO::FETCH_COLUMN, 0);
}
@@ -322,7 +330,7 @@ class Table
$this->limit(1);
$this->columns = array($column);
- return $this->db->execute($this->buildSelectQuery(), $this->condition->getValues())->fetchColumn();
+ return $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues())->fetchColumn();
}
/**
@@ -331,7 +339,7 @@ class Table
* @access public
* @param string $sql
* @param string $alias
- * @return Table
+ * @return $this
*/
public function subquery($sql, $alias)
{
@@ -348,11 +356,11 @@ class Table
public function exists()
{
$sql = sprintf(
- 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->condition->build(),
+ 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build(),
$this->db->escapeIdentifier($this->name)
);
- $rq = $this->db->execute($sql, $this->condition->getValues());
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
$result = $rq->fetchColumn();
return $result ? true : false;
@@ -367,11 +375,11 @@ class Table
public function count()
{
$sql = sprintf(
- 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
+ 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
$this->db->escapeIdentifier($this->name)
);
- $rq = $this->db->execute($sql, $this->condition->getValues());
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
$result = $rq->fetchColumn();
return $result ? (int) $result : 0;
@@ -387,17 +395,59 @@ class Table
public function sum($column)
{
$sql = sprintf(
- 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
+ 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
$this->db->escapeIdentifier($column),
$this->db->escapeIdentifier($this->name)
);
- $rq = $this->db->execute($sql, $this->condition->getValues());
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
$result = $rq->fetchColumn();
return $result ? (float) $result : 0;
}
+ /**
+ * Increment column value
+ *
+ * @access public
+ * @param string $column
+ * @param string $value
+ * @return boolean
+ */
+ public function increment($column, $value)
+ {
+ $sql = sprintf(
+ 'UPDATE %s SET %s=%s+%d '.$this->conditionBuilder->build(),
+ $this->db->escapeIdentifier($this->name),
+ $this->db->escapeIdentifier($column),
+ $this->db->escapeIdentifier($column),
+ $value
+ );
+
+ return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
+ }
+
+ /**
+ * Decrement column value
+ *
+ * @access public
+ * @param string $column
+ * @param string $value
+ * @return boolean
+ */
+ public function decrement($column, $value)
+ {
+ $sql = sprintf(
+ 'UPDATE %s SET %s=%s-%d '.$this->conditionBuilder->build(),
+ $this->db->escapeIdentifier($this->name),
+ $this->db->escapeIdentifier($column),
+ $this->db->escapeIdentifier($column),
+ $value
+ );
+
+ return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
+ }
+
/**
* Left join
*
@@ -407,7 +457,7 @@ class Table
* @param string $local_column Local column
* @param string $local_table Local table
* @param string $alias Join table alias
- * @return Table
+ * @return $this
*/
public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
{
@@ -430,7 +480,7 @@ class Table
* @param string $column1
* @param string $table2
* @param string $column2
- * @return Table
+ * @return $this
*/
public function left($table1, $alias1, $column1, $table2, $column2)
{
@@ -445,13 +495,37 @@ class Table
return $this;
}
+ /**
+ * Inner join
+ *
+ * @access public
+ * @param string $table1
+ * @param string $alias1
+ * @param string $column1
+ * @param string $table2
+ * @param string $column2
+ * @return $this
+ */
+ public function inner($table1, $alias1, $column1, $table2, $column2)
+ {
+ $this->joins[] = sprintf(
+ 'JOIN %s AS %s ON %s=%s',
+ $this->db->escapeIdentifier($table1),
+ $this->db->escapeIdentifier($alias1),
+ $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
+ $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
+ );
+
+ return $this;
+ }
+
/**
* Order by
*
* @access public
* @param string $column Column name
* @param string $order Direction ASC or DESC
- * @return Table
+ * @return $this
*/
public function orderBy($column, $order = self::SORT_ASC)
{
@@ -473,7 +547,7 @@ class Table
*
* @access public
* @param string $column
- * @return Table
+ * @return $this
*/
public function asc($column)
{
@@ -486,7 +560,7 @@ class Table
*
* @access public
* @param string $column
- * @return Table
+ * @return $this
*/
public function desc($column)
{
@@ -499,7 +573,7 @@ class Table
*
* @access public
* @param integer $value
- * @return Table
+ * @return $this
*/
public function limit($value)
{
@@ -515,7 +589,7 @@ class Table
*
* @access public
* @param integer $value
- * @return Table
+ * @return $this
*/
public function offset($value)
{
@@ -530,7 +604,7 @@ class Table
* Group by
*
* @access public
- * @return Table
+ * @return $this
*/
public function groupBy()
{
@@ -543,7 +617,7 @@ class Table
*
* @access public
* @param string $select
- * @return Table
+ * @return $this
*/
public function select($select)
{
@@ -555,7 +629,7 @@ class Table
* Define the columns for the select
*
* @access public
- * @return Table
+ * @return $this
*/
public function columns()
{
@@ -569,7 +643,7 @@ class Table
* @access public
* @param string $column
* @param mixed $value
- * @return Table
+ * @return $this
*/
public function sumColumn($column, $value)
{
@@ -581,7 +655,7 @@ class Table
* Distinct
*
* @access public
- * @return Table
+ * @return $this
*/
public function distinct()
{
@@ -595,7 +669,7 @@ class Table
*
* @access public
* @param Closure|array $callback
- * @return Table
+ * @return $this
*/
public function callback($callback)
{
@@ -623,7 +697,7 @@ class Table
$this->sqlSelect,
$this->db->escapeIdentifier($this->name),
implode(' ', $this->joins),
- $this->condition->build(),
+ $this->conditionBuilder->build(),
empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy),
$this->sqlOrder,
$this->sqlLimit,
@@ -637,11 +711,11 @@ class Table
* @access public
* @param string $name
* @param array $arguments
- * @return Table
+ * @return $this
*/
public function __call($name, array $arguments)
{
- call_user_func_array(array($this->condition, $name), $arguments);
+ call_user_func_array(array($this->conditionBuilder, $name), $arguments);
return $this;
}
}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php
new file mode 100644
index 0000000..d8fcaf0
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php
@@ -0,0 +1,93 @@
+url = getenv($environmentVariable);
+ }
+
+ /**
+ * Get object instance
+ *
+ * @access public
+ * @param string $environmentVariable
+ * @return static
+ */
+ public static function getInstance($environmentVariable = 'DATABASE_URL')
+ {
+ return new static($environmentVariable);
+ }
+
+ /**
+ * Return true if the variable is defined
+ *
+ * @access public
+ * @return bool
+ */
+ public function isEnvironmentVariableDefined()
+ {
+ return ! empty($this->url);
+ }
+
+ /**
+ * Get settings from URL
+ *
+ * @access public
+ * @param string $url
+ * @return array
+ */
+ public function getSettings($url = '')
+ {
+ $url = $url ?: $this->url;
+ $components = parse_url($url);
+
+ if ($components === false) {
+ return array();
+ }
+
+ return array(
+ 'driver' => $this->getUrlComponent($components, 'scheme'),
+ 'username' => $this->getUrlComponent($components, 'user'),
+ 'password' => $this->getUrlComponent($components, 'pass'),
+ 'hostname' => $this->getUrlComponent($components, 'host'),
+ 'port' => $this->getUrlComponent($components, 'port'),
+ 'database' => ltrim($this->getUrlComponent($components, 'path'), '/'),
+ );
+ }
+
+ /**
+ * Get URL component
+ *
+ * @access private
+ * @param array $components
+ * @param string $component
+ * @return mixed|null
+ */
+ private function getUrlComponent(array $components, $component)
+ {
+ return ! empty($components[$component]) ? $components[$component] : null;
+ }
+}