Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/joomla4/ |
| [Home] [System Details] [Kill Me] |
home/lmsyaran/public_html/libraries/fof/database/query/postgresql.php000064400000033457151156412760022223
0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.3
*/
class FOFDatabaseQueryPostgresql extends FOFDatabaseQuery implements
FOFDatabaseQueryLimitable
{
/**
* @var object The FOR UPDATE element used in "FOR UPDATE"
lock
* @since 11.3
*/
protected $forUpdate = null;
/**
* @var object The FOR SHARE element used in "FOR SHARE"
lock
* @since 11.3
*/
protected $forShare = null;
/**
* @var object The NOWAIT element used in "FOR SHARE" and
"FOR UPDATE" lock
* @since 11.3
*/
protected $noWait = null;
/**
* @var object The LIMIT element
* @since 11.3
*/
protected $limit = null;
/**
* @var object The OFFSET element
* @since 11.3
*/
protected $offset = null;
/**
* @var object The RETURNING element of INSERT INTO
* @since 11.3
*/
protected $returning = null;
/**
* Magic function to convert the query to a string, only for postgresql
specific query
*
* @return string The completed query.
*
* @since 11.3
*/
public function __toString()
{
$query = '';
switch ($this->type)
{
case 'select':
$query .= (string) $this->select;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->group)
{
$query .= (string) $this->group;
}
if ($this->having)
{
$query .= (string) $this->having;
}
if ($this->order)
{
$query .= (string) $this->order;
}
if ($this->forUpdate)
{
$query .= (string) $this->forUpdate;
}
else
{
if ($this->forShare)
{
$query .= (string) $this->forShare;
}
}
if ($this->noWait)
{
$query .= (string) $this->noWait;
}
break;
case 'update':
$query .= (string) $this->update;
$query .= (string) $this->set;
if ($this->join)
{
$onWord = ' ON ';
// Workaround for special case of JOIN with UPDATE
foreach ($this->join as $join)
{
$joinElem = $join->getElements();
$joinArray = explode($onWord, $joinElem[0]);
$this->from($joinArray[0]);
$this->where($joinArray[1]);
}
$query .= (string) $this->from;
}
if ($this->where)
{
$query .= (string) $this->where;
}
break;
case 'insert':
$query .= (string) $this->insert;
if ($this->values)
{
if ($this->columns)
{
$query .= (string) $this->columns;
}
$elements = $this->values->getElements();
if (!($elements[0] instanceof $this))
{
$query .= ' VALUES ';
}
$query .= (string) $this->values;
if ($this->returning)
{
$query .= (string) $this->returning;
}
}
break;
default:
$query = parent::__toString();
break;
}
if ($this instanceof FOFDatabaseQueryLimitable)
{
$query = $this->processLimit($query, $this->limit,
$this->offset);
}
return $query;
}
/**
* Clear data from the query or a specific clause of the query.
*
* @param string $clause Optionally, the name of the clause to clear,
or nothing to clear the whole query.
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function clear($clause = null)
{
switch ($clause)
{
case 'limit':
$this->limit = null;
break;
case 'offset':
$this->offset = null;
break;
case 'forUpdate':
$this->forUpdate = null;
break;
case 'forShare':
$this->forShare = null;
break;
case 'noWait':
$this->noWait = null;
break;
case 'returning':
$this->returning = null;
break;
case 'select':
case 'update':
case 'delete':
case 'insert':
case 'from':
case 'join':
case 'set':
case 'where':
case 'group':
case 'having':
case 'order':
case 'columns':
case 'values':
parent::clear($clause);
break;
default:
$this->type = null;
$this->limit = null;
$this->offset = null;
$this->forUpdate = null;
$this->forShare = null;
$this->noWait = null;
$this->returning = null;
parent::clear($clause);
break;
}
return $this;
}
/**
* Casts a value to a char.
*
* Ensure that the value is properly quoted before passing to the method.
*
* Usage:
* $query->select($query->castAsChar('a'));
*
* @param string $value The value to cast as a char.
*
* @return string Returns the cast value.
*
* @since 11.3
*/
public function castAsChar($value)
{
return $value . '::text';
}
/**
* Concatenates an array of column names or values.
*
* Usage:
* $query->select($query->concatenate(array('a',
'b')));
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.3
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
return implode(' || ' . $this->quote($separator) . '
|| ', $values);
}
else
{
return implode(' || ', $values);
}
}
/**
* Gets the current date and time.
*
* @return string Return string used in query to obtain
*
* @since 11.3
*/
public function currentTimestamp()
{
return 'NOW()';
}
/**
* Sets the FOR UPDATE lock on select's output row
*
* @param string $table_name The table to lock
* @param string $glue The glue by which to join the conditions.
Defaults to ',' .
*
* @return FOFDatabaseQueryPostgresql FOR UPDATE query element
*
* @since 11.3
*/
public function forUpdate($table_name, $glue = ',')
{
$this->type = 'forUpdate';
if (is_null($this->forUpdate))
{
$glue = strtoupper($glue);
$this->forUpdate = new FOFDatabaseQueryElement('FOR
UPDATE', 'OF ' . $table_name, "$glue ");
}
else
{
$this->forUpdate->append($table_name);
}
return $this;
}
/**
* Sets the FOR SHARE lock on select's output row
*
* @param string $table_name The table to lock
* @param string $glue The glue by which to join the conditions.
Defaults to ',' .
*
* @return FOFDatabaseQueryPostgresql FOR SHARE query element
*
* @since 11.3
*/
public function forShare($table_name, $glue = ',')
{
$this->type = 'forShare';
if (is_null($this->forShare))
{
$glue = strtoupper($glue);
$this->forShare = new FOFDatabaseQueryElement('FOR SHARE',
'OF ' . $table_name, "$glue ");
}
else
{
$this->forShare->append($table_name);
}
return $this;
}
/**
* Used to get a string to extract year from date column.
*
* Usage:
*
$query->select($query->year($query->quoteName('dateColumn')));
*
* @param string $date Date column containing year to be extracted.
*
* @return string Returns string to extract year from a date.
*
* @since 12.1
*/
public function year($date)
{
return 'EXTRACT (YEAR FROM ' . $date . ')';
}
/**
* Used to get a string to extract month from date column.
*
* Usage:
*
$query->select($query->month($query->quoteName('dateColumn')));
*
* @param string $date Date column containing month to be extracted.
*
* @return string Returns string to extract month from a date.
*
* @since 12.1
*/
public function month($date)
{
return 'EXTRACT (MONTH FROM ' . $date . ')';
}
/**
* Used to get a string to extract day from date column.
*
* Usage:
*
$query->select($query->day($query->quoteName('dateColumn')));
*
* @param string $date Date column containing day to be extracted.
*
* @return string Returns string to extract day from a date.
*
* @since 12.1
*/
public function day($date)
{
return 'EXTRACT (DAY FROM ' . $date . ')';
}
/**
* Used to get a string to extract hour from date column.
*
* Usage:
*
$query->select($query->hour($query->quoteName('dateColumn')));
*
* @param string $date Date column containing hour to be extracted.
*
* @return string Returns string to extract hour from a date.
*
* @since 12.1
*/
public function hour($date)
{
return 'EXTRACT (HOUR FROM ' . $date . ')';
}
/**
* Used to get a string to extract minute from date column.
*
* Usage:
*
$query->select($query->minute($query->quoteName('dateColumn')));
*
* @param string $date Date column containing minute to be extracted.
*
* @return string Returns string to extract minute from a date.
*
* @since 12.1
*/
public function minute($date)
{
return 'EXTRACT (MINUTE FROM ' . $date . ')';
}
/**
* Used to get a string to extract seconds from date column.
*
* Usage:
*
$query->select($query->second($query->quoteName('dateColumn')));
*
* @param string $date Date column containing second to be extracted.
*
* @return string Returns string to extract second from a date.
*
* @since 12.1
*/
public function second($date)
{
return 'EXTRACT (SECOND FROM ' . $date . ')';
}
/**
* Sets the NOWAIT lock on select's output row
*
* @return FOFDatabaseQueryPostgresql NO WAIT query element
*
* @since 11.3
*/
public function noWait ()
{
$this->type = 'noWait';
if (is_null($this->noWait))
{
$this->noWait = new FOFDatabaseQueryElement('NOWAIT',
null);
}
return $this;
}
/**
* Set the LIMIT clause to the query
*
* @param integer $limit An int of how many row will be returned
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function limit($limit = 0)
{
if (is_null($this->limit))
{
$this->limit = new FOFDatabaseQueryElement('LIMIT', (int)
$limit);
}
return $this;
}
/**
* Set the OFFSET clause to the query
*
* @param integer $offset An int for skipping row
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function offset($offset = 0)
{
if (is_null($this->offset))
{
$this->offset = new FOFDatabaseQueryElement('OFFSET', (int)
$offset);
}
return $this;
}
/**
* Add the RETURNING element to INSERT INTO statement.
*
* @param mixed $pkCol The name of the primary key column.
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function returning($pkCol)
{
if (is_null($this->returning))
{
$this->returning = new FOFDatabaseQueryElement('RETURNING',
$pkCol);
}
return $this;
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
if ($limit > 0)
{
$query .= ' LIMIT ' . $limit;
}
if ($offset > 0)
{
$query .= ' OFFSET ' . $offset;
}
return $query;
}
/**
* Add to the current date and time in Postgresql.
* Usage:
* $query->select($query->dateAdd());
* Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
*
* @param datetime $date The date to add to
* @param string $interval The string representation of the
appropriate number of units
* @param string $datePart The part of the date to perform the
addition on
*
* @return string The string with the appropriate sql for addition of
dates
*
* @since 13.1
* @note Not all drivers support all units. Check appropriate
references
* @link
http://www.postgresql.org/docs/9.0/static/functions-datetime.html.
*/
public function dateAdd($date, $interval, $datePart)
{
if (substr($interval, 0, 1) != '-')
{
return "timestamp '" . $date . "' + interval
'" . $interval . " " . $datePart . "'";
}
else
{
return "timestamp '" . $date . "' - interval
'" . ltrim($interval, '-') . " " . $datePart
. "'";
}
}
/**
* Return correct regexp operator for Postgresql.
*
* Ensure that the regexp operator is Postgresql compatible.
*
* Usage:
* $query->where('field ' . $query->regexp($search));
*
* @param string $value The regex pattern.
*
* @return string Returns the regex operator.
*
* @since 11.3
*/
public function regexp($value)
{
return ' ~* ' . $value;
}
/**
* Return correct rand() function for Postgresql.
*
* Ensure that the rand() function is Postgresql compatible.
*
* Usage:
* $query->Rand();
*
* @return string The correct rand function.
*
* @since 3.5
*/
public function Rand()
{
return ' RANDOM() ';
}
}
home/lmsyaran/public_html/libraries/fof/database/driver/postgresql.php000064400000112307151156413130022331
0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* PostgreSQL database driver
*
* @since 12.1
*/
class FOFDatabaseDriverPostgresql extends FOFDatabaseDriver
{
/**
* The database driver name
*
* @var string
* @since 12.1
*/
public $name = 'postgresql';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'postgresql';
/**
* Quote for named objects
*
* @var string
* @since 12.1
*/
protected $nameQuote = '"';
/**
* The null/zero date string
*
* @var string
* @since 12.1
*/
protected $nullDate = '1970-01-01 00:00:00';
/**
* The minimum supported database version.
*
* @var string
* @since 12.1
*/
protected static $dbMinimum = '8.3.18';
/**
* Operator used for concatenation
*
* @var string
* @since 12.1
*/
protected $concat_operator = '||';
/**
* FOFDatabaseDriverPostgresqlQuery object returned by getQuery
*
* @var FOFDatabaseDriverPostgresqlQuery
* @since 12.1
*/
protected $queryObject = null;
/**
* Database object constructor
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct( $options )
{
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ?
$options['user'] : '';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
// Finalize initialization
parent::__construct($options);
}
/**
* Database object destructor
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Make sure the postgresql extension for PHP is installed and enabled.
if (!function_exists('pg_connect'))
{
throw new RuntimeException('PHP extension pg_connect is not
available.');
}
// Build the DSN for the connection.
$dsn = '';
if (!empty($this->options['host']))
{
$dsn .= "host={$this->options['host']} ";
}
$dsn .= "dbname={$this->options['database']}
user={$this->options['user']}
password={$this->options['password']}";
// Attempt to connect to the server.
if (!($this->connection = @pg_connect($dsn)))
{
throw new RuntimeException('Error connecting to PGSQL
database.');
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT);
pg_query('SET standard_conforming_strings=off');
pg_query('SET escape_string_warning=off');
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if (is_resource($this->connection))
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
pg_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = pg_escape_string($this->connection, $text);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the PostgreSQL connector is available
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function test()
{
return (function_exists('pg_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean
*
* @since 12.1
*/
public function connected()
{
$this->connect();
if (is_resource($this->connection))
{
return pg_ping($this->connection);
}
return false;
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return boolean
*
* @since 12.1
* @throws RuntimeException
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $this->quoteName($tableName));
$this->execute();
return true;
}
/**
* Get the number of affected rows by the last INSERT, UPDATE, REPLACE or
DELETE for the previous executed SQL statement.
*
* @return integer The number of affected rows in the previous operation
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return pg_affected_rows($this->cursor);
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 12.1
* @throws RuntimeException
*/
public function getCollation()
{
$this->connect();
$this->setQuery('SHOW LC_COLLATE');
$array = $this->loadAssocList();
return $array[0]['lc_collate'];
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
return pg_client_encoding($this->connection);
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
* This command is only valid for statements like SELECT or SHOW that
return an actual result set.
* To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or
DELETE query, use getAffectedRows().
*
* @param resource $cur An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cur = null)
{
$this->connect();
return pg_num_rows((int) $cur ? $cur : $this->cursor);
}
/**
* Get the current or query, or new FOFDatabaseQuery object.
*
* @param boolean $new False to return the last query set, True to
return a new FOFDatabaseQuery object.
* @param boolean $asObj False to return last query as string, true to
get FOFDatabaseQueryPostgresql object.
*
* @return FOFDatabaseQuery The current query object or a new object
extending the FOFDatabaseQuery class.
*
* @since 12.1
* @throws RuntimeException
*/
public function getQuery($new = false, $asObj = false)
{
if ($new)
{
// Make sure we have a query class for this driver.
if (!class_exists('FOFDatabaseQueryPostgresql'))
{
throw new RuntimeException('FOFDatabaseQueryPostgresql Class not
found.');
}
$this->queryObject = new FOFDatabaseQueryPostgresql($this);
return $this->queryObject;
}
else
{
if ($asObj)
{
return $this->queryObject;
}
else
{
return $this->sql;
}
}
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* This is unsupported by PostgreSQL.
*
* @param mixed $tables A table name or a list of table names.
*
* @return string An empty char because this function is not supported
by PostgreSQL.
*
* @since 12.1
*/
public function getTableCreate($tables)
{
return '';
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$result = array();
$tableSub = $this->replacePrefix($table);
$this->setQuery('
SELECT a.attname AS "column_name",
pg_catalog.format_type(a.atttypid, a.atttypmod) as "type",
CASE WHEN a.attnotnull IS TRUE
THEN \'NO\'
ELSE \'YES\'
END AS "null",
CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT
NULL
THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true)
END as "Default",
CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL
THEN \'\'
ELSE pg_catalog.col_description(a.attrelid, a.attnum)
END AS "comments"
FROM pg_catalog.pg_attribute a
LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND
a.attnum=adef.adnum
LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
WHERE a.attrelid =
(SELECT oid FROM pg_catalog.pg_class WHERE relname=' .
$this->quote($tableSub) . '
AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
nspname = \'public\')
)
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum'
);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->column_name] = preg_replace("/[(0-9)]/",
'', $field->type);
}
}
else
{
foreach ($fields as $field)
{
if (stristr(strtolower($field->type), "character
varying"))
{
$field->Default = "";
}
if (stristr(strtolower($field->type), "text"))
{
$field->Default = "";
}
// Do some dirty translation to MySQL output.
// TODO: Come up with and implement a standard across databases.
$result[$field->column_name] = (object) array(
'column_name' => $field->column_name,
'type' => $field->type,
'null' => $field->null,
'Default' => $field->Default,
'comments' => '',
'Field' => $field->column_name,
'Type' => $field->type,
'Null' => $field->null,
// TODO: Improve query above to return primary key info as well
// 'Key' => ($field->PK == '1' ?
'PRI' : '')
);
}
}
/* Change Postgresql's NULL::* type with PHP's null one */
foreach ($fields as $field)
{
if (preg_match("/^NULL::*/", $field->Default))
{
$field->Default = null;
}
}
return $result;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
if (in_array($table, $tableList))
{
// Get the details columns information.
$this->setQuery('
SELECT indexname AS "idxName", indisprimary AS
"isPrimary", indisunique AS "isUnique",
CASE WHEN indisprimary = true THEN
( SELECT \'ALTER TABLE \' || tablename || \' ADD
\' || pg_catalog.pg_get_constraintdef(const.oid, true)
FROM pg_constraint AS const WHERE const.conname=
pgClassFirst.relname )
ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true)
END AS "Query"
FROM pg_indexes
LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname
LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid
WHERE tablename=' . $this->quote($table) . ' ORDER BY
indkey'
);
$keys = $this->loadObjectList();
return $keys;
}
return false;
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
$query = $this->getQuery(true)
->select('table_name')
->from('information_schema.tables')
->where('table_type=' . $this->quote('BASE
TABLE'))
->where('table_schema NOT IN (' .
$this->quote('pg_catalog') . ', ' .
$this->quote('information_schema') . ')')
->order('table_name ASC');
$this->setQuery($query);
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the details list of sequences for a table.
*
* @param string $table The name of the table.
*
* @return array An array of sequences specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableSequences($table)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
if (in_array($table, $tableList))
{
$name = array(
's.relname', 'n.nspname', 't.relname',
'a.attname', 'info.data_type',
'info.minimum_value', 'info.maximum_value',
'info.increment', 'info.cycle_option'
);
$as = array('sequence', 'schema', 'table',
'column', 'data_type', 'minimum_value',
'maximum_value', 'increment',
'cycle_option');
if (version_compare($this->getVersion(), '9.1.0') >= 0)
{
$name[] .= 'info.start_value';
$as[] .= 'start_value';
}
// Get the details columns information.
$query = $this->getQuery(true)
->select($this->quoteName($name, $as))
->from('pg_class AS s')
->join('LEFT', "pg_depend d ON d.objid=s.oid AND
d.classid='pg_class'::regclass AND
d.refclassid='pg_class'::regclass")
->join('LEFT', 'pg_class t ON t.oid=d.refobjid')
->join('LEFT', 'pg_namespace n ON
n.oid=t.relnamespace')
->join('LEFT', 'pg_attribute a ON a.attrelid=t.oid
AND a.attnum=d.refobjsubid')
->join('LEFT', 'information_schema.sequences AS info
ON info.sequence_name=s.relname')
->where("s.relkind='S' AND d.deptype='a'
AND t.relname=" . $this->quote($table));
$this->setQuery($query);
$seq = $this->loadObjectList();
return $seq;
}
return false;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$version = pg_version($this->connection);
return $version['server'];
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
* To be called after the INSERT statement, it's MANDATORY to have a
sequence on
* every primary key table.
*
* To get the auto incremented value it's possible to call this
function after
* INSERT INTO query, or use INSERT INTO with RETURNING clause.
*
* @example with insertid() call:
* $query = $this->getQuery(true)
* ->insert('jos_dbtest')
* ->columns('title,start_date,description')
* ->values("'testTitle2nd','1971-01-01','testDescription2nd'");
* $this->setQuery($query);
* $this->execute();
* $id = $this->insertid();
*
* @example with RETURNING clause:
* $query = $this->getQuery(true)
* ->insert('jos_dbtest')
* ->columns('title,start_date,description')
* ->values("'testTitle2nd','1971-01-01','testDescription2nd'")
* ->returning('id');
* $this->setQuery($query);
* $id = $this->loadResult();
*
* @return integer The value of the auto-increment field from the last
inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
$insertQuery = $this->getQuery(false, true);
$table = $insertQuery->__get('insert')->getElements();
/* find sequence column name */
$colNameQuery = $this->getQuery(true);
$colNameQuery->select('column_default')
->from('information_schema.columns')
->where("table_name=" .
$this->quote($this->replacePrefix(str_replace('"',
'', $table[0]))), 'AND')
->where("column_default LIKE '%nextval%'");
$this->setQuery($colNameQuery);
$colName = $this->loadRow();
$changedColName = str_replace('nextval', 'currval',
$colName);
$insertidQuery = $this->getQuery(true);
$insertidQuery->select($changedColName);
$this->setQuery($insertidQuery);
$insertVal = $this->loadRow();
return $insertVal[0];
}
/**
* Locks a table in the database.
*
* @param string $tableName The name of the table to unlock.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($tableName)
{
$this->transactionStart();
$this->setQuery('LOCK TABLE ' .
$this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE
MODE')->execute();
return $this;
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_resource($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
$query .= ' LIMIT ' . $this->limit . ' OFFSET ' .
$this->offset;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
}
// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
$this->cursor = @pg_query($this->connection, $query);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Get the error number and message before we execute any more queries.
$errorNum = $this->getErrorNumber();
$errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, null, $e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Get the error number and message from before we tried to reconnect.
$this->errorNum = $errorNum;
$this->errorMsg = $errorMsg;
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg);
}
}
return $this->cursor;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by PostgreSQL.
* @param string $prefix Not used by PostgreSQL.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
// Origin Table does not exist
if (!in_array($oldTable, $tableList))
{
// Origin Table not found
throw new RuntimeException('Table not found in Postgresql
database.');
}
else
{
/* Rename indexes */
$this->setQuery(
'SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname=' . $this->quote($oldTable, true) .
'
AND pg_class.oid=pg_index.indrelid );'
);
$oldIndexes = $this->loadColumn();
foreach ($oldIndexes as $oldIndex)
{
$changedIdxName = str_replace($oldTable, $newTable, $oldIndex);
$this->setQuery('ALTER INDEX ' .
$this->escape($oldIndex) . ' RENAME TO ' .
$this->escape($changedIdxName));
$this->execute();
}
/* Rename sequence */
$this->setQuery(
'SELECT relname
FROM pg_class
WHERE relkind = \'S\'
AND relnamespace IN (
SELECT oid
FROM pg_namespace
WHERE nspname NOT LIKE \'pg_%\'
AND nspname != \'information_schema\'
)
AND relname LIKE \'%' . $oldTable . '%\' ;'
);
$oldSequences = $this->loadColumn();
foreach ($oldSequences as $oldSequence)
{
$changedSequenceName = str_replace($oldTable, $newTable, $oldSequence);
$this->setQuery('ALTER SEQUENCE ' .
$this->escape($oldSequence) . ' RENAME TO ' .
$this->escape($changedSequenceName));
$this->execute();
}
/* Rename table */
$this->setQuery('ALTER TABLE ' .
$this->escape($oldTable) . ' RENAME TO ' .
$this->escape($newTable));
$this->execute();
}
return true;
}
/**
* Selects the database, but redundant for PostgreSQL
*
* @param string $database Database name to select.
*
* @return boolean Always true
*
* @since 12.1
*/
public function select($database)
{
return true;
}
/**
* Custom settings for UTF support
*
* @return integer Zero on success, -1 on failure
*
* @since 12.1
*/
public function setUtf()
{
$this->connect();
return pg_set_client_encoding($this->connection, 'UTF8');
}
/**
* This function return a field value as a prepared string to be used in a
SQL statement.
*
* @param array $columns Array of table's column returned by
::getTableColumns.
* @param string $field_name The table field's name.
* @param string $field_value The variable value to quote and return.
*
* @return string The quoted string.
*
* @since 12.1
*/
public function sqlValue($columns, $field_name, $field_value)
{
switch ($columns[$field_name])
{
case 'boolean':
$val = 'NULL';
if ($field_value == 't')
{
$val = 'TRUE';
}
elseif ($field_value == 'f')
{
$val = 'FALSE';
}
break;
case 'bigint':
case 'bigserial':
case 'integer':
case 'money':
case 'numeric':
case 'real':
case 'smallint':
case 'serial':
case 'numeric,':
$val = strlen($field_value) == 0 ? 'NULL' : $field_value;
break;
case 'date':
case 'timestamp without time zone':
if (empty($field_value))
{
$field_value = $this->getNullDate();
}
$val = $this->quote($field_value);
break;
default:
$val = $this->quote($field_value);
break;
}
return $val;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('COMMIT')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('ROLLBACK')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
$this->setQuery('RELEASE SAVEPOINT ' .
$this->quoteName($savepoint))->execute();
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
if ($this->setQuery('START TRANSACTION')->execute())
{
$this->transactionDepth = 1;
}
return;
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return pg_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return pg_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
return pg_fetch_object(is_null($cursor) ? $this->cursor : $cursor,
null, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
pg_free_result($cursor ? $cursor : $this->cursor);
}
/**
* Inserts a row into a table based on an object's properties.
*
* @param string $table The name of the database table to insert
into.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param string $key The name of the primary key. If provided the
object property is updated.
*
* @return boolean True on success.
*
* @since 12.1
* @throws RuntimeException
*/
public function insertObject($table, &$object, $key = null)
{
$columns = $this->getTableColumns($table);
$fields = array();
$values = array();
// Iterate over the object variables to build the query fields and
values.
foreach (get_object_vars($object) as $k => $v)
{
// Only process non-null scalars.
if (is_array($v) or is_object($v) or $v === null)
{
continue;
}
// Ignore any internal fields or primary keys with value 0.
if (($k[0] == "_") || ($k == $key && (($v === 0) ||
($v === '0'))))
{
continue;
}
// Prepare and sanitize the fields and values for the database query.
$fields[] = $this->quoteName($k);
$values[] = $this->sqlValue($columns, $k, $v);
}
// Create the base insert statement.
$query = $this->getQuery(true)
->insert($this->quoteName($table))
->columns($fields)
->values(implode(',', $values));
$retVal = false;
if ($key)
{
$query->returning($key);
// Set the query and execute the insert.
$this->setQuery($query);
$id = $this->loadResult();
if ($id)
{
$object->$key = $id;
$retVal = true;
}
}
else
{
// Set the query and execute the insert.
$this->setQuery($query);
if ($this->execute())
{
$retVal = true;
}
}
return $retVal;
}
/**
* Test to see if the PostgreSQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('pg_connect'));
}
/**
* Returns an array containing database's table list.
*
* @return array The database's table list.
*
* @since 12.1
*/
public function showTables()
{
$this->connect();
$query = $this->getQuery(true)
->select('table_name')
->from('information_schema.tables')
->where('table_type = ' . $this->quote('BASE
TABLE'))
->where('table_schema NOT IN (' .
$this->quote('pg_catalog') . ', ' .
$this->quote('information_schema') . ' )');
$this->setQuery($query);
$tableList = $this->loadColumn();
return $tableList;
}
/**
* Get the substring position inside a string
*
* @param string $substring The string being sought
* @param string $string The string/column being searched
*
* @return integer The position of $substring in $string
*
* @since 12.1
*/
public function getStringPositionSql( $substring, $string )
{
$this->connect();
$query = "SELECT POSITION( $substring IN $string )";
$this->setQuery($query);
$position = $this->loadRow();
return $position['position'];
}
/**
* Generate a random value
*
* @return float The random generated number
*
* @since 12.1
*/
public function getRandom()
{
$this->connect();
$this->setQuery('SELECT RANDOM()');
$random = $this->loadAssoc();
return $random['random'];
}
/**
* Get the query string to alter the database character set.
*
* @param string $dbName The database name
*
* @return string The query that alter the database query string
*
* @since 12.1
*/
public function getAlterDbCharacterSet( $dbName )
{
$query = 'ALTER DATABASE ' . $this->quoteName($dbName) .
' SET CLIENT_ENCODING TO ' . $this->quote('UTF8');
return $query;
}
/**
* Get the query string to create new Database in correct PostgreSQL
syntax.
*
* @param object $options object coming from "initialise"
function to pass user and database name to database driver.
* @param boolean $utf True if the database supports the UTF-8
character set, not used in PostgreSQL "CREATE DATABASE" query.
*
* @return string The query that creates database, owned by
$options['user']
*
* @since 12.1
*/
public function getCreateDbQuery($options, $utf)
{
$query = 'CREATE DATABASE ' .
$this->quoteName($options->db_name) . ' OWNER ' .
$this->quoteName($options->db_user);
if ($utf)
{
$query .= ' ENCODING ' . $this->quote('UTF-8');
}
return $query;
}
/**
* This function replaces a string identifier
<var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $query The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 12.1
*/
public function replacePrefix($query, $prefix = '#__')
{
$query = trim($query);
if (strpos($query, '\''))
{
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'currval'))
{
$query = explode('currval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('currval', $query);
}
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'nextval'))
{
$query = explode('nextval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('nextval', $query);
}
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'setval'))
{
$query = explode('setval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('setval', $query);
}
$explodedQuery = explode('\'', $query);
for ($nIndex = 0; $nIndex < count($explodedQuery); $nIndex = $nIndex
+ 2)
{
if (strpos($explodedQuery[$nIndex], $prefix))
{
$explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix,
$explodedQuery[$nIndex]);
}
}
$replacedQuery = implode('\'', $explodedQuery);
}
else
{
$replacedQuery = str_replace($prefix, $this->tablePrefix, $query);
}
return $replacedQuery;
}
/**
* Method to release a savepoint.
*
* @param string $savepointName Savepoint's name to release
*
* @return void
*
* @since 12.1
*/
public function releaseTransactionSavepoint( $savepointName )
{
$this->connect();
$this->setQuery('RELEASE SAVEPOINT ' .
$this->quoteName($this->escape($savepointName)));
$this->execute();
}
/**
* Method to create a savepoint.
*
* @param string $savepointName Savepoint's name to create
*
* @return void
*
* @since 12.1
*/
public function transactionSavepoint( $savepointName )
{
$this->connect();
$this->setQuery('SAVEPOINT ' .
$this->quoteName($this->escape($savepointName)));
$this->execute();
}
/**
* Unlocks tables in the database, this command does not exist in
PostgreSQL,
* it is automatically done on commit or rollback.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->transactionCommit();
return $this;
}
/**
* Updates a row in a table based on an object's properties.
*
* @param string $table The name of the database table to update.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param array $key The name of the primary key.
* @param boolean $nulls True to update null fields or false to
ignore them.
*
* @return boolean True on success.
*
* @since 12.1
* @throws RuntimeException
*/
public function updateObject($table, &$object, $key, $nulls = false)
{
$columns = $this->getTableColumns($table);
$fields = array();
$where = array();
if (is_string($key))
{
$key = array($key);
}
if (is_object($key))
{
$key = (array) $key;
}
// Create the base update statement.
$statement = 'UPDATE ' . $this->quoteName($table) . '
SET %s WHERE %s';
// Iterate over the object variables to build the query fields/value
pairs.
foreach (get_object_vars($object) as $k => $v)
{
// Only process scalars that are not internal fields.
if (is_array($v) or is_object($v) or $k[0] == '_')
{
continue;
}
// Set the primary key to the WHERE clause instead of a field to update.
if (in_array($k, $key))
{
$key_val = $this->sqlValue($columns, $k, $v);
$where[] = $this->quoteName($k) . '=' . $key_val;
continue;
}
// Prepare and sanitize the fields and values for the database query.
if ($v === null)
{
// If the value is null and we want to update nulls then set it.
if ($nulls)
{
$val = 'NULL';
}
// If the value is null and we do not want to update nulls then ignore
this field.
else
{
continue;
}
}
// The field is not null so we prep it for update.
else
{
$val = $this->sqlValue($columns, $k, $v);
}
// Add the field to be updated.
$fields[] = $this->quoteName($k) . '=' . $val;
}
// We don't have any fields to update.
if (empty($fields))
{
return true;
}
// Set the query and execute the update.
$this->setQuery(sprintf($statement, implode(",", $fields),
implode(' AND ', $where)));
return $this->execute();
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE)
. ' ';
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
$errorMessage = (string) pg_last_error($this->connection);
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
$query = str_replace($this->tablePrefix, '#__',
$query);
}
return $errorMessage . "SQL=" . $query;
}
}