merge
This commit is contained in:
commit
046a724272
4209 changed files with 1186656 additions and 0 deletions
114
www/analytics/core/Db/Adapter.php
Normal file
114
www/analytics/core/Db/Adapter.php
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db;
|
||||
|
||||
|
||||
use Piwik\Loader;
|
||||
use Zend_Db_Table;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Adapter
|
||||
{
|
||||
/**
|
||||
* Create adapter
|
||||
*
|
||||
* @param string $adapterName database adapter name
|
||||
* @param array $dbInfos database connection info
|
||||
* @param bool $connect
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public static function factory($adapterName, & $dbInfos, $connect = true)
|
||||
{
|
||||
if ($connect) {
|
||||
if ($dbInfos['port'][0] == '/') {
|
||||
$dbInfos['unix_socket'] = $dbInfos['port'];
|
||||
unset($dbInfos['host']);
|
||||
unset($dbInfos['port']);
|
||||
}
|
||||
|
||||
// not used by Zend Framework
|
||||
unset($dbInfos['tables_prefix']);
|
||||
unset($dbInfos['adapter']);
|
||||
unset($dbInfos['schema']);
|
||||
}
|
||||
|
||||
$className = self::getAdapterClassName($adapterName);
|
||||
Loader::loadClass($className);
|
||||
|
||||
$adapter = new $className($dbInfos);
|
||||
|
||||
if ($connect) {
|
||||
$adapter->getConnection();
|
||||
|
||||
Zend_Db_Table::setDefaultAdapter($adapter);
|
||||
// we don't want the connection information to appear in the logs
|
||||
$adapter->resetConfig();
|
||||
}
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get adapter class name
|
||||
*
|
||||
* @param string $adapterName
|
||||
* @return string
|
||||
*/
|
||||
private static function getAdapterClassName($adapterName)
|
||||
{
|
||||
return 'Piwik\Db\Adapter\\' . str_replace(' ', '\\', ucwords(str_replace(array('_', '\\'), ' ', strtolower($adapterName))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default port for named adapter
|
||||
*
|
||||
* @param string $adapterName
|
||||
* @return int
|
||||
*/
|
||||
public static function getDefaultPortForAdapter($adapterName)
|
||||
{
|
||||
$className = self::getAdapterClassName($adapterName);
|
||||
return call_user_func(array($className, 'getDefaultPort'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of adapters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAdapters()
|
||||
{
|
||||
static $adapterNames = array(
|
||||
// currently supported by Piwik
|
||||
'Pdo\Mysql',
|
||||
'Mysqli',
|
||||
|
||||
// other adapters supported by Zend_Db
|
||||
// 'Pdo_Pgsql',
|
||||
// 'Pdo_Mssql',
|
||||
// 'Sqlsrv',
|
||||
// 'Pdo_Ibm',
|
||||
// 'Db2',
|
||||
// 'Pdo_Oci',
|
||||
// 'Oracle',
|
||||
);
|
||||
|
||||
$adapters = array();
|
||||
|
||||
foreach ($adapterNames as $adapterName) {
|
||||
$className = '\Piwik\Db\Adapter\\' . $adapterName;
|
||||
if (call_user_func(array($className, 'isEnabled'))) {
|
||||
$adapters[strtoupper($adapterName)] = call_user_func(array($className, 'getDefaultPort'));
|
||||
}
|
||||
}
|
||||
|
||||
return $adapters;
|
||||
}
|
||||
}
|
||||
177
www/analytics/core/Db/Adapter/Mysqli.php
Normal file
177
www/analytics/core/Db/Adapter/Mysqli.php
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db\Adapter;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Config;
|
||||
use Piwik\Db\AdapterInterface;
|
||||
use Piwik\Piwik;
|
||||
use Zend_Config;
|
||||
use Zend_Db_Adapter_Mysqli;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array|Zend_Config $config database configuration
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
// Enable LOAD DATA INFILE
|
||||
$config['driver_options'][MYSQLI_OPT_LOCAL_INFILE] = true;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the configuration variables in this adapter.
|
||||
*/
|
||||
public function resetConfig()
|
||||
{
|
||||
$this->_config = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default port.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getDefaultPort()
|
||||
{
|
||||
return 3306;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check MySQL version
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkServerVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$requiredVersion = Config::getInstance()->General['minimum_mysql_version'];
|
||||
if (version_compare($serverVersion, $requiredVersion) === -1) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check client version compatibility against database server
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkClientVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$clientVersion = $this->getClientVersion();
|
||||
// incompatible change to DECIMAL implementation in 5.0.3
|
||||
if (version_compare($serverVersion, '5.0.3') >= 0
|
||||
&& version_compare($clientVersion, '5.0.3') < 0
|
||||
) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports blobs as fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBlobDataType()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports bulk loading
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBulkLoader()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error number
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param string $errno
|
||||
* @return bool
|
||||
*/
|
||||
public function isErrNo($e, $errno)
|
||||
{
|
||||
if (is_null($this->_connection)) {
|
||||
if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
|
||||
return $match[1] == $errno;
|
||||
}
|
||||
return mysqli_connect_errno() == $errno;
|
||||
}
|
||||
|
||||
return mysqli_errno($this->_connection) == $errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute unprepared SQL query and throw away the result
|
||||
*
|
||||
* Workaround some SQL statements not compatible with prepare().
|
||||
* See http://framework.zend.com/issues/browse/ZF-1398
|
||||
*
|
||||
* @param string $sqlQuery
|
||||
* @return int Number of rows affected (SELECT/INSERT/UPDATE/DELETE)
|
||||
*/
|
||||
public function exec($sqlQuery)
|
||||
{
|
||||
$rc = mysqli_query($this->_connection, $sqlQuery);
|
||||
$rowsAffected = mysqli_affected_rows($this->_connection);
|
||||
if (!is_bool($rc)) {
|
||||
mysqli_free_result($rc);
|
||||
}
|
||||
return $rowsAffected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the connection character set equal to utf8?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionUTF8()
|
||||
{
|
||||
$charset = mysqli_character_set_name($this->_connection);
|
||||
return $charset === 'utf8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientVersion()
|
||||
{
|
||||
$this->_connect();
|
||||
$version = $this->_connection->server_version;
|
||||
$major = (int)($version / 10000);
|
||||
$minor = (int)($version % 10000 / 100);
|
||||
$revision = (int)($version % 100);
|
||||
return $major . '.' . $minor . '.' . $revision;
|
||||
}
|
||||
}
|
||||
264
www/analytics/core/Db/Adapter/Pdo/Mssql.php
Normal file
264
www/analytics/core/Db/Adapter/Pdo/Mssql.php
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db\Adapter\Pdo;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Piwik\Config;
|
||||
use Piwik\Db\AdapterInterface;
|
||||
use Piwik\Piwik;
|
||||
use Zend_Db;
|
||||
use Zend_Db_Adapter_Exception;
|
||||
use Zend_Db_Adapter_Pdo_Mssql;
|
||||
use Zend_Db_Profiler;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Returns connection handle
|
||||
*
|
||||
* @throws Zend_Db_Adapter_Exception
|
||||
* @return resource
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
// if we already have a PDO object, no need to re-connect.
|
||||
if ($this->_connection) {
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
$this->_pdoType = "sqlsrv";
|
||||
// get the dsn first, because some adapters alter the $_pdoType
|
||||
//$dsn = $this->_dsn();
|
||||
|
||||
// check for PDO extension
|
||||
if (!extension_loaded('pdo')) {
|
||||
/**
|
||||
* @see Zend_Db_Adapter_Exception
|
||||
*/
|
||||
throw new \Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
|
||||
}
|
||||
|
||||
// check the PDO driver is available
|
||||
if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) {
|
||||
/**
|
||||
* @see Zend_Db_Adapter_Exception
|
||||
*/
|
||||
throw new \Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
|
||||
}
|
||||
|
||||
// create PDO connection
|
||||
$q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
|
||||
|
||||
// add the persistence flag if we find it in our config array
|
||||
if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true)) {
|
||||
$this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
|
||||
}
|
||||
|
||||
try {
|
||||
$serverName = $this->_config["host"];
|
||||
$database = $this->_config["dbname"];
|
||||
if (is_null($database)) {
|
||||
$database = 'master';
|
||||
}
|
||||
$uid = $this->_config['username'];
|
||||
$pwd = $this->_config['password'];
|
||||
if ($this->_config["port"] != "") {
|
||||
$serverName = $serverName . "," . $this->_config["port"];
|
||||
}
|
||||
|
||||
$this->_connection = new PDO("sqlsrv:$serverName", $uid, $pwd, array('Database' => $database));
|
||||
|
||||
if ($this->_connection === false) {
|
||||
die(self::FormatErrors(sqlsrv_errors()));
|
||||
}
|
||||
|
||||
/*
|
||||
$this->_connection = new PDO(
|
||||
$dsn,
|
||||
$this->_config['username'],
|
||||
$this->_config['password'],
|
||||
$this->_config['driver_options']
|
||||
);
|
||||
*/
|
||||
|
||||
$this->_profiler->queryEnd($q);
|
||||
|
||||
// set the PDO connection to perform case-folding on array keys, or not
|
||||
$this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
|
||||
$this->_connection->setAttribute(PDO::SQLSRV_ENCODING_UTF8, true);
|
||||
|
||||
// always use exceptions.
|
||||
$this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
return $this->_connection;
|
||||
} catch (PDOException $e) {
|
||||
/**
|
||||
* @see Zend_Db_Adapter_Exception
|
||||
*/
|
||||
throw new \Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the configuration variables in this adapter.
|
||||
*/
|
||||
public function resetConfig()
|
||||
{
|
||||
$this->_config = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default port.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getDefaultPort()
|
||||
{
|
||||
return 1433;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check MSSQL version
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkServerVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$requiredVersion = Config::getInstance()->General['minimum_mssql_version'];
|
||||
if (version_compare($serverVersion, $requiredVersion) === -1) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Mssql server version
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getServerVersion()
|
||||
{
|
||||
try {
|
||||
$stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion");
|
||||
$result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
|
||||
if (count($result)) {
|
||||
return $result[0][0];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check client version compatibility against database server
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkClientVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$clientVersion = $this->getClientVersion();
|
||||
if (version_compare($serverVersion, '10') >= 0
|
||||
&& version_compare($clientVersion, '10') < 0
|
||||
) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionIncompatibleClientServerVersions', array('MSSQL', $clientVersion, $serverVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_sqlsrv', $extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports blobs as fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBlobDataType()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports bulk loading
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBulkLoader()
|
||||
{
|
||||
/**
|
||||
* BULK INSERT doesn't have a way to escape a terminator that appears in a value
|
||||
*
|
||||
* @link http://msdn.microsoft.com/en-us/library/ms188365.aspx
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error number
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param string $errno
|
||||
* @return bool
|
||||
*/
|
||||
public function isErrNo($e, $errno)
|
||||
{
|
||||
if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
|
||||
return $match[1] == $errno;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the connection character set equal to utf8?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionUTF8()
|
||||
{
|
||||
//check the getconnection, it's specified on the connection string.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve client version in PHP style
|
||||
*
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
public function getClientVersion()
|
||||
{
|
||||
$this->_connect();
|
||||
try {
|
||||
$version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
|
||||
$requiredVersion = Config::getInstance()->General['minimum_mssql_client_version'];
|
||||
if (version_compare($version['DriverVer'], $requiredVersion) === -1) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MSSQL', $version['DriverVer'], $requiredVersion)));
|
||||
} else {
|
||||
return $version['DriverVer'];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// In case of the driver doesn't support getting attributes
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
233
www/analytics/core/Db/Adapter/Pdo/Mysql.php
Normal file
233
www/analytics/core/Db/Adapter/Pdo/Mysql.php
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db\Adapter\Pdo;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Piwik\Config;
|
||||
use Piwik\Db\AdapterInterface;
|
||||
use Piwik\Piwik;
|
||||
use Zend_Config;
|
||||
use Zend_Db_Adapter_Pdo_Mysql;
|
||||
use Zend_Db_Select;
|
||||
use Zend_Db_Statement_Interface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array|Zend_Config $config database configuration
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
// Enable LOAD DATA INFILE
|
||||
if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE')) {
|
||||
$config['driver_options'][PDO::MYSQL_ATTR_LOCAL_INFILE] = true;
|
||||
}
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns connection handle
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
if ($this->_connection) {
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
$this->_connect();
|
||||
|
||||
/**
|
||||
* Before MySQL 5.1.17, server-side prepared statements
|
||||
* do not use the query cache.
|
||||
* @see http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html
|
||||
*
|
||||
* MySQL also does not support preparing certain DDL and SHOW
|
||||
* statements.
|
||||
* @see http://framework.zend.com/issues/browse/ZF-1398
|
||||
*/
|
||||
$this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
|
||||
// MYSQL_ATTR_USE_BUFFERED_QUERY will use more memory when enabled
|
||||
// $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
|
||||
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the configuration variables in this adapter.
|
||||
*/
|
||||
public function resetConfig()
|
||||
{
|
||||
$this->_config = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default port.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getDefaultPort()
|
||||
{
|
||||
return 3306;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check MySQL version
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkServerVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$requiredVersion = Config::getInstance()->General['minimum_mysql_version'];
|
||||
if (version_compare($serverVersion, $requiredVersion) === -1) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check client version compatibility against database server
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkClientVersion()
|
||||
{
|
||||
$serverVersion = $this->getServerVersion();
|
||||
$clientVersion = $this->getClientVersion();
|
||||
// incompatible change to DECIMAL implementation in 5.0.3
|
||||
if (version_compare($serverVersion, '5.0.3') >= 0
|
||||
&& version_compare($clientVersion, '5.0.3') < 0
|
||||
) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionIncompatibleClientServerVersions', array('MySQL', $clientVersion, $serverVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) && in_array('mysql', PDO::getAvailableDrivers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports blobs as fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBlobDataType()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports bulk loading
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBulkLoader()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error number
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param string $errno
|
||||
* @return bool
|
||||
*/
|
||||
public function isErrNo($e, $errno)
|
||||
{
|
||||
if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
|
||||
return $match[1] == $errno;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the connection character set equal to utf8?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionUTF8()
|
||||
{
|
||||
$charsetInfo = $this->fetchAll('SHOW VARIABLES LIKE ?', array('character_set_connection'));
|
||||
if (empty($charsetInfo)) {
|
||||
return false;
|
||||
}
|
||||
$charset = $charsetInfo[0]['Value'];
|
||||
return $charset === 'utf8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve client version in PHP style
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientVersion()
|
||||
{
|
||||
$this->_connect();
|
||||
try {
|
||||
$version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
|
||||
$matches = null;
|
||||
if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// In case of the driver doesn't support getting attributes
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var \Zend_Db_Statement_Pdo[]
|
||||
*/
|
||||
private $cachePreparedStatement = array();
|
||||
|
||||
/**
|
||||
* Prepares and executes an SQL statement with bound data.
|
||||
* Caches prepared statements to avoid preparing the same query more than once
|
||||
*
|
||||
* @param string|Zend_Db_Select $sql The SQL statement with placeholders.
|
||||
* @param array $bind An array of data to bind to the placeholders.
|
||||
* @return Zend_Db_Statement_Interface
|
||||
*/
|
||||
public function query($sql, $bind = array())
|
||||
{
|
||||
if (!is_string($sql)) {
|
||||
return parent::query($sql, $bind);
|
||||
}
|
||||
|
||||
if (isset($this->cachePreparedStatement[$sql])) {
|
||||
if (!is_array($bind)) {
|
||||
$bind = array($bind);
|
||||
}
|
||||
|
||||
$stmt = $this->cachePreparedStatement[$sql];
|
||||
$stmt->execute($bind);
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
$stmt = parent::query($sql, $bind);
|
||||
$this->cachePreparedStatement[$sql] = $stmt;
|
||||
return $stmt;
|
||||
}
|
||||
}
|
||||
182
www/analytics/core/Db/Adapter/Pdo/Pgsql.php
Normal file
182
www/analytics/core/Db/Adapter/Pdo/Pgsql.php
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db\Adapter\Pdo;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Piwik\Config;
|
||||
use Piwik\Db\AdapterInterface;
|
||||
use Piwik\Piwik;
|
||||
use Zend_Db_Adapter_Pdo_Pgsql;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkServerVersion()
|
||||
{
|
||||
$databaseVersion = $this->getServerVersion();
|
||||
$requiredVersion = Config::getInstance()->General['minimum_pgsql_version'];
|
||||
if (version_compare($databaseVersion, $requiredVersion) === -1) {
|
||||
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check client version compatibility against database server
|
||||
*/
|
||||
public function checkClientVersion()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports bulk loading
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBulkLoader()
|
||||
{
|
||||
/**
|
||||
* COPY ?
|
||||
*
|
||||
* @link http://www.postgresql.org/docs/current/interactive/sql-copy.html
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test error number
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param string $errno
|
||||
* @return bool
|
||||
*/
|
||||
public function isErrNo($e, $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 key name '%s'
|
||||
// PostgreSQL: relation "%s" already exists
|
||||
'1061' => '42P07',
|
||||
|
||||
// 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',
|
||||
);
|
||||
|
||||
if (preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match)) {
|
||||
return $match[1] == $map[$errno];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the connection character set equal to utf8?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionUTF8()
|
||||
{
|
||||
$charset = $this->fetchOne('SHOW client_encoding');
|
||||
return strtolower($charset) === 'utf8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve client version in PHP style
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientVersion()
|
||||
{
|
||||
$this->_connect();
|
||||
try {
|
||||
$version = $this->_connection->getAttribute(PDO::ATTR_CLIENT_VERSION);
|
||||
$matches = null;
|
||||
if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// In case of the driver doesn't support getting attributes
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
73
www/analytics/core/Db/AdapterInterface.php
Normal file
73
www/analytics/core/Db/AdapterInterface.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Db;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
*/
|
||||
interface AdapterInterface
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Returns true if this adapter supports bulk loading
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBulkLoader();
|
||||
|
||||
/**
|
||||
* Test error number
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param string $errno
|
||||
* @return bool
|
||||
*/
|
||||
public function isErrNo($e, $errno);
|
||||
|
||||
/**
|
||||
* Is the connection character set equal to utf8?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnectionUTF8();
|
||||
}
|
||||
255
www/analytics/core/Db/BatchInsert.php
Normal file
255
www/analytics/core/Db/BatchInsert.php
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db;
|
||||
|
||||
use Exception;
|
||||
use Piwik\AssetManager;
|
||||
use Piwik\Common;
|
||||
use Piwik\Config;
|
||||
|
||||
use Piwik\Db;
|
||||
use Piwik\DbHelper;
|
||||
use Piwik\Log;
|
||||
use Piwik\SettingsPiwik;
|
||||
use Piwik\SettingsServer;
|
||||
|
||||
class BatchInsert
|
||||
{
|
||||
/**
|
||||
* Performs a batch insert into a specific table by iterating through the data
|
||||
*
|
||||
* NOTE: you should use tableInsertBatch() which will fallback to this function if LOAD DATA INFILE not available
|
||||
*
|
||||
* @param string $tableName PREFIXED table name! you must call Common::prefixTable() before passing the table name
|
||||
* @param array $fields array of unquoted field names
|
||||
* @param array $values array of data to be inserted
|
||||
* @param bool $ignoreWhenDuplicate Ignore new rows that contain unique key values that duplicate old rows
|
||||
*/
|
||||
public static function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true)
|
||||
{
|
||||
$fieldList = '(' . join(',', $fields) . ')';
|
||||
$ignore = $ignoreWhenDuplicate ? 'IGNORE' : '';
|
||||
|
||||
foreach ($values as $row) {
|
||||
$query = "INSERT $ignore
|
||||
INTO " . $tableName . "
|
||||
$fieldList
|
||||
VALUES (" . Common::getSqlStringFieldsArray($row) . ")";
|
||||
Db::query($query, $row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a batch insert into a specific table using either LOAD DATA INFILE or plain INSERTs,
|
||||
* as a fallback. On MySQL, LOAD DATA INFILE is 20x faster than a series of plain INSERTs.
|
||||
*
|
||||
* @param string $tableName PREFIXED table name! you must call Common::prefixTable() before passing the table name
|
||||
* @param array $fields array of unquoted field names
|
||||
* @param array $values array of data to be inserted
|
||||
* @param bool $throwException Whether to throw an exception that was caught while trying
|
||||
* LOAD DATA INFILE, or not.
|
||||
* @throws Exception
|
||||
* @return bool True if the bulk LOAD was used, false if we fallback to plain INSERTs
|
||||
*/
|
||||
public static function tableInsertBatch($tableName, $fields, $values, $throwException = false)
|
||||
{
|
||||
$filePath = PIWIK_USER_PATH . '/tmp/assets/' . $tableName . '-' . Common::generateUniqId() . '.csv';
|
||||
$filePath = SettingsPiwik::rewriteTmpPathWithHostname($filePath);
|
||||
|
||||
$loadDataInfileEnabled = Config::getInstance()->General['enable_load_data_infile'];
|
||||
|
||||
if ($loadDataInfileEnabled
|
||||
&& Db::get()->hasBulkLoader()) {
|
||||
try {
|
||||
$fileSpec = array(
|
||||
'delim' => "\t",
|
||||
'quote' => '"', // chr(34)
|
||||
'escape' => '\\\\', // chr(92)
|
||||
'escapespecial_cb' => function ($str) {
|
||||
return str_replace(array(chr(92), chr(34)), array(chr(92) . chr(92), chr(92) . chr(34)), $str);
|
||||
},
|
||||
'eol' => "\r\n",
|
||||
'null' => 'NULL',
|
||||
);
|
||||
|
||||
// hack for charset mismatch
|
||||
if (!DbHelper::isDatabaseConnectionUTF8() && !isset(Config::getInstance()->database['charset'])) {
|
||||
$fileSpec['charset'] = 'latin1';
|
||||
}
|
||||
|
||||
self::createCSVFile($filePath, $fileSpec, $values);
|
||||
|
||||
if (!is_readable($filePath)) {
|
||||
throw new Exception("File $filePath could not be read.");
|
||||
}
|
||||
|
||||
$rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec);
|
||||
if ($rc) {
|
||||
unlink($filePath);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::info("LOAD DATA INFILE failed or not supported, falling back to normal INSERTs... Error was: %s", $e->getMessage());
|
||||
|
||||
if ($throwException) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if all else fails, fallback to a series of INSERTs
|
||||
@unlink($filePath);
|
||||
self::tableInsertBatchIterate($tableName, $fields, $values);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch insert into table from CSV (or other delimited) file.
|
||||
*
|
||||
* @param string $tableName Name of table
|
||||
* @param array $fields Field names
|
||||
* @param string $filePath Path name of a file.
|
||||
* @param array $fileSpec File specifications (delimiter, line terminator, etc)
|
||||
*
|
||||
* @throws Exception
|
||||
* @return bool True if successful; false otherwise
|
||||
*/
|
||||
public static function createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec)
|
||||
{
|
||||
// Chroot environment: prefix the path with the absolute chroot path
|
||||
$chrootPath = Config::getInstance()->General['absolute_chroot_path'];
|
||||
if(!empty($chrootPath)) {
|
||||
$filePath = $chrootPath . $filePath;
|
||||
}
|
||||
|
||||
// On Windows, MySQL expects forward slashes as directory separators
|
||||
if (SettingsServer::isWindows()) {
|
||||
$filePath = str_replace('\\', '/', $filePath);
|
||||
}
|
||||
|
||||
$query = "
|
||||
'$filePath'
|
||||
REPLACE
|
||||
INTO TABLE
|
||||
`" . $tableName . "`";
|
||||
|
||||
if (isset($fileSpec['charset'])) {
|
||||
$query .= ' CHARACTER SET ' . $fileSpec['charset'];
|
||||
}
|
||||
|
||||
$fieldList = '(' . join(',', $fields) . ')';
|
||||
|
||||
$query .= "
|
||||
FIELDS TERMINATED BY
|
||||
'" . $fileSpec['delim'] . "'
|
||||
ENCLOSED BY
|
||||
'" . $fileSpec['quote'] . "'
|
||||
";
|
||||
if (isset($fileSpec['escape'])) {
|
||||
$query .= " ESCAPED BY '" . $fileSpec['escape'] . "'";
|
||||
}
|
||||
$query .= "
|
||||
LINES TERMINATED BY
|
||||
'" . $fileSpec['eol'] . "'
|
||||
$fieldList
|
||||
";
|
||||
|
||||
/*
|
||||
* First attempt: assume web server and MySQL server are on the same machine;
|
||||
* this requires that the db user have the FILE privilege; however, since this is
|
||||
* a global privilege, it may not be granted due to security concerns
|
||||
*/
|
||||
$keywords = array('');
|
||||
|
||||
/*
|
||||
* Second attempt: using the LOCAL keyword means the client reads the file and sends it to the server;
|
||||
* the LOCAL keyword may trigger a known PHP PDO_MYSQL bug when MySQL not built with --enable-local-infile
|
||||
* @see http://bugs.php.net/bug.php?id=54158
|
||||
*/
|
||||
$openBaseDir = ini_get('open_basedir');
|
||||
$safeMode = ini_get('safe_mode');
|
||||
if (empty($openBaseDir) && empty($safeMode)) {
|
||||
// php 5.x - LOAD DATA LOCAL INFILE is disabled if open_basedir restrictions or safe_mode enabled
|
||||
$keywords[] = 'LOCAL ';
|
||||
}
|
||||
|
||||
$exceptions = array();
|
||||
foreach ($keywords as $keyword) {
|
||||
$queryStart = 'LOAD DATA ' . $keyword . 'INFILE ';
|
||||
$sql = $queryStart . $query;
|
||||
try {
|
||||
$result = @Db::exec($sql);
|
||||
if (empty($result) || $result < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
// echo $sql . ' ---- ' . $e->getMessage();
|
||||
$code = $e->getCode();
|
||||
$message = $e->getMessage() . ($code ? "[$code]" : '');
|
||||
if (!Db::get()->isErrNo($e, '1148')) {
|
||||
Log::info("LOAD DATA INFILE failed... Error was: %s", $message);
|
||||
}
|
||||
$exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message;
|
||||
}
|
||||
}
|
||||
if (count($exceptions)) {
|
||||
throw new Exception(implode(",", $exceptions));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create CSV (or other delimited) files
|
||||
*
|
||||
* @param string $filePath filename to create
|
||||
* @param array $fileSpec File specifications (delimiter, line terminator, etc)
|
||||
* @param array $rows Array of array corresponding to rows of values
|
||||
* @throws Exception if unable to create or write to file
|
||||
*/
|
||||
static protected function createCSVFile($filePath, $fileSpec, $rows)
|
||||
{
|
||||
// Set up CSV delimiters, quotes, etc
|
||||
$delim = $fileSpec['delim'];
|
||||
$quote = $fileSpec['quote'];
|
||||
$eol = $fileSpec['eol'];
|
||||
$null = $fileSpec['null'];
|
||||
$escapespecial_cb = $fileSpec['escapespecial_cb'];
|
||||
|
||||
$fp = @fopen($filePath, 'wb');
|
||||
if (!$fp) {
|
||||
throw new Exception('Error creating the tmp file ' . $filePath . ', please check that the webserver has write permission to write this file.');
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$output = '';
|
||||
foreach ($row as $value) {
|
||||
if (!isset($value) || is_null($value) || $value === false) {
|
||||
$output .= $null . $delim;
|
||||
} else {
|
||||
$output .= $quote . $escapespecial_cb($value) . $quote . $delim;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace delim with eol
|
||||
$output = substr_replace($output, $eol, -1);
|
||||
|
||||
$ret = fwrite($fp, $output);
|
||||
if (!$ret) {
|
||||
fclose($fp);
|
||||
throw new Exception('Error writing to the tmp file ' . $filePath);
|
||||
}
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
@chmod($filePath, 0777);
|
||||
}
|
||||
}
|
||||
245
www/analytics/core/Db/Schema.php
Normal file
245
www/analytics/core/Db/Schema.php
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db;
|
||||
|
||||
use Piwik\Config;
|
||||
use Piwik\Singleton;
|
||||
|
||||
/**
|
||||
* Schema abstraction
|
||||
*
|
||||
* Note: no relation to the ZF proposals for Zend_Db_Schema_Manager
|
||||
*
|
||||
* @method static \Piwik\Db\Schema getInstance()
|
||||
*/
|
||||
class Schema extends Singleton
|
||||
{
|
||||
const DEFAULT_SCHEMA = 'Mysql';
|
||||
|
||||
/**
|
||||
* Type of database schema
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $schema = null;
|
||||
|
||||
|
||||
/**
|
||||
* Get schema class name
|
||||
*
|
||||
* @param string $schemaName
|
||||
* @return string
|
||||
*/
|
||||
private static function getSchemaClassName($schemaName)
|
||||
{
|
||||
// Upgrade from pre 2.0.4
|
||||
if(strtolower($schemaName) == 'myisam'
|
||||
|| empty($schemaName)) {
|
||||
$schemaName = self::DEFAULT_SCHEMA;
|
||||
}
|
||||
|
||||
$class = str_replace(' ', '\\', ucwords(str_replace('_', ' ', strtolower($schemaName))));
|
||||
return '\Piwik\Db\Schema\\' . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of schemas
|
||||
*
|
||||
* @param string $adapterName
|
||||
* @return array
|
||||
*/
|
||||
public static function getSchemas($adapterName)
|
||||
{
|
||||
static $allSchemaNames = array(
|
||||
'MYSQL' => array(
|
||||
self::DEFAULT_SCHEMA,
|
||||
// InfiniDB
|
||||
),
|
||||
|
||||
// Microsoft SQL Server
|
||||
// 'MSSQL' => array( 'Mssql' ),
|
||||
|
||||
// PostgreSQL
|
||||
// 'PDO_PGSQL' => array( 'Pgsql' ),
|
||||
|
||||
// IBM DB2
|
||||
// 'IBM' => array( 'Ibm' ),
|
||||
|
||||
// Oracle
|
||||
// 'OCI' => array( 'Oci' ),
|
||||
);
|
||||
|
||||
$adapterName = strtoupper($adapterName);
|
||||
switch ($adapterName) {
|
||||
case 'PDO_MYSQL':
|
||||
case 'MYSQLI':
|
||||
$adapterName = 'MYSQL';
|
||||
break;
|
||||
|
||||
case 'PDO_MSSQL':
|
||||
case 'SQLSRV':
|
||||
$adapterName = 'MSSQL';
|
||||
break;
|
||||
|
||||
case 'PDO_IBM':
|
||||
case 'DB2':
|
||||
$adapterName = 'IBM';
|
||||
break;
|
||||
|
||||
case 'PDO_OCI':
|
||||
case 'ORACLE':
|
||||
$adapterName = 'OCI';
|
||||
break;
|
||||
}
|
||||
$schemaNames = $allSchemaNames[$adapterName];
|
||||
|
||||
$schemas = array();
|
||||
|
||||
foreach ($schemaNames as $schemaName) {
|
||||
$className = __NAMESPACE__ . '\\Schema\\' . $schemaName;
|
||||
if (call_user_func(array($className, 'isAvailable'))) {
|
||||
$schemas[] = $schemaName;
|
||||
}
|
||||
}
|
||||
|
||||
return $schemas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load schema
|
||||
*/
|
||||
private function loadSchema()
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
$dbInfos = $config->database;
|
||||
$schemaName = trim($dbInfos['schema']);
|
||||
|
||||
$className = self::getSchemaClassName($schemaName);
|
||||
$this->schema = new $className();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance that subclasses Schema
|
||||
*
|
||||
* @return \Piwik\Db\SchemaInterface
|
||||
*/
|
||||
private function getSchema()
|
||||
{
|
||||
if ($this->schema === null) {
|
||||
$this->loadSchema();
|
||||
}
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL to create a specific Piwik table
|
||||
*
|
||||
* @param string $tableName name of the table to create
|
||||
* @return string SQL
|
||||
*/
|
||||
public function getTableCreateSql($tableName)
|
||||
{
|
||||
return $this->getSchema()->getTableCreateSql($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL to create Piwik tables
|
||||
*
|
||||
* @return array array of strings containing SQL
|
||||
*/
|
||||
public function getTablesCreateSql()
|
||||
{
|
||||
return $this->getSchema()->getTablesCreateSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new table in the database.
|
||||
*
|
||||
* @param string $nameWithoutPrefix The name of the table without any piwik prefix.
|
||||
* @param string $createDefinition The table create definition
|
||||
*/
|
||||
public function createTable($nameWithoutPrefix, $createDefinition)
|
||||
{
|
||||
$this->getSchema()->createTable($nameWithoutPrefix, $createDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create database
|
||||
*
|
||||
* @param null|string $dbName database name to create
|
||||
*/
|
||||
public function createDatabase($dbName = null)
|
||||
{
|
||||
$this->getSchema()->createDatabase($dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop database
|
||||
*/
|
||||
public function dropDatabase($dbName = null)
|
||||
{
|
||||
$this->getSchema()->dropDatabase($dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all tables
|
||||
*/
|
||||
public function createTables()
|
||||
{
|
||||
$this->getSchema()->createTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entry in the User table for the "anonymous" user.
|
||||
*/
|
||||
public function createAnonymousUser()
|
||||
{
|
||||
$this->getSchema()->createAnonymousUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate all tables
|
||||
*/
|
||||
public function truncateAllTables()
|
||||
{
|
||||
$this->getSchema()->truncateAllTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Names of all the prefixed tables in piwik
|
||||
* Doesn't use the DB
|
||||
*
|
||||
* @return array Table names
|
||||
*/
|
||||
public function getTablesNames()
|
||||
{
|
||||
return $this->getSchema()->getTablesNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of tables installed
|
||||
*
|
||||
* @param bool $forceReload Invalidate cache
|
||||
* @return array installed tables
|
||||
*/
|
||||
public function getTablesInstalled($forceReload = true)
|
||||
{
|
||||
return $this->getSchema()->getTablesInstalled($forceReload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Piwik tables exist
|
||||
*
|
||||
* @return bool True if tables exist; false otherwise
|
||||
*/
|
||||
public function hasTables()
|
||||
{
|
||||
return $this->getSchema()->hasTables();
|
||||
}
|
||||
}
|
||||
539
www/analytics/core/Db/Schema/Mysql.php
Normal file
539
www/analytics/core/Db/Schema/Mysql.php
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Db\Schema;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Common;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db\SchemaInterface;
|
||||
use Piwik\Db;
|
||||
use Piwik\DbHelper;
|
||||
|
||||
/**
|
||||
* MySQL schema
|
||||
*/
|
||||
class Mysql implements SchemaInterface
|
||||
{
|
||||
/**
|
||||
* Is this MySQL storage engine available?
|
||||
*
|
||||
* @param string $engineName
|
||||
* @return bool True if available and enabled; false otherwise
|
||||
*/
|
||||
static private function hasStorageEngine($engineName)
|
||||
{
|
||||
$db = Db::get();
|
||||
$allEngines = $db->fetchAssoc('SHOW ENGINES');
|
||||
if (array_key_exists($engineName, $allEngines)) {
|
||||
$support = $allEngines[$engineName]['Support'];
|
||||
return $support == 'DEFAULT' || $support == 'YES';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this schema available?
|
||||
*
|
||||
* @return bool True if schema is available; false otherwise
|
||||
*/
|
||||
static public function isAvailable()
|
||||
{
|
||||
return self::hasStorageEngine('InnoDB');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL to create Piwik tables
|
||||
*
|
||||
* @return array array of strings containing SQL
|
||||
*/
|
||||
public function getTablesCreateSql()
|
||||
{
|
||||
$engine = $this->getTableEngine();
|
||||
$prefixTables = $this->getTablePrefix();
|
||||
|
||||
$tables = array(
|
||||
'user' => "CREATE TABLE {$prefixTables}user (
|
||||
login VARCHAR(100) NOT NULL,
|
||||
password CHAR(32) NOT NULL,
|
||||
alias VARCHAR(45) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL,
|
||||
token_auth CHAR(32) NOT NULL,
|
||||
superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0',
|
||||
date_registered TIMESTAMP NULL,
|
||||
PRIMARY KEY(login),
|
||||
UNIQUE KEY uniq_keytoken(token_auth)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'access' => "CREATE TABLE {$prefixTables}access (
|
||||
login VARCHAR(100) NOT NULL,
|
||||
idsite INTEGER UNSIGNED NOT NULL,
|
||||
access VARCHAR(10) NULL,
|
||||
PRIMARY KEY(login, idsite)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'site' => "CREATE TABLE {$prefixTables}site (
|
||||
idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(90) NOT NULL,
|
||||
main_url VARCHAR(255) NOT NULL,
|
||||
ts_created TIMESTAMP NULL,
|
||||
ecommerce TINYINT DEFAULT 0,
|
||||
sitesearch TINYINT DEFAULT 1,
|
||||
sitesearch_keyword_parameters TEXT NOT NULL,
|
||||
sitesearch_category_parameters TEXT NOT NULL,
|
||||
timezone VARCHAR( 50 ) NOT NULL,
|
||||
currency CHAR( 3 ) NOT NULL,
|
||||
excluded_ips TEXT NOT NULL,
|
||||
excluded_parameters TEXT NOT NULL,
|
||||
excluded_user_agents TEXT NOT NULL,
|
||||
`group` VARCHAR(250) NOT NULL,
|
||||
`type` VARCHAR(255) NOT NULL,
|
||||
keep_url_fragment TINYINT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(idsite)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'site_url' => "CREATE TABLE {$prefixTables}site_url (
|
||||
idsite INTEGER(10) UNSIGNED NOT NULL,
|
||||
url VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY(idsite, url)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'goal' => " CREATE TABLE `{$prefixTables}goal` (
|
||||
`idsite` int(11) NOT NULL,
|
||||
`idgoal` int(11) NOT NULL,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`match_attribute` varchar(20) NOT NULL,
|
||||
`pattern` varchar(255) NOT NULL,
|
||||
`pattern_type` varchar(10) NOT NULL,
|
||||
`case_sensitive` tinyint(4) NOT NULL,
|
||||
`allow_multiple` tinyint(4) NOT NULL,
|
||||
`revenue` float NOT NULL,
|
||||
`deleted` tinyint(4) NOT NULL default '0',
|
||||
PRIMARY KEY (`idsite`,`idgoal`)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
|
||||
idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
tag VARCHAR(50) NULL,
|
||||
timestamp TIMESTAMP NULL,
|
||||
level VARCHAR(16) NULL,
|
||||
message TEXT NULL,
|
||||
PRIMARY KEY(idlogger_message)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
|
||||
'log_action' => "CREATE TABLE {$prefixTables}log_action (
|
||||
idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name TEXT,
|
||||
hash INTEGER(10) UNSIGNED NOT NULL,
|
||||
type TINYINT UNSIGNED NULL,
|
||||
url_prefix TINYINT(2) NULL,
|
||||
PRIMARY KEY(idaction),
|
||||
INDEX index_type_hash (type, hash)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
|
||||
idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
idsite INTEGER(10) UNSIGNED NOT NULL,
|
||||
idvisitor BINARY(8) NOT NULL,
|
||||
visitor_localtime TIME NOT NULL,
|
||||
visitor_returning TINYINT(1) NOT NULL,
|
||||
visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visitor_days_since_last SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visitor_days_since_first SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visit_first_action_time DATETIME NOT NULL,
|
||||
visit_last_action_time DATETIME NOT NULL,
|
||||
visit_exit_idaction_url INTEGER(11) UNSIGNED NULL DEFAULT 0,
|
||||
visit_exit_idaction_name INTEGER(11) UNSIGNED NOT NULL,
|
||||
visit_entry_idaction_url INTEGER(11) UNSIGNED NOT NULL,
|
||||
visit_entry_idaction_name INTEGER(11) UNSIGNED NOT NULL,
|
||||
visit_total_actions SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visit_total_searches SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visit_total_events SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visit_total_time SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visit_goal_converted TINYINT(1) NOT NULL,
|
||||
visit_goal_buyer TINYINT(1) NOT NULL,
|
||||
referer_type TINYINT(1) UNSIGNED NULL,
|
||||
referer_name VARCHAR(70) NULL,
|
||||
referer_url TEXT NOT NULL,
|
||||
referer_keyword VARCHAR(255) NULL,
|
||||
config_id BINARY(8) NOT NULL,
|
||||
config_os CHAR(3) NOT NULL,
|
||||
config_browser_name VARCHAR(10) NOT NULL,
|
||||
config_browser_version VARCHAR(20) NOT NULL,
|
||||
config_resolution VARCHAR(9) NOT NULL,
|
||||
config_pdf TINYINT(1) NOT NULL,
|
||||
config_flash TINYINT(1) NOT NULL,
|
||||
config_java TINYINT(1) NOT NULL,
|
||||
config_director TINYINT(1) NOT NULL,
|
||||
config_quicktime TINYINT(1) NOT NULL,
|
||||
config_realplayer TINYINT(1) NOT NULL,
|
||||
config_windowsmedia TINYINT(1) NOT NULL,
|
||||
config_gears TINYINT(1) NOT NULL,
|
||||
config_silverlight TINYINT(1) NOT NULL,
|
||||
config_cookie TINYINT(1) NOT NULL,
|
||||
location_ip VARBINARY(16) NOT NULL,
|
||||
location_browser_lang VARCHAR(20) NOT NULL,
|
||||
location_country CHAR(3) NOT NULL,
|
||||
location_region char(2) DEFAULT NULL,
|
||||
location_city varchar(255) DEFAULT NULL,
|
||||
location_latitude float(10, 6) DEFAULT NULL,
|
||||
location_longitude float(10, 6) DEFAULT NULL,
|
||||
PRIMARY KEY(idvisit),
|
||||
INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time),
|
||||
INDEX index_idsite_datetime (idsite, visit_last_action_time),
|
||||
INDEX index_idsite_idvisitor (idsite, idvisitor)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` (
|
||||
idsite int(10) UNSIGNED NOT NULL,
|
||||
idvisitor BINARY(8) NOT NULL,
|
||||
server_time DATETIME NOT NULL,
|
||||
idvisit INTEGER(10) UNSIGNED NOT NULL,
|
||||
idorder varchar(100) NOT NULL,
|
||||
|
||||
idaction_sku INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_name INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_category INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_category2 INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_category3 INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_category4 INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_category5 INTEGER(10) UNSIGNED NOT NULL,
|
||||
price FLOAT NOT NULL,
|
||||
quantity INTEGER(10) UNSIGNED NOT NULL,
|
||||
deleted TINYINT(1) UNSIGNED NOT NULL,
|
||||
|
||||
PRIMARY KEY(idvisit, idorder, idaction_sku),
|
||||
INDEX index_idsite_servertime ( idsite, server_time )
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
|
||||
idvisit int(10) unsigned NOT NULL,
|
||||
idsite int(10) unsigned NOT NULL,
|
||||
idvisitor BINARY(8) NOT NULL,
|
||||
server_time datetime NOT NULL,
|
||||
idaction_url int(11) default NULL,
|
||||
idlink_va int(11) default NULL,
|
||||
referer_visit_server_date date default NULL,
|
||||
referer_type int(10) unsigned default NULL,
|
||||
referer_name varchar(70) default NULL,
|
||||
referer_keyword varchar(255) default NULL,
|
||||
visitor_returning tinyint(1) NOT NULL,
|
||||
visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visitor_days_since_first SMALLINT(5) UNSIGNED NOT NULL,
|
||||
visitor_days_since_order SMALLINT(5) UNSIGNED NOT NULL,
|
||||
location_country char(3) NOT NULL,
|
||||
location_region char(2) DEFAULT NULL,
|
||||
location_city varchar(255) DEFAULT NULL,
|
||||
location_latitude float(10, 6) DEFAULT NULL,
|
||||
location_longitude float(10, 6) DEFAULT NULL,
|
||||
url text NOT NULL,
|
||||
idgoal int(10) NOT NULL,
|
||||
buster int unsigned NOT NULL,
|
||||
|
||||
idorder varchar(100) default NULL,
|
||||
items SMALLINT UNSIGNED DEFAULT NULL,
|
||||
revenue float default NULL,
|
||||
revenue_subtotal float default NULL,
|
||||
revenue_tax float default NULL,
|
||||
revenue_shipping float default NULL,
|
||||
revenue_discount float default NULL,
|
||||
|
||||
PRIMARY KEY (idvisit, idgoal, buster),
|
||||
UNIQUE KEY unique_idsite_idorder (idsite, idorder),
|
||||
INDEX index_idsite_datetime ( idsite, server_time )
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
|
||||
idlink_va INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
idsite int(10) UNSIGNED NOT NULL,
|
||||
idvisitor BINARY(8) NOT NULL,
|
||||
server_time DATETIME NOT NULL,
|
||||
idvisit INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_url INTEGER(10) UNSIGNED DEFAULT NULL,
|
||||
idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
|
||||
idaction_name INTEGER(10) UNSIGNED,
|
||||
idaction_name_ref INTEGER(10) UNSIGNED NOT NULL,
|
||||
idaction_event_category INTEGER(10) UNSIGNED DEFAULT NULL,
|
||||
idaction_event_action INTEGER(10) UNSIGNED DEFAULT NULL,
|
||||
time_spent_ref_action INTEGER(10) UNSIGNED NOT NULL,
|
||||
|
||||
custom_float FLOAT NULL DEFAULT NULL,
|
||||
PRIMARY KEY(idlink_va),
|
||||
INDEX index_idvisit(idvisit),
|
||||
INDEX index_idsite_servertime ( idsite, server_time )
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
|
||||
query TEXT NOT NULL,
|
||||
count INTEGER UNSIGNED NULL,
|
||||
sum_time_ms FLOAT NULL,
|
||||
UNIQUE KEY query(query(100))
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'option' => "CREATE TABLE `{$prefixTables}option` (
|
||||
option_name VARCHAR( 255 ) NOT NULL,
|
||||
option_value LONGTEXT NOT NULL,
|
||||
autoload TINYINT NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY ( option_name ),
|
||||
INDEX autoload( autoload )
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'session' => "CREATE TABLE {$prefixTables}session (
|
||||
id CHAR(32) NOT NULL,
|
||||
modified INTEGER,
|
||||
lifetime INTEGER,
|
||||
data TEXT,
|
||||
PRIMARY KEY ( id )
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
|
||||
idarchive INTEGER UNSIGNED NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
idsite INTEGER UNSIGNED NULL,
|
||||
date1 DATE NULL,
|
||||
date2 DATE NULL,
|
||||
period TINYINT UNSIGNED NULL,
|
||||
ts_archived DATETIME NULL,
|
||||
value DOUBLE NULL,
|
||||
PRIMARY KEY(idarchive, name),
|
||||
INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
|
||||
INDEX index_period_archived(period, ts_archived)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
|
||||
'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
|
||||
idarchive INTEGER UNSIGNED NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
idsite INTEGER UNSIGNED NULL,
|
||||
date1 DATE NULL,
|
||||
date2 DATE NULL,
|
||||
period TINYINT UNSIGNED NULL,
|
||||
ts_archived DATETIME NULL,
|
||||
value MEDIUMBLOB NULL,
|
||||
PRIMARY KEY(idarchive, name),
|
||||
INDEX index_period_archived(period, ts_archived)
|
||||
) ENGINE=$engine DEFAULT CHARSET=utf8
|
||||
",
|
||||
);
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL to create a specific Piwik table
|
||||
*
|
||||
* @param string $tableName
|
||||
* @throws Exception
|
||||
* @return string SQL
|
||||
*/
|
||||
public function getTableCreateSql($tableName)
|
||||
{
|
||||
$tables = DbHelper::getTablesCreateSql();
|
||||
|
||||
if (!isset($tables[$tableName])) {
|
||||
throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
|
||||
}
|
||||
|
||||
return $tables[$tableName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Names of all the prefixed tables in piwik
|
||||
* Doesn't use the DB
|
||||
*
|
||||
* @return array Table names
|
||||
*/
|
||||
public function getTablesNames()
|
||||
{
|
||||
$aTables = array_keys($this->getTablesCreateSql());
|
||||
$prefixTables = $this->getTablePrefix();
|
||||
$return = array();
|
||||
foreach ($aTables as $table) {
|
||||
$return[] = $prefixTables . $table;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
private $tablesInstalled = null;
|
||||
|
||||
/**
|
||||
* Get list of tables installed
|
||||
*
|
||||
* @param bool $forceReload Invalidate cache
|
||||
* @return array installed Tables
|
||||
*/
|
||||
public function getTablesInstalled($forceReload = true)
|
||||
{
|
||||
if (is_null($this->tablesInstalled)
|
||||
|| $forceReload === true
|
||||
) {
|
||||
$db = Db::get();
|
||||
$prefixTables = $this->getTablePrefix();
|
||||
|
||||
// '_' matches any character; force it to be literal
|
||||
$prefixTables = str_replace('_', '\_', $prefixTables);
|
||||
|
||||
$allTables = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
|
||||
|
||||
// all the tables to be installed
|
||||
$allMyTables = $this->getTablesNames();
|
||||
|
||||
// we get the intersection between all the tables in the DB and the tables to be installed
|
||||
$tablesInstalled = array_intersect($allMyTables, $allTables);
|
||||
|
||||
// at this point we have the static list of core tables, but let's add the monthly archive tables
|
||||
$allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'");
|
||||
$allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
|
||||
|
||||
$allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
|
||||
|
||||
$this->tablesInstalled = $allTablesReallyInstalled;
|
||||
}
|
||||
return $this->tablesInstalled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any table exists
|
||||
*
|
||||
* @return bool True if tables exist; false otherwise
|
||||
*/
|
||||
public function hasTables()
|
||||
{
|
||||
return count($this->getTablesInstalled()) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create database
|
||||
*
|
||||
* @param string $dbName Name of the database to create
|
||||
*/
|
||||
public function createDatabase($dbName = null)
|
||||
{
|
||||
if (is_null($dbName)) {
|
||||
$dbName = $this->getDbName();
|
||||
}
|
||||
Db::exec("CREATE DATABASE IF NOT EXISTS " . $dbName . " DEFAULT CHARACTER SET utf8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new table in the database.
|
||||
*
|
||||
* @param string $nameWithoutPrefix The name of the table without any piwik prefix.
|
||||
* @param string $createDefinition The table create definition, see the "MySQL CREATE TABLE" specification for
|
||||
* more information.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createTable($nameWithoutPrefix, $createDefinition)
|
||||
{
|
||||
$statement = sprintf("CREATE TABLE `%s` ( %s ) ENGINE=%s DEFAULT CHARSET=utf8 ;",
|
||||
Common::prefixTable($nameWithoutPrefix),
|
||||
$createDefinition,
|
||||
$this->getTableEngine());
|
||||
|
||||
try {
|
||||
Db::exec($statement);
|
||||
} catch (Exception $e) {
|
||||
// mysql code error 1050:table already exists
|
||||
// see bug #153 http://dev.piwik.org/trac/ticket/153
|
||||
if (!Db::get()->isErrNo($e, '1050')) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop database
|
||||
*/
|
||||
public function dropDatabase($dbName = null)
|
||||
{
|
||||
$dbName = $dbName ?: $this->getDbName();
|
||||
Db::exec("DROP DATABASE IF EXISTS " . $dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all tables
|
||||
*/
|
||||
public function createTables()
|
||||
{
|
||||
$db = Db::get();
|
||||
$prefixTables = $this->getTablePrefix();
|
||||
|
||||
$tablesAlreadyInstalled = $this->getTablesInstalled();
|
||||
$tablesToCreate = $this->getTablesCreateSql();
|
||||
unset($tablesToCreate['archive_blob']);
|
||||
unset($tablesToCreate['archive_numeric']);
|
||||
|
||||
foreach ($tablesToCreate as $tableName => $tableSql) {
|
||||
$tableName = $prefixTables . $tableName;
|
||||
if (!in_array($tableName, $tablesAlreadyInstalled)) {
|
||||
$db->query($tableSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entry in the User table for the "anonymous" user.
|
||||
*/
|
||||
public function createAnonymousUser()
|
||||
{
|
||||
// The anonymous user is the user that is assigned by default
|
||||
// note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin
|
||||
$db = Db::get();
|
||||
$db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . "
|
||||
VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );");
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate all tables
|
||||
*/
|
||||
public function truncateAllTables()
|
||||
{
|
||||
$tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true);
|
||||
foreach ($tablesAlreadyInstalled as $table) {
|
||||
Db::query("TRUNCATE `$table`");
|
||||
}
|
||||
}
|
||||
|
||||
private function getTablePrefix()
|
||||
{
|
||||
$dbInfos = Db::getDatabaseConfig();
|
||||
$prefixTables = $dbInfos['tables_prefix'];
|
||||
|
||||
return $prefixTables;
|
||||
}
|
||||
|
||||
private function getTableEngine()
|
||||
{
|
||||
$dbInfos = Db::getDatabaseConfig();
|
||||
$engine = $dbInfos['type'];
|
||||
return $engine;
|
||||
}
|
||||
|
||||
private function getDbName()
|
||||
{
|
||||
$dbInfos = Db::getDatabaseConfig();
|
||||
$dbName = $dbInfos['dbname'];
|
||||
|
||||
return $dbName;
|
||||
}
|
||||
}
|
||||
96
www/analytics/core/Db/SchemaInterface.php
Normal file
96
www/analytics/core/Db/SchemaInterface.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Db;
|
||||
|
||||
/**
|
||||
* Database schema interface
|
||||
*/
|
||||
interface SchemaInterface
|
||||
{
|
||||
/**
|
||||
* Is this schema available?
|
||||
*
|
||||
* @return bool True if schema is available; false otherwise
|
||||
*/
|
||||
static public function isAvailable();
|
||||
|
||||
/**
|
||||
* Get the SQL to create a specific Piwik table
|
||||
*
|
||||
* @param string $tableName
|
||||
* @return string SQL
|
||||
*/
|
||||
public function getTableCreateSql($tableName);
|
||||
|
||||
/**
|
||||
* Get the SQL to create Piwik tables
|
||||
*
|
||||
* @return array array of strings containing SQL
|
||||
*/
|
||||
public function getTablesCreateSql();
|
||||
|
||||
/**
|
||||
* Creates a new table in the database.
|
||||
*
|
||||
* @param string $nameWithoutPrefix The name of the table without any piwik prefix.
|
||||
* @param string $createDefinition The table create definition
|
||||
*/
|
||||
public function createTable($nameWithoutPrefix, $createDefinition);
|
||||
|
||||
/**
|
||||
* Create database
|
||||
*
|
||||
* @param string $dbName Name of the database to create
|
||||
*/
|
||||
public function createDatabase($dbName = null);
|
||||
|
||||
/**
|
||||
* Drop database
|
||||
*/
|
||||
public function dropDatabase();
|
||||
|
||||
/**
|
||||
* Create all tables
|
||||
*/
|
||||
public function createTables();
|
||||
|
||||
/**
|
||||
* Creates an entry in the User table for the "anonymous" user.
|
||||
*/
|
||||
public function createAnonymousUser();
|
||||
|
||||
/**
|
||||
* Truncate all tables
|
||||
*/
|
||||
public function truncateAllTables();
|
||||
|
||||
/**
|
||||
* Names of all the prefixed tables in piwik
|
||||
* Doesn't use the DB
|
||||
*
|
||||
* @return array Table names
|
||||
*/
|
||||
public function getTablesNames();
|
||||
|
||||
/**
|
||||
* Get list of tables installed
|
||||
*
|
||||
* @param bool $forceReload Invalidate cache
|
||||
* @return array installed Tables
|
||||
*/
|
||||
public function getTablesInstalled($forceReload = true);
|
||||
|
||||
/**
|
||||
* Checks whether any table exists
|
||||
*
|
||||
* @return bool True if tables exist; false otherwise
|
||||
*/
|
||||
public function hasTables();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue