Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorrobocoder <anthon.pang@gmail.com>2009-09-18 03:29:27 +0400
committerrobocoder <anthon.pang@gmail.com>2009-09-18 03:29:27 +0400
commit0494a63c73790b3d62788e3f5f6eefca468ea22b (patch)
tree471116a26dd71c2a0208dddb94e6fc2931f73318 /core
parentfd16c9ce87e010d1e5652c362e23ae05b41fd854 (diff)
fixes #904 - MySQL error codes; unsupported adapters can map these to driver-specific SQLSTATE (see example)
fixes #980 - Piwik Installation support for "MySQL Improved" (mysqli) extension fixes #984 - Set client connection charset to utf8. Fixed tracker profiling data not recorded until after report generated. More refactoring and database abstraction: - Installation gets a list of adapters instead of hardcoding in the plugin - checking for database-specific system requirements deferred to the adapter - error detection moved to adapter but we still use MySQL error codes rather than defining new constants Note: unit tests don't run with MYSQLI -- Zend Framework's Mysqli adapater doesn't support prepare() yet git-svn-id: http://dev.piwik.org/svn/trunk@1473 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'core')
-rw-r--r--core/Archive/Single.php14
-rw-r--r--core/ArchiveProcessing.php21
-rw-r--r--core/Db.php85
-rw-r--r--core/Db/Mysqli.php64
-rw-r--r--core/Db/Pdo/Mysql.php86
-rw-r--r--core/Db/Pdo/Pgsql.php147
-rw-r--r--core/Piwik.php44
-rw-r--r--core/Tracker.php2
-rw-r--r--core/Tracker/Action.php4
-rw-r--r--core/Tracker/Db.php26
-rw-r--r--core/Tracker/Db/Mysqli.php98
-rw-r--r--core/Tracker/Db/Pdo/Mysql.php34
-rw-r--r--core/Tracker/Db/Pdo/Pgsql.php82
-rw-r--r--core/Tracker/GoalManager.php2
-rw-r--r--core/Tracker/Visit.php7
-rw-r--r--core/Updater.php5
-rw-r--r--core/Updates/0.4.2.php4
-rw-r--r--core/Updates/0.4.3.php14
18 files changed, 671 insertions, 68 deletions
diff --git a/core/Archive/Single.php b/core/Archive/Single.php
index 52b3f7c7e9..464ecf7f31 100644
--- a/core/Archive/Single.php
+++ b/core/Archive/Single.php
@@ -255,7 +255,7 @@ class Piwik_Archive_Single extends Piwik_Archive
}
// uncompress when selecting from the BLOB table
- if($typeValue == 'blob')
+ if($typeValue == 'blob' && $db->hasBlobDataType())
{
$value = gzuncompress($value);
}
@@ -331,6 +331,7 @@ class Piwik_Archive_Single extends Piwik_Archive
$tableBlob = $this->archiveProcessing->getTableArchiveBlobName();
$db = Zend_Registry::get('db');
+ $hasBlobs = $db->hasBlobDataType();
$query = $db->query("SELECT value, name
FROM $tableBlob
WHERE idarchive = ?
@@ -342,8 +343,15 @@ class Piwik_Archive_Single extends Piwik_Archive
{
$value = $row['value'];
$name = $row['name'];
-
- $this->blobCached[$name] = gzuncompress($value);
+
+ if($hasBlobs)
+ {
+ $this->blobCached[$name] = gzuncompress($value);
+ }
+ else
+ {
+ $this->blobCached[$name] = $value;
+ }
}
}
diff --git a/core/ArchiveProcessing.php b/core/ArchiveProcessing.php
index c46b2794d7..91eed568a4 100644
--- a/core/ArchiveProcessing.php
+++ b/core/ArchiveProcessing.php
@@ -101,6 +101,13 @@ abstract class Piwik_ArchiveProcessing
* @var int
*/
protected $maxTimestampArchive;
+
+ /**
+ * Compress blobs
+ *
+ * @var bool
+ */
+ protected $compressBlob;
/**
* Id of the current site
@@ -263,6 +270,9 @@ abstract class Piwik_ArchiveProcessing
$this->maxTimestampArchive = Piwik_Date::today()->getTimestamp();
}
}
+
+ $db = Zend_Registry::get('db');
+ $this->compressBlob = $db->hasBlobDataType();
}
/**
@@ -466,7 +476,16 @@ abstract class Piwik_ArchiveProcessing
destroy($records);
return true;
}
- $record = new Piwik_ArchiveProcessing_Record_Blob($name, $value);
+
+ if($this->compressBlob)
+ {
+ $record = new Piwik_ArchiveProcessing_Record_Blob($name, $value);
+ }
+ else
+ {
+ $record = new Piwik_ArchiveProcessing_Record($name, $value);
+ }
+
$this->insertRecord($record);
destroy($record);
return true;
diff --git a/core/Db.php b/core/Db.php
index 19f23b3db7..7d26980e6d 100644
--- a/core/Db.php
+++ b/core/Db.php
@@ -17,6 +17,7 @@ class Piwik_Db
{
/**
* Create adapter
+ *
* @return mixed (Piwik_Db_Mysqli, Piwik_Db_Pdo_Mysql, etc)
*/
public static function factory($adapterName, $config)
@@ -25,4 +26,88 @@ class Piwik_Db
$adapter = new $adapterName($config);
return $adapter;
}
+
+ /*
+ * Recursive glob()
+ *
+ * @return array
+ */
+ private static function globr($sDir, $sPattern, $nFlags = NULL)
+ {
+ $sDir = escapeshellcmd($sDir);
+ $aFiles = glob("$sDir/$sPattern", $nFlags);
+ foreach (glob("$sDir/*", GLOB_ONLYDIR) as $sSubDir)
+ {
+ $aSubFiles = self::globr($sSubDir, $sPattern, $nFlags);
+ $aFiles = array_merge($aFiles, $aSubFiles);
+ }
+ return $aFiles;
+ }
+
+ /**
+ * Get list of adapters
+ *
+ * @return array
+ */
+ public static function getAdapters()
+ {
+ $path = PIWIK_INCLUDE_PATH . '/core/Db';
+ $pathLength = strlen($path) + 1;
+ $adapters = self::globr($path, '*.php');
+ $adapterNames = array();
+ foreach($adapters as $adapter)
+ {
+ $adapterName = str_replace('/', '_', substr($adapter, $pathLength, -strlen('.php')));
+ $className = 'Piwik_Db_'.$adapterName;
+ if(call_user_func(array($className, 'isEnabled')))
+ {
+ $adapterNames[strtoupper($adapterName)] = call_user_func(array($className, 'getDefaultPort'));
+ }
+ }
+ return $adapterNames;
+ }
+}
+
+interface Piwik_Db_iAdapter
+{
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig();
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort();
+
+ /**
+ * Check database server version
+ *
+ * @throws Exception if database version is less than required version
+ */
+ public function checkServerVersion();
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled();
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType();
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno);
}
diff --git a/core/Db/Mysqli.php b/core/Db/Mysqli.php
index c0f32becd1..4b5dd09d09 100644
--- a/core/Db/Mysqli.php
+++ b/core/Db/Mysqli.php
@@ -13,8 +13,14 @@
/**
* @package Piwik
*/
-class Piwik_Db_Mysqli extends Zend_Db_Adapter_Mysqli
+class Piwik_Db_Mysqli extends Zend_Db_Adapter_Mysqli implements Piwik_Db_iAdapter
{
+ public function __construct($config)
+ {
+ $config['charset'] = 'utf8';
+ parent::__construct($config);
+ }
+
/**
* Reset the configuration variables in this adapter.
*/
@@ -22,4 +28,60 @@ class Piwik_Db_Mysqli extends Zend_Db_Adapter_Mysqli
{
$this->_config = array();
}
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
+
+ /**
+ * Check MySQL version
+ */
+ public function checkServerVersion()
+ {
+// $databaseVersion = $this->getServerVersion();
+ $databaseVersion = $this->fetchOne('SELECT VERSION()', array());
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mysql_version;
+ if(version_compare($databaseVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('Core_ExceptionDatabaseVersion', array('MySQL', $databaseVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('mysqli', $extensions) && function_exists('mysqli_set_charset');
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ return mysqli_errno($this->_connection) == $errno;
+ }
}
diff --git a/core/Db/Pdo/Mysql.php b/core/Db/Pdo/Mysql.php
index 0daac70d6a..5d6f1241ce 100644
--- a/core/Db/Pdo/Mysql.php
+++ b/core/Db/Pdo/Mysql.php
@@ -13,8 +13,35 @@
/**
* @package Piwik
*/
-class Piwik_Db_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql
+class Piwik_Db_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql implements Piwik_Db_iAdapter
{
+ public function __construct($config)
+ {
+ $config['driver_options'] = array(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
+ parent::__construct($config);
+ }
+
+ /**
+ * Returns connection handle
+ *
+ * @return resource
+ */
+ public function getConnection()
+ {
+ if($this->_connection)
+ {
+ return $this->_connection;
+ }
+
+ $this->_connect();
+
+ // see http://framework.zend.com/issues/browse/ZF-1398
+ $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+ $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+
+ return $this->_connection;
+ }
+
/**
* Reset the configuration variables in this adapter.
*/
@@ -22,4 +49,61 @@ class Piwik_Db_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql
{
$this->_config = array();
}
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 3306;
+ }
+
+ /**
+ * Check MySQL version
+ */
+ public function checkServerVersion()
+ {
+// $databaseVersion = $this->getServerVersion();
+ $databaseVersion = $this->fetchOne('SELECT VERSION()', array());
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_mysql_version;
+ if(version_compare($databaseVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('Core_ExceptionDatabaseVersion', array('MySQL', $databaseVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_mysql', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ return true;
+ }
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ $errInfo = $this->errorInfo();
+ return $errInfo[1] == $errno;
+ }
}
diff --git a/core/Db/Pdo/Pgsql.php b/core/Db/Pdo/Pgsql.php
new file mode 100644
index 0000000000..511bbab56d
--- /dev/null
+++ b/core/Db/Pdo/Pgsql.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
+ * @version $Id$
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+
+/**
+ * @package Piwik
+ */
+class Piwik_Db_Pdo_Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements Piwik_Db_iAdapter
+{
+ /**
+ * Reset the configuration variables in this adapter.
+ */
+ public function resetConfig()
+ {
+ $this->_config = array();
+ }
+
+ /**
+ * Return default port.
+ *
+ * @return int
+ */
+ public static function getDefaultPort()
+ {
+ return 5432;
+ }
+
+ /**
+ * Check PostgreSQL version
+ */
+ public function checkServerVersion()
+ {
+// $databaseVersion = $this->getServerVersion();
+ $databaseVersion = $this->fetchOne('show server_version', array());
+ $requiredVersion = Zend_Registry::get('config')->General->minimum_pgsql_version;
+ if(version_compare($databaseVersion, $requiredVersion) === -1)
+ {
+ throw new Exception(Piwik_TranslateException('Core_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
+ }
+ }
+
+ /**
+ * Returns true if this adapter's required extensions are enabled
+ *
+ * @return bool
+ */
+ public static function isEnabled()
+ {
+ /**
+ * @todo This adapter is incomplete.
+ */
+ return false;
+ $extensions = @get_loaded_extensions();
+ return in_array('PDO', $extensions) && in_array('pdo_pgsql', $extensions);
+ }
+
+ /**
+ * Returns true if this adapter supports blobs as fields
+ *
+ * @return bool
+ */
+ public function hasBlobDataType()
+ {
+ // large objects must be loaded from a file using a non-SQL API
+ // and then referenced by the object ID (oid);
+ // the alternative, bytea fields, incur a space and time
+ // penalty for encoding/decoding
+ return false;
+ }
+
+ /**
+ * Pre-process SQL to handle MySQL-isms
+ *
+ * @return string
+ */
+ public function preprocessSql($query)
+ {
+ $search = array(
+ // In MySQL, OPTION is still a reserved keyword; Piwik uses
+ // backticking in case table_prefix is empty.
+ '`',
+
+ // MySQL implicitly does 'ORDER BY column' when there's a
+ // 'GROUP BY column'; Piwik uses 'ORDER BY NULL' when order
+ // doesn't matter, for better performance.
+ 'ORDER BY NULL',
+ );
+
+ $replace = array(
+ '',
+ '',
+ );
+
+ $query = str_replace($search, $replace, $query);
+ }
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
+ $map = array(
+ // MySQL: Unknown database '%s'
+ // PostgreSQL: database "%s" does not exist
+ '1049' => '08006',
+
+ // MySQL: Table '%s' already exists
+ // PostgreSQL: relation "%s" already exists
+ '1050' => '42P07',
+
+ // MySQL: Unknown column '%s' in '%s'
+ // PostgreSQL: column "%s" does not exist
+ '1054' => '42703',
+
+ // MySQL: Duplicate column name '%s'
+ // PostgreSQL: column "%s" of relation "%s" already exists
+ '1060' => '42701',
+
+ // MySQL: Duplicate entry '%s' for key '%s'
+ // PostgreSQL: duplicate key violates unique constraint
+ '1062' => '23505',
+
+ // MySQL: Can't DROP '%s'; check that column/key exists
+ // PostgreSQL: index "%s" does not exist
+ '1091' => '42704',
+
+ // MySQL: Table '%s.%s' doesn't exist
+ // PostgreSQL: relation "%s" does not exist
+ '1146' => '42P01',
+ );
+
+ $errInfo = $this->errorInfo();
+ return $errInfo[0] == $map[$errno];
+ }
+}
diff --git a/core/Piwik.php b/core/Piwik.php
index 257417329c..1a66d9d719 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -246,17 +246,16 @@ class Piwik
if(is_null($db))
{
- $db = Zend_Registry::get('db');
+ $db = Piwik_Tracker::getDatabase();
}
$tableName = Piwik_Common::prefixTable('log_profiling');
- $all = $db->fetchAll('SELECT *, sum_time_ms / count as avg_time_ms
- FROM '.$tableName );
+ $all = $db->fetchAll('SELECT * FROM '.$tableName );
if($all === false)
{
return;
}
- usort($all, 'maxSumMsFirst');
+ uasort($all, 'maxSumMsFirst');
$infoIndexedByQuery = array();
foreach($all as $infoQuery)
@@ -298,6 +297,7 @@ class Piwik
'sumTimeMs' => $existing['count'] + $query->getElapsedSecs() * 1000);
$infoIndexedByQuery[$query->getQuery()] = $new;
}
+
if(!function_exists('sortTimeDesc'))
{
function sortTimeDesc($a,$b)
@@ -341,9 +341,7 @@ class Piwik
$avgTimeMs = $timeMs / $count;
$avgTimeString = " (average = <b>". round($avgTimeMs,1) . "ms</b>)";
}
- $query = str_replace(array("\t","\n","\r\n","\r"), "_toberemoved_", $query);
- $query = str_replace('_toberemoved__toberemoved_','',$query);
- $query = str_replace('_toberemoved_', ' ',$query);
+ $query = preg_replace('/([\t\n\r ]+)/', ' ', $query);
$output .= "Executed <b>$count</b> time". ($count==1?'':'s') ." in <b>".$timeMs."ms</b> $avgTimeString <pre>\t$query</pre>";
}
Piwik::log($output);
@@ -1385,7 +1383,7 @@ class Piwik
$tablesInstalled = array_intersect($allMyTables, $allTables);
// at this point we have only the piwik tables which is good
- // but we still miss the piwik generated tables (using the class Piwik_TablePartitioning)`
+ // but we still miss the piwik generated tables (using the class Piwik_TablePartitioning)
$idSiteInSql = "no";
if(!is_null($idSite))
{
@@ -1439,16 +1437,14 @@ class Piwik
unset($dbInfos['host']);
unset($dbInfos['port']);
}
+
+ // not used by Zend Framework
+ unset($dbInfos['tables_prefix']);
+ unset($dbInfos['adapter']);
+
$db = Piwik_Db::factory($config->database->adapter, $dbInfos);
$db->getConnection();
- if($config->database->adapter == 'PDO_MYSQL')
- {
- // see http://framework.zend.com/issues/browse/ZF-1398
- $db->getConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
- $db->getConnection()->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
- }
-
Zend_Db_Table::setDefaultAdapter($db);
$db->resetConfig(); // we don't want this information to appear in the logs
}
@@ -1460,11 +1456,29 @@ class Piwik
Zend_Registry::get('db')->closeConnection();
}
+ /**
+ * Returns the MySQL database server version
+ *
+ * @deprecated 0.4.4
+ */
static public function getMysqlVersion()
{
return Piwik_FetchOne("SELECT VERSION()");
}
+ /**
+ * Checks the database server version against the required minimum
+ * version.
+ *
+ * @see config/global.ini.php
+ * @since 0.4.4
+ * @throws Exception if server version is less than the required version
+ */
+ static public function checkDatabaseVersion()
+ {
+ Zend_Registry::get('db')->checkServerVersion();
+ }
+
static public function createLogObject()
{
$configAPI = Zend_Registry::get('config')->log;
diff --git a/core/Tracker.php b/core/Tracker.php
index 88b4bedc6d..e49ecc06c2 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -71,6 +71,7 @@ class Piwik_Tracker
} catch(Piwik_Tracker_Visit_Excluded $e) {
}
}
+
$this->end();
}
@@ -130,6 +131,7 @@ class Piwik_Tracker
if($GLOBALS['PIWIK_TRACKER_DEBUG'] === true)
{
+ self::$db->recordProfiling();
Piwik::printSqlProfilingReportTracker(self::$db);
}
diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php
index 1cbb72156c..5afe0738cf 100644
--- a/core/Tracker/Action.php
+++ b/core/Tracker/Action.php
@@ -138,10 +138,10 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface
);
// the action name has not been found, create it
- if($idAction === false)
+ if($idAction === false || $idAction == '')
{
Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */
- INSERT INTO ". Piwik_Common::prefixTable('log_action'). "( name, type )
+ INSERT INTO ". Piwik_Common::prefixTable('log_action'). " ( name, type )
VALUES (?,?)",
array($this->getActionName(),$this->getActionType())
);
diff --git a/core/Tracker/Db.php b/core/Tracker/Db.php
index 6616e74c74..193c1a71a5 100644
--- a/core/Tracker/Db.php
+++ b/core/Tracker/Db.php
@@ -31,7 +31,7 @@ abstract class Piwik_Tracker_Db
* You can then use Piwik::printSqlProfilingReportTracker();
* to display the SQLProfiling report and see which queries take time, etc.
*/
- public function enableProfiling()
+ public static function enableProfiling()
{
self::$profiling = true;
}
@@ -39,7 +39,7 @@ abstract class Piwik_Tracker_Db
/**
* Disables the SQL profiling logging.
*/
- public function disableProfiling()
+ public static function disableProfiling()
{
self::$profiling = false;
}
@@ -121,10 +121,6 @@ abstract class Piwik_Tracker_Db
*/
public function disconnect()
{
- if(self::$profiling)
- {
- $this->recordProfiling();
- }
$this->connection = null;
}
@@ -159,6 +155,14 @@ abstract class Piwik_Tracker_Db
}
/**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ abstract public function rowCount($queryResult);
+
+ /**
* Executes a query, using optional bound parameters.
*
* @param string Query
@@ -176,4 +180,12 @@ abstract class Piwik_Tracker_Db
* @return int
*/
abstract public function lastInsertId();
- }
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool True if error number matches; false otherwise
+ */
+ abstract public function isErrNo($errno);
+}
diff --git a/core/Tracker/Db/Mysqli.php b/core/Tracker/Db/Mysqli.php
index 61454c4be1..90c70e7e45 100644
--- a/core/Tracker/Db/Mysqli.php
+++ b/core/Tracker/Db/Mysqli.php
@@ -18,8 +18,10 @@
*/
class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
{
- private $connection = null;
+ protected $connection = null;
private $host;
+ private $port;
+ private $socket;
private $dbname;
private $username;
private $password;
@@ -31,15 +33,21 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
{
if(isset($dbInfo['unix_socket']) && $dbInfo['unix_socket'][0] == '/')
{
- $this->host = ':' . $dbInfo['unix_socket'];
+ $this->host = null;
+ $this->port = null;
+ $this->socket = $dbInfo['unix_socket'];
}
else if ($dbInfo['port'][0] == '/')
{
- $this->host = ':' . $dbInfo['port'];
+ $this->host = null;
+ $this->port = null;
+ $this->socket = $dbInfo['port'];
}
else
{
- $this->host = $dbInfo['host'] . ':' . $dbInfo['port'];
+ $this->host = $dbInfo['host'];
+ $this->port = $dbInfo['port'];
+ $this->socket = null;
}
$this->dbname = $dbInfo['dbname'];
$this->username = $dbInfo['username'];
@@ -63,8 +71,16 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
$timer = $this->initProfiler();
}
- $this->connection = mysql_connect($this->host, $this->username, $this->password);
- $result = mysql_select_db($this->dbname);
+ $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket);
+ if(!$this->connection || mysqli_connect_errno())
+ {
+ throw new Exception("Connect failed: " . mysqli_connect_error());
+ }
+
+ if(!mysqli_set_charset($this->connection, 'utf8'))
+ {
+ throw new Exception("Set Charset failed: " . mysqli_error($this->connection));
+ }
$this->password = '';
@@ -79,10 +95,7 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
*/
public function disconnect()
{
- if(self::$profiling)
- {
- $this->recordProfiling();
- }
+ mysqli_close($this->connection);
$this->connection = null;
}
@@ -97,12 +110,23 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
public function fetchAll( $query, $parameters = array() )
{
try {
+ if(self::$profiling)
+ {
+ $timer = $this->initProfiler();
+ }
+
$query = $this->prepare( $query, $parameters );
- $rs = mysql_query($query);
- while($row = mysql_fetch_array($rs, MYSQL_ASSOC))
+ $rs = mysqli_query($this->connection, $query);
+ while($row = mysqli_fetch_array($rs, MYSQL_ASSOC))
{
$rows[] = $row;
}
+ mysqli_free_result($rs);
+
+ if(self::$profiling)
+ {
+ $this->recordQueryProfile($query, $timer);
+ }
return $rows;
} catch (Exception $e) {
throw new Exception("Error query: ".$e->getMessage());
@@ -121,13 +145,24 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
public function fetch( $query, $parameters = array() )
{
try {
+ if(self::$profiling)
+ {
+ $timer = $this->initProfiler();
+ }
+
$query = $this->prepare( $query, $parameters );
- $rs = mysql_query($query);
+ $rs = mysqli_query($this->connection, $query);
if($rs === false)
{
return false;
}
- $row = mysql_fetch_array($rs, MYSQL_ASSOC);
+ $row = mysqli_fetch_array($rs, MYSQL_ASSOC);
+ mysqli_free_result($rs);
+
+ if(self::$profiling)
+ {
+ $this->recordQueryProfile($query, $timer);
+ }
return $row;
} catch (Exception $e) {
throw new Exception("Error query: ".$e->getMessage());
@@ -149,6 +184,7 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
{
return false;
}
+
try {
if(self::$profiling)
{
@@ -160,7 +196,11 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
$parameters = array( $parameters );
}
$query = $this->prepare( $query, $parameters );
- $result = mysql_query($query);
+ $result = mysqli_query($this->connection, $query);
+ if(!is_bool($result))
+ {
+ mysqli_free_result($result);
+ }
if(self::$profiling)
{
@@ -173,7 +213,7 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
Parameters: ".var_export($parameters, true));
}
}
-
+
/**
* Returns the last inserted ID in the DB
*
@@ -181,7 +221,7 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
*/
public function lastInsertId()
{
- return mysql_insert_id();
+ return mysqli_insert_id($this->connection);
}
/**
@@ -199,7 +239,29 @@ class Piwik_Tracker_Db_Mysqli extends Piwik_Tracker_Db
}
$query = str_replace('?', "'%s'", $query);
array_unshift($parameters, $query);
- $query = call_user_func_array(sprintf, $parameters);
+ $query = call_user_func_array('sprintf', $parameters);
return $query;
}
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ return mysqli_errno($this->_connection) == $errno;
+ }
+
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return mysqli_affected_rows($this->connection);
+ }
}
diff --git a/core/Tracker/Db/Pdo/Mysql.php b/core/Tracker/Db/Pdo/Mysql.php
index 5c67be8973..bb8ced6da9 100644
--- a/core/Tracker/Db/Pdo/Mysql.php
+++ b/core/Tracker/Db/Pdo/Mysql.php
@@ -18,7 +18,7 @@
*/
class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
{
- private $connection = null;
+ protected $connection = null;
private $dsn;
private $username;
private $password;
@@ -60,8 +60,10 @@ class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
{
$timer = $this->initProfiler();
}
+
+ $config = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'");
- $this->connection = new PDO($this->dsn, $this->username, $this->password);
+ $this->connection = new PDO($this->dsn, $this->username, $this->password, $config);
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// we may want to setAttribute(PDO::ATTR_TIMEOUT ) to a few seconds (default is 60) in case the DB is locked
// the piwik.php would stay waiting for the database... bad!
@@ -79,10 +81,6 @@ class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
*/
public function disconnect()
{
- if(self::$profiling)
- {
- $this->recordProfiling();
- }
$this->connection = null;
}
@@ -146,6 +144,7 @@ class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
{
return false;
}
+
try {
if(self::$profiling)
{
@@ -181,4 +180,27 @@ class Piwik_Tracker_Db_Pdo_Mysql extends Piwik_Tracker_Db
{
return $this->connection->lastInsertId();
}
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ $errInfo = $this->errorInfo();
+ return $errInfo[1] == $errno;
+ }
+
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return $queryResult->rowCount();
+ }
}
diff --git a/core/Tracker/Db/Pdo/Pgsql.php b/core/Tracker/Db/Pdo/Pgsql.php
new file mode 100644
index 0000000000..d415cbd632
--- /dev/null
+++ b/core/Tracker/Db/Pdo/Pgsql.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
+ * @version $Id$
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+
+/**
+ * PDO PostgreSQL wrapper
+ *
+ * @package Piwik
+ * @subpackage Piwik_Tracker
+ */
+class Piwik_Tracker_Db_Pdo_Pgsql extends Piwik_Tracker_Db_Pdo_Mysql
+{
+ /**
+ * Builds the DB object
+ */
+ public function __construct( $dbInfo, $driverName = 'pgsql')
+ {
+ parent::__construct( $dbInfo, $driverName );
+ }
+
+ /**
+ * Test error number
+ *
+ * @param string $errno
+ * @return bool
+ */
+ public function isErrNo($errno)
+ {
+ // map MySQL driver-specific error codes to PostgreSQL SQLSTATE
+ $map = array(
+ // MySQL: Unknown database '%s'
+ // PostgreSQL: database "%s" does not exist
+ '1049' => '08006',
+
+ // MySQL: Table '%s' already exists
+ // PostgreSQL: relation "%s" already exists
+ '1050' => '42P07',
+
+ // MySQL: Unknown column '%s' in '%s'
+ // PostgreSQL: column "%s" does not exist
+ '1054' => '42703',
+
+ // MySQL: Duplicate column name '%s'
+ // PostgreSQL: column "%s" of relation "%s" already exists
+ '1060' => '42701',
+
+ // MySQL: Duplicate entry '%s' for key '%s'
+ // PostgreSQL: duplicate key violates unique constraint
+ '1062' => '23505',
+
+ // MySQL: Can't DROP '%s'; check that column/key exists
+ // PostgreSQL: index "%s" does not exist
+ '1091' => '42704',
+
+ // MySQL: Table '%s.%s' doesn't exist
+ // PostgreSQL: relation "%s" does not exist
+ '1146' => '42P01',
+ );
+
+ $errInfo = $this->errorInfo();
+ return $errInfo[0] == $map[$errno];
+ }
+
+ /**
+ * Return number of affected rows in last query
+ *
+ * @param mixed $queryResult Result from query()
+ * @return int
+ */
+ public function rowCount($queryResult)
+ {
+ return $queryResult->rowCount();
+ }
+}
diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index 1208ce60a1..074f9fdcbf 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -211,7 +211,7 @@ class Piwik_Tracker_GoalManager
VALUES ($bindFields) ", array_values($newGoal)
);
} catch( Exception $e) {
- if(strpos($e->getMessage(), '1062') !== false)
+ if(Piwik_Tracker::isErrNo('1062'))
{
// integrity violation when same visit converts to the same goal twice
printDebug("--> Goal already recorded for this (idvisit, idgoal)");
diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php
index 3b129ef102..323a720806 100644
--- a/core/Tracker/Visit.php
+++ b/core/Tracker/Visit.php
@@ -230,7 +230,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
visit_total_actions = visit_total_actions + 1, ";
$this->visitorInfo['visit_exit_idaction'] = $actionId;
}
- $statement = Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ". $this->idsite ." */
+
+ $result = Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ". $this->idsite ." */
UPDATE ". Piwik_Common::prefixTable('log_visit')."
SET $sqlActionIdUpdate
$sqlUpdateGoalConverted
@@ -243,10 +244,12 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface
$this->visitorInfo['idvisit'],
$this->visitorInfo['visitor_idcookie'] )
);
- if($statement->rowCount() == 0)
+
+ if(Piwik_Tracker::getDatabase()->rowCount($result) == 0)
{
throw new Piwik_Tracker_Visit_VisitorNotFoundInDatabase("The visitor with visitor_idcookie=".$this->visitorInfo['visitor_idcookie']." and idvisit=".$this->visitorInfo['idvisit']." wasn't found in the DB, we fallback to a new visitor");
}
+
$this->visitorInfo['idsite'] = $this->idsite;
$this->visitorInfo['visit_server_date'] = $this->getCurrentDate();
diff --git a/core/Updater.php b/core/Updater.php
index a7647cb838..b5f6aa990b 100644
--- a/core/Updater.php
+++ b/core/Updater.php
@@ -192,7 +192,8 @@ class Piwik_Updater
try {
$currentVersion = Piwik_GetOption('version_'.$name);
} catch( Exception $e) {
- if(preg_match('/1146/', $e->getMessage()))
+ // mysql error 1146: table doesn't exist
+ if(Zend_Registry::get('db')->isErrNo('1146'))
{
// case when the option table is not yet created (before 0.2.10)
$currentVersion = false;
@@ -245,7 +246,7 @@ class Piwik_Updater
try {
Piwik_Query( $update );
} catch(Exception $e) {
- if(($ignoreError === false) || !preg_match($ignoreError, $e->getMessage()))
+ if(($ignoreError === false) || !Zend_Registry::get('db')->isErrNo($ignoreError))
{
$message = $file .":\nError trying to execute the query '". $update ."'.\nThe error was: ". $e->getMessage();
throw new Piwik_Updater_UpdateErrorException($message);
diff --git a/core/Updates/0.4.2.php b/core/Updates/0.4.2.php
index abb1e38ba2..ef91786033 100644
--- a/core/Updates/0.4.2.php
+++ b/core/Updates/0.4.2.php
@@ -20,9 +20,9 @@ class Piwik_Updates_0_4_2 implements Piwik_iUpdate
{
Piwik_Updater::updateDatabase(__FILE__, array(
'ALTER TABLE `'. Piwik::prefixTable('log_visit') .'`
- ADD `config_java` TINYINT(1) NOT NULL AFTER `config_flash`' => '/1060/',
+ ADD `config_java` TINYINT(1) NOT NULL AFTER `config_flash`' => '1060',
'ALTER TABLE `'. Piwik::prefixTable('log_visit') .'`
- ADD `config_quicktime` TINYINT(1) NOT NULL AFTER `config_director`' => '/1060/',
+ ADD `config_quicktime` TINYINT(1) NOT NULL AFTER `config_director`' => '1060',
'ALTER TABLE `'. Piwik::prefixTable('log_visit') .'`
ADD `config_gears` TINYINT(1) NOT NULL AFTER `config_windowsmedia`,
ADD `config_silverlight` TINYINT(1) NOT NULL AFTER `config_gears`' => false,
diff --git a/core/Updates/0.4.3.php b/core/Updates/0.4.3.php
index 95e7c35512..b4524186a1 100644
--- a/core/Updates/0.4.3.php
+++ b/core/Updates/0.4.3.php
@@ -20,7 +20,7 @@ class Piwik_Updates_0_4_3 implements Piwik_iUpdate
Piwik_Updater::updateDatabase(__FILE__, array(
// 0.1.7 [463]
'ALTER IGNORE TABLE `'. Piwik::prefixTable('log_visit') .'`
- CHANGE `location_provider` `location_provider` VARCHAR( 100 ) DEFAULT NULL' => '/1054/',
+ CHANGE `location_provider` `location_provider` VARCHAR( 100 ) DEFAULT NULL' => '1054',
// 0.1.7 [470]
'ALTER TABLE `'. Piwik::prefixTable('logger_api_call') .'`
CHANGE `parameter_names_default_values` `parameter_names_default_values` TEXT,
@@ -34,10 +34,10 @@ class Piwik_Updates_0_4_3 implements Piwik_iUpdate
CHANGE `message` `message` TEXT' => false,
// 0.2.2 [489]
'ALTER IGNORE TABLE `'. Piwik::prefixTable('site') .'`
- CHANGE `feedburnerName` `feedburnerName` VARCHAR( 100 ) DEFAULT NULL' => '/1054/',
+ CHANGE `feedburnerName` `feedburnerName` VARCHAR( 100 ) DEFAULT NULL' => '1054',
// 0.2.12 [673]
// Note: requires INDEX privilege
- 'DROP INDEX index_idaction ON `'. Piwik::prefixTable('log_action') .'`' => '/1072|1091/',
+ 'DROP INDEX index_idaction ON `'. Piwik::prefixTable('log_action') .'`' => '1091',
// 0.2.27 [826]
'ALTER IGNORE TABLE `'. Piwik::prefixTable('log_visit') .'`
CHANGE `visit_goal_converted` `visit_goal_converted` TINYINT(1) NOT NULL' => false,
@@ -47,14 +47,14 @@ class Piwik_Updates_0_4_3 implements Piwik_iUpdate
'ALTER TABLE `'. Piwik::prefixTable('user') .'`
CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => false,
'ALTER TABLE `'. Piwik::prefixTable('user_dashboard') .'`
- CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '/1146/',
+ CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '1146',
'ALTER TABLE `'. Piwik::prefixTable('user_language') .'`
- CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '/1146/',
+ CHANGE `login` `login` VARCHAR( 100 ) NOT NULL' => '1146',
// 0.2.33 [1020]
'ALTER TABLE `'. Piwik::prefixTable('user_dashboard') .'`
- CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '/1146/',
+ CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '1146',
'ALTER TABLE `'. Piwik::prefixTable('user_language') .'`
- CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '/1146/',
+ CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci ' => '1146',
// 0.4 [1140]
'ALTER TABLE `'. Piwik::prefixTable('log_visit') .'`
CHANGE `location_ip` `location_ip` BIGINT UNSIGNED NOT NULL' => false,