2016-11-15 18:39:34 +00:00
|
|
|
<?php
|
|
|
|
namespace devilbox;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @requires devilbox::Logger
|
|
|
|
*/
|
2017-05-06 09:13:33 +00:00
|
|
|
class Pgsql extends _Base implements _iBase
|
2016-11-15 18:39:34 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
*
|
|
|
|
* Statics
|
|
|
|
*
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Postgres instance
|
|
|
|
* @var Postgres|null
|
|
|
|
*/
|
|
|
|
protected static $instance = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Singleton Instance getter.
|
|
|
|
*
|
|
|
|
* @param string $user Username
|
|
|
|
* @param string $pass Password
|
|
|
|
* @param string $host Host
|
|
|
|
* @return object|null
|
|
|
|
*/
|
2017-05-06 09:13:33 +00:00
|
|
|
public static function getInstance($host = null, $user = null, $pass = null)
|
2016-11-15 18:39:34 +00:00
|
|
|
{
|
|
|
|
if (!isset(static::$instance)) {
|
|
|
|
static::$instance = new static($user, $pass, $host);
|
|
|
|
}
|
|
|
|
// If current Postgres instance was unable to connect
|
|
|
|
if ((static::$instance->getConnectError())) {
|
2017-05-06 09:13:33 +00:00
|
|
|
//loadClass('Logger')->error('Instance has errors:' . "\r\n" . var_export(static::$instance, true) . "\r\n");
|
2016-11-15 18:39:34 +00:00
|
|
|
}
|
|
|
|
return static::$instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect to database
|
|
|
|
*
|
|
|
|
* @param string $err Reference to error message
|
|
|
|
* @param string $user Postgres username
|
|
|
|
* @param string $pass Postgres password
|
|
|
|
* @param string $host Postgres hostname
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2017-05-06 09:13:33 +00:00
|
|
|
public static function testConnection(&$err, $host, $user, $pass)
|
2016-11-15 18:39:34 +00:00
|
|
|
{
|
|
|
|
$err = false;
|
|
|
|
|
|
|
|
// Silence errors and try to connect
|
|
|
|
error_reporting(0);
|
2017-04-21 00:37:01 +00:00
|
|
|
$link = pg_connect('host='.$host.' user='.$user.' password='.$pass);
|
2016-11-15 18:39:34 +00:00
|
|
|
error_reporting(-1);
|
|
|
|
|
|
|
|
if (!$link || pg_connection_status($link) !== PGSQL_CONNECTION_OK) {
|
2017-05-06 09:13:33 +00:00
|
|
|
$err = 'Failed to connect';
|
2016-11-15 18:39:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pg_close($link);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
*
|
|
|
|
* Private Variables
|
|
|
|
*
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Postgres Resource
|
|
|
|
* @var resource|null
|
|
|
|
*/
|
|
|
|
private $_link = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
*
|
|
|
|
* Construct/Destructor
|
|
|
|
*
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use singleton getInstance() instead.
|
|
|
|
*
|
|
|
|
* @param string $user Username
|
|
|
|
* @param string $pass Password
|
|
|
|
* @param string $host Host
|
|
|
|
* @param string $database Database name
|
|
|
|
*/
|
|
|
|
public function __construct($user, $pass, $host, $database = null)
|
|
|
|
{
|
|
|
|
// Silence errors and try to connect
|
|
|
|
error_reporting(0);
|
|
|
|
if ($database !== null) {
|
2017-04-21 00:37:01 +00:00
|
|
|
$link = pg_connect('host='.$host.' dbname='.$database.' user='.$user.' password='.$pass);
|
2016-11-15 18:39:34 +00:00
|
|
|
} else {
|
2017-04-21 00:37:01 +00:00
|
|
|
$link = pg_connect('host='.$host.' user='.$user.' password='.$pass);
|
2016-11-15 18:39:34 +00:00
|
|
|
}
|
|
|
|
error_reporting(-1);
|
|
|
|
|
|
|
|
if (!$link || pg_connection_status($link) !== PGSQL_CONNECTION_OK) {
|
2017-05-06 09:13:33 +00:00
|
|
|
$this->setConnectError('Failed to connect to '.$user.'@'.$host);
|
|
|
|
$this->setConnectErrno(1);
|
|
|
|
//loadClass('Logger')->error($this->_connect_error);
|
2016-11-15 18:39:34 +00:00
|
|
|
} else {
|
|
|
|
$this->_link = $link;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*/
|
|
|
|
public function __destruct()
|
|
|
|
{
|
|
|
|
if ($this->_link) {
|
2017-05-06 09:13:33 +00:00
|
|
|
@pg_close($this->_link);
|
2016-11-15 18:39:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
*
|
|
|
|
* PostgreSQL Select functions
|
|
|
|
*
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query Database
|
|
|
|
*
|
|
|
|
* @param string $query Postgres Query
|
|
|
|
* @param function $callback Callback function
|
|
|
|
* @return mixed[]
|
|
|
|
*/
|
|
|
|
public function select($query, $callback = null)
|
|
|
|
{
|
|
|
|
if (!$this->_link) {
|
2017-04-19 17:48:44 +00:00
|
|
|
loadClass('Logger')->error('Postgres error, link is no resource in select()');
|
2016-11-15 18:39:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!($result = pg_query($this->_link, $query))) {
|
2017-05-06 09:13:33 +00:00
|
|
|
$this->setError('PostgreSQL - error on result: '.pg_result_error($result)."\n" . 'query:'."\n" . $query);
|
|
|
|
$this->setErrno(1);
|
|
|
|
loadClass('Logger')->error($this->getError());
|
2016-11-15 18:39:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$data = array();
|
|
|
|
|
|
|
|
if ($callback) {
|
|
|
|
while ($row = pg_fetch_assoc($result)) {
|
|
|
|
$callback($row, $data);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while ($row = pg_fetch_assoc($result)) {
|
|
|
|
$data[] = $row;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pg_free_result($result);
|
|
|
|
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all PostgreSQL Databases.
|
|
|
|
* @return mixed[] Array of databases
|
|
|
|
*/
|
|
|
|
public function getDatabases()
|
|
|
|
{
|
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data[$row['database']] = array(
|
|
|
|
'charset' => $row['charset'],
|
|
|
|
'collation' => $row['collation']
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
$sql = 'SELECT
|
|
|
|
S.datname AS database,
|
|
|
|
S.datcollate AS collation,
|
|
|
|
pg_encoding_to_char(S.encoding) AS charset
|
|
|
|
FROM
|
|
|
|
pg_database AS S
|
|
|
|
WHERE datistemplate = false;';
|
|
|
|
|
|
|
|
$databases = $this->select($sql, $callback);
|
|
|
|
$databases = $databases ? $databases : array();
|
|
|
|
|
|
|
|
// Get schemas for each database
|
|
|
|
foreach ($databases as $name => &$database) {
|
2017-05-06 09:13:33 +00:00
|
|
|
$PSQL = new Postgres('postgres', loadClass('Docker')->getEnv('PGSQL_ROOT_PASSWORD'), $GLOBALS['PGSQL_HOST_ADDR'], $name);
|
2016-11-15 18:39:34 +00:00
|
|
|
|
|
|
|
$sql = "SELECT n.nspname AS schemas FROM pg_catalog.pg_namespace AS n WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema';";
|
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data[$row['schemas']] = array();
|
|
|
|
};
|
|
|
|
$schemas = $PSQL->select($sql, $callback);
|
|
|
|
$databases[$name]['schemas'] = $schemas;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $databases;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get Schema size in Megabytes.
|
|
|
|
*
|
|
|
|
* @param string $database Database name.
|
|
|
|
* @param string $schema Schema name.
|
|
|
|
* @return integer
|
|
|
|
*/
|
|
|
|
public function getSchemaSize($database, $schema)
|
|
|
|
{
|
2017-05-06 09:13:33 +00:00
|
|
|
$PSQL = new Postgres('postgres', loadClass('Docker')->getEnv('PGSQL_ROOT_PASSWORD'), $GLOBALS['PGSQL_HOST_ADDR'], $database);
|
2016-11-15 18:39:34 +00:00
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data = $row['size'];
|
|
|
|
|
|
|
|
};
|
|
|
|
$sql = "SELECT
|
|
|
|
ROUND(sum(table_size) / 1048576, 2) AS size
|
|
|
|
FROM (
|
|
|
|
SELECT pg_catalog.pg_namespace.nspname AS schema_name,
|
|
|
|
pg_relation_size(pg_catalog.pg_class.oid) AS table_size
|
|
|
|
FROM pg_catalog.pg_class
|
|
|
|
JOIN pg_catalog.pg_namespace ON relnamespace = pg_catalog.pg_namespace.oid
|
|
|
|
WHERE pg_catalog.pg_namespace.nspname = '".$schema."'
|
|
|
|
) t
|
|
|
|
GROUP BY schema_name;";
|
|
|
|
|
|
|
|
$size = $PSQL->select($sql, $callback);
|
|
|
|
return $size ? $size : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get Number of Tables per Schema
|
|
|
|
*
|
|
|
|
* @param string $database Database name.
|
|
|
|
* @param string $schema Schema name.
|
|
|
|
* @return integer
|
|
|
|
*/
|
|
|
|
public function getTableCount($database, $schema)
|
|
|
|
{
|
2017-05-06 09:13:33 +00:00
|
|
|
$PSQL = new Postgres('postgres', loadClass('Docker')->getEnv('PGSQL_ROOT_PASSWORD'), $GLOBALS['PGSQL_HOST_ADDR'], $database);
|
2016-11-15 18:39:34 +00:00
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data = $row['count'];
|
|
|
|
};
|
|
|
|
|
|
|
|
$sql = "SELECT
|
|
|
|
COUNT(*) AS count
|
|
|
|
FROM
|
|
|
|
information_schema.tables
|
|
|
|
WHERE
|
|
|
|
table_schema = '".$schema."'
|
|
|
|
AND
|
|
|
|
table_type = 'BASE TABLE';
|
|
|
|
";
|
|
|
|
|
|
|
|
$count = $PSQL->select($sql, $callback);
|
|
|
|
return $count ? $count : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*********************************************************************************
|
|
|
|
*
|
2017-05-06 09:13:33 +00:00
|
|
|
* Interface required functions
|
2016-11-15 18:39:34 +00:00
|
|
|
*
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
/**
|
2017-05-06 09:13:33 +00:00
|
|
|
* Get PgSQL Name.
|
2016-11-15 18:39:34 +00:00
|
|
|
*
|
2017-05-06 09:13:33 +00:00
|
|
|
* @return string PgSQL short name.
|
2016-11-15 18:39:34 +00:00
|
|
|
*/
|
2017-05-06 09:13:33 +00:00
|
|
|
public function getName($default = 'PostgreSQL')
|
2016-11-15 18:39:34 +00:00
|
|
|
{
|
2017-05-06 09:13:33 +00:00
|
|
|
if (!static::isAvailable('pgsql')) {
|
|
|
|
return $default;
|
|
|
|
}
|
2016-11-15 18:39:34 +00:00
|
|
|
|
2017-05-06 09:13:33 +00:00
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data = $row['version'];
|
|
|
|
};
|
2016-11-15 18:39:34 +00:00
|
|
|
|
2017-05-06 09:13:33 +00:00
|
|
|
$name = $this->egrep('/[a-zA-Z0-9]*/', $this->select('SELECT version();', $callback));
|
|
|
|
|
|
|
|
if (!$name) {
|
|
|
|
loadClass('Logger')->error('Could not get PgSQL Name');
|
|
|
|
return $default;
|
|
|
|
}
|
|
|
|
return $name;
|
2016-11-15 18:39:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-05-06 09:13:33 +00:00
|
|
|
* Get PgSQL Version.
|
2016-11-15 18:39:34 +00:00
|
|
|
*
|
2017-05-06 09:13:33 +00:00
|
|
|
* @return string PgSQL version.
|
2016-11-15 18:39:34 +00:00
|
|
|
*/
|
2017-05-06 09:13:33 +00:00
|
|
|
public function getVersion()
|
2016-11-15 18:39:34 +00:00
|
|
|
{
|
2017-05-06 09:13:33 +00:00
|
|
|
if (!static::isAvailable('pgsql')) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
$callback = function ($row, &$data) {
|
|
|
|
$data = $row['version'];
|
|
|
|
};
|
|
|
|
|
|
|
|
$version = $this->egrep('/[.0-9]+/', $this->select('SELECT version();', $callback));
|
|
|
|
|
|
|
|
if (!$version) {
|
|
|
|
loadClass('Logger')->error('Could not get PgSQL version');
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
return $version;
|
2016-11-15 18:39:34 +00:00
|
|
|
}
|
|
|
|
}
|