Spade

Mini Shell

Directory:~$ /home/lmsyaran/public_html/joomla4/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/public_html/joomla4/database.zip

PK��[��database.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Database connector class.
 *
 * @since       1.7.0
 * @deprecated  4.0
 */
abstract class JDatabase
{
	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 * @deprecated  4.0
	 */
	public function query()
	{
		JLog::add('JDatabase::query() is deprecated, use
JDatabaseDriver::execute() instead.', JLog::WARNING,
'deprecated');

		return $this->execute();
	}

	/**
	 * Get a list of available database connectors.  The list will only be
populated with connectors that both
	 * the class exists and the static test method returns true.  This gives
us the ability to have a multitude
	 * of connector classes that are self-aware as to whether or not they are
able to be used on a given system.
	 *
	 * @return  array  An array of available database connectors.
	 *
	 * @since   1.7.0
	 * @deprecated  4.0
	 */
	public static function getConnectors()
	{
		JLog::add('JDatabase::getConnectors() is deprecated, use
JDatabaseDriver::getConnectors() instead.', JLog::WARNING,
'deprecated');

		return JDatabaseDriver::getConnectors();
	}

	/**
	 * Gets the error message from the database connection.
	 *
	 * @param   boolean  $escaped  True to escape the message string for use
in JavaScript.
	 *
	 * @return  string  The error message for the most recent query.
	 *
	 * @deprecated  4.0
	 * @since   1.7.0
	 */
	public function getErrorMsg($escaped = false)
	{
		JLog::add('JDatabase::getErrorMsg() is deprecated, use exception
handling instead.', JLog::WARNING, 'deprecated');

		if ($escaped)
		{
			return addslashes($this->errorMsg);
		}
		else
		{
			return $this->errorMsg;
		}
	}

	/**
	 * Gets the error number from the database connection.
	 *
	 * @return      integer  The error number for the most recent query.
	 *
	 * @since       1.7.0
	 * @deprecated  4.0
	 */
	public function getErrorNum()
	{
		JLog::add('JDatabase::getErrorNum() is deprecated, use exception
handling instead.', JLog::WARNING, 'deprecated');

		return $this->errorNum;
	}

	/**
	 * Method to return a JDatabaseDriver instance based on the given options.
 There are three global options and then
	 * the rest are specific to the database driver.  The 'driver'
option defines which JDatabaseDriver class is
	 * used for the connection -- the default is 'mysqli'.  The
'database' option determines which database is to
	 * be used for the connection.  The 'select' option determines
whether the connector should automatically select
	 * the chosen database.
	 *
	 * Instances are unique to the given options and new objects are only
created when a unique options array is
	 * passed into the method.  This ensures that we don't end up with
unnecessary database connection resources.
	 *
	 * @param   array  $options  Parameters to be passed to the database
driver.
	 *
	 * @return  JDatabaseDriver  A database object.
	 *
	 * @since       1.7.0
	 * @deprecated  4.0
	 */
	public static function getInstance($options = array())
	{
		JLog::add('JDatabase::getInstance() is deprecated, use
JDatabaseDriver::getInstance() instead.', JLog::WARNING,
'deprecated');

		return JDatabaseDriver::getInstance($options);
	}

	/**
	 * Splits a string of multiple queries into an array of individual
queries.
	 *
	 * @param   string  $query  Input SQL string with which to split into
individual queries.
	 *
	 * @return  array  The queries from the input string separated into an
array.
	 *
	 * @since   1.7.0
	 * @deprecated  4.0
	 */
	public static function splitSql($query)
	{
		JLog::add('JDatabase::splitSql() is deprecated, use
JDatabaseDriver::splitSql() instead.', JLog::WARNING,
'deprecated');

		return JDatabaseDriver::splitSql($query);
	}

	/**
	 * Return the most recent error message for the database connector.
	 *
	 * @param   boolean  $showSQL  True to display the SQL statement sent to
the database as well as the error.
	 *
	 * @return  string  The error message for the most recent query.
	 *
	 * @since   1.7.0
	 * @deprecated  4.0
	 */
	public function stderr($showSQL = false)
	{
		JLog::add('JDatabase::stderr() is deprecated.', JLog::WARNING,
'deprecated');

		if ($this->errorNum != 0)
		{
			return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED',
$this->errorNum, $this->errorMsg)
			. ($showSQL ? "<br />SQL =
<pre>$this->sql</pre>" : '');
		}
		else
		{
			return JText::_('JLIB_DATABASE_FUNCTION_NOERROR');
		}
	}

	/**
	 * Test to see if the connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   1.7.0
	 * @deprecated  4.0 - Use JDatabaseDriver::isSupported() instead.
	 */
	public static function test()
	{
		JLog::add('JDatabase::test() is deprecated. Use
JDatabaseDriver::isSupported() instead.', JLog::WARNING,
'deprecated');

		return static::isSupported();
	}
}
PK��[�kp0�7�7driver/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL database driver
 *
 * @link        https://dev.mysql.com/doc/
 * @since       3.0.0
 * @deprecated  4.0  Use MySQLi or PDO MySQL instead
 */
class JDatabaseDriverMysql extends JDatabaseDriverMysqli
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'mysql';

	/**
	 * Constructor.
	 *
	 * @param   array  $options  Array of database options with keys: host,
user, password, database, select.
	 *
	 * @since   3.0.0
	 */
	public function __construct($options)
	{
		// PHP's `mysql` extension is not present in PHP 7, block
instantiation in this environment
		if (PHP_MAJOR_VERSION >= 7)
		{
			throw new JDatabaseExceptionUnsupported(
				'This driver is unsupported in PHP 7, please use the MySQLi or PDO
MySQL driver instead.'
			);
		}

		// Get some basic values from the 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'] : '';
		$options['select'] = (isset($options['select'])) ?
(bool) $options['select'] : true;

		// Finalize initialisation.
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		// Make sure the MySQL extension for PHP is installed and enabled.
		if (!self::isSupported())
		{
			throw new JDatabaseExceptionUnsupported('Make sure the MySQL
extension for PHP is installed and enabled.');
		}

		// Attempt to connect to the server.
		if (!($this->connection = @
mysql_connect($this->options['host'],
$this->options['user'],
$this->options['password'], true)))
		{
			throw new JDatabaseExceptionConnecting('Could not connect to MySQL
server.');
		}

		// Set sql_mode to non_strict mode
		mysql_query("SET @@SESSION.sql_mode = '';",
$this->connection);

		// If auto-select is enabled select the given database.
		if ($this->options['select'] &&
!empty($this->options['database']))
		{
			$this->select($this->options['database']);
		}

		// Pre-populate the UTF-8 Multibyte compatibility flag based on server
version
		$this->utf8mb4 = $this->serverClaimsUtf8mb4Support();

		// Set the character set (needed for MySQL 4.1.2+).
		$this->utf = $this->setUtf();

		// Disable query cache and turn profiling ON in debug mode.
		if ($this->debug)
		{
			mysql_query('SET query_cache_type = 0;',
$this->connection);

			if ($this->hasProfiling())
			{
				mysql_query('SET profiling_history_size = 100, profiling =
1;', $this->connection);
			}
		}
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		// Close the connection.
		if (is_resource($this->connection))
		{
			foreach ($this->disconnectHandlers as $h)
			{
				call_user_func_array($h, array( &$this));
			}

			mysql_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   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$this->connect();

		$result = mysql_real_escape_string($text, $this->getConnection());

		if ($extra)
		{
			$result = addcslashes($result, '%_');
		}

		return $result;
	}

	/**
	 * Test to see if the MySQL connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return PHP_MAJOR_VERSION < 7 &&
function_exists('mysql_connect');
	}

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return  boolean  True if connected to the database engine.
	 *
	 * @since   3.0.0
	 */
	public function connected()
	{
		if (is_resource($this->connection))
		{
			return @mysql_ping($this->connection);
		}

		return false;
	}

	/**
	 * 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.
	 *
	 * @since   3.0.0
	 */
	public function getAffectedRows()
	{
		$this->connect();

		return mysql_affected_rows($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 an INSERT, UPDATE, REPLACE
or DELETE query, use getAffectedRows().
	 *
	 * @param   resource  $cursor  An optional database cursor resource to
extract the row count from.
	 *
	 * @return  integer   The number of returned rows.
	 *
	 * @since   3.0.0
	 */
	public function getNumRows($cursor = null)
	{
		$this->connect();

		return mysql_num_rows($cursor ? $cursor : $this->cursor);
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.0.0
	 */
	public function getVersion()
	{
		$this->connect();

		return mysql_get_server_info($this->connection);
	}

	/**
	 * Method to get the auto-incremented value from the last INSERT
statement.
	 *
	 * @return  integer  The value of the auto-increment field from the last
inserted row.
	 *
	 * @since   3.0.0
	 */
	public function insertid()
	{
		$this->connect();

		return mysql_insert_id($this->connection);
	}

	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function execute()
	{
		$this->connect();

		// 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 JDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
		{
			$query .= ' LIMIT ' . $this->offset . ', ' .
$this->limit;
		}

		if (!is_resource($this->connection))
		{
			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
			throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
		}

		// 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;

			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 = @mysql_query($query, $this->connection);

		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.
			$this->errorNum = $this->getErrorNumber();
			$this->errorMsg = $this->getErrorMessage();

			// 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)
				{
					// Get the error number and message.
					$this->errorNum = $this->getErrorNumber();
					$this->errorMsg = $this->getErrorMessage();

					// Throw the normal query exception.
					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

					throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum, $e);
				}

				// Since we were able to reconnect, run the query again.
				return $this->execute();
			}
			// The server was not disconnected.
			else
			{
				// Throw the normal query exception.
				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

				throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
			}
		}

		return $this->cursor;
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		if (!$database)
		{
			return false;
		}

		if (!mysql_select_db($database, $this->connection))
		{
			throw new JDatabaseExceptionConnecting('Could not connect to MySQL
database.');
		}

		return true;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		// If UTF is not supported return false immediately
		if (!$this->utf)
		{
			return false;
		}

		// Make sure we're connected to the server
		$this->connect();

		// Which charset should I use, plain utf8 or multibyte utf8mb4?
		$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';

		$result = @mysql_set_charset($charset, $this->connection);

		/**
		 * If I could not set the utf8mb4 charset then the server doesn't
support utf8mb4 despite claiming otherwise.
		 * This happens on old MySQL server versions (less than 5.5.3) using the
mysqlnd PHP driver. Since mysqlnd
		 * masks the server version and reports only its own we can not be sure
if the server actually does support
		 * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the
utf8mb4 charset is undefined in this case we
		 * catch the error and determine that utf8mb4 is not supported!
		 */
		if (!$result && $this->utf8mb4)
		{
			$this->utf8mb4 = false;
			$result = @mysql_set_charset('utf8', $this->connection);
		}

		return $result;
	}

	/**
	 * 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   3.0.0
	 */
	protected function fetchArray($cursor = null)
	{
		return mysql_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   3.0.0
	 */
	protected function fetchAssoc($cursor = null)
	{
		return mysql_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   3.0.0
	 */
	protected function fetchObject($cursor = null, $class =
'stdClass')
	{
		return mysql_fetch_object($cursor ? $cursor : $this->cursor, $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   3.0.0
	 */
	protected function freeResult($cursor = null)
	{
		mysql_free_result($cursor ? $cursor : $this->cursor);
	}

	/**
	 * Internal function to check if profiling is available
	 *
	 * @return  boolean
	 *
	 * @since   3.1.3
	 */
	private function hasProfiling()
	{
		try
		{
			$res = mysql_query("SHOW VARIABLES LIKE
'have_profiling'", $this->connection);
			$row = mysql_fetch_assoc($res);

			return isset($row);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	/**
	 * Does the database server claim to have support for UTF-8 Multibyte
(utf8mb4) collation?
	 *
	 * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL
server). mysqlnd supports utf8mb4 since 5.0.9.
	 *
	 * @return  boolean
	 *
	 * @since   CMS 3.5.0
	 */
	private function serverClaimsUtf8mb4Support()
	{
		$client_version = mysql_get_client_info();
		$server_version = $this->getVersion();

		if (version_compare($server_version, '5.5.3',
'<'))
		{
			return false;
		}
		else
		{
			if (strpos($client_version, 'mysqlnd') !== false)
			{
				$client_version = preg_replace('/^\D+([\d.]+).*/',
'$1', $client_version);

				return version_compare($client_version, '5.0.9',
'>=');
			}
			else
			{
				return version_compare($client_version, '5.5.3',
'>=');
			}
		}
	}

	/**
	 * Return the actual SQL Error number
	 *
	 * @return  integer  The SQL Error number
	 *
	 * @since   3.4.6
	 */
	protected function getErrorNumber()
	{
		return (int) mysql_errno($this->connection);
	}

	/**
	 * Return the actual SQL Error message
	 *
	 * @return  string  The SQL Error message
	 *
	 * @since   3.4.6
	 */
	protected function getErrorMessage()
	{
		$errorMessage = (string) mysql_error($this->connection);

		// Replace the Databaseprefix with `#__` if we are not in Debug
		if (!$this->debug)
		{
			$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
		}

		return $errorMessage;
	}
}
PK��[�'�HaHadriver/mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQLi database driver
 *
 * @link   https://www.php.net/manual/en/book.mysqli.php
 * @since  3.0.0
 */
class JDatabaseDriverMysqli extends JDatabaseDriver
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'mysqli';

	/**
	 * The type of the database server family supported by this driver.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType = 'mysql';

	/**
	 * @var    mysqli  The database connection resource.
	 * @since  1.7.0
	 */
	protected $connection;

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc. The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.0.1
	 */
	protected $nameQuote = '`';

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  3.0.1
	 */
	protected $nullDate = '0000-00-00 00:00:00';

	/**
	 * @var    string  The minimum supported database version.
	 * @since  3.0.1
	 */
	protected static $dbMinimum = '5.0.4';

	/**
	 * Constructor.
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since   3.0.0
	 */
	public function __construct($options)
	{
		// Get some basic values from the 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'] : '';
		$options['select']   = (isset($options['select'])) ?
(bool) $options['select'] : true;
		$options['port']     = (isset($options['port'])) ?
(int) $options['port'] : null;
		$options['socket']   = (isset($options['socket'])) ?
$options['socket'] : null;

		// Finalize initialisation.
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		/*
		 * Unlike mysql_connect(), mysqli_connect() takes the port and socket as
separate arguments. Therefore, we
		 * have to extract them from the host string.
		 */
		$port = isset($this->options['port']) ?
$this->options['port'] : 3306;
		$regex =
'/^(?P<host>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P<port>.+))?$/';

		if (preg_match($regex, $this->options['host'], $matches))
		{
			// It's an IPv4 address with or without port
			$this->options['host'] = $matches['host'];

			if (!empty($matches['port']))
			{
				$port = $matches['port'];
			}
		}
		elseif
(preg_match('/^(?P<host>\[.*\])(:(?P<port>.+))?$/',
$this->options['host'], $matches))
		{
			// We assume square-bracketed IPv6 address with or without port, e.g.
[fe80:102::2%eth1]:3306
			$this->options['host'] = $matches['host'];

			if (!empty($matches['port']))
			{
				$port = $matches['port'];
			}
		}
		elseif
(preg_match('/^(?P<host>(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P<port>[^:]+))?$/i',
$this->options['host'], $matches))
		{
			// Named host (e.g example.com or localhost) with or without port
			$this->options['host'] = $matches['host'];

			if (!empty($matches['port']))
			{
				$port = $matches['port'];
			}
		}
		elseif (preg_match('/^:(?P<port>[^:]+)$/',
$this->options['host'], $matches))
		{
			// Empty host, just port, e.g. ':3306'
			$this->options['host'] = 'localhost';
			$port = $matches['port'];
		}
		// ... else we assume normal (naked) IPv6 address, so host and port stay
as they are or default

		// Get the port number or socket name
		if (is_numeric($port))
		{
			$this->options['port'] = (int) $port;
		}
		else
		{
			$this->options['socket'] = $port;
		}

		// Make sure the MySQLi extension for PHP is installed and enabled.
		if (!self::isSupported())
		{
			throw new JDatabaseExceptionUnsupported('The MySQLi extension for
PHP is not installed or enabled.');
		}

		$this->connection = @mysqli_connect(
			$this->options['host'],
$this->options['user'],
$this->options['password'], null,
$this->options['port'], $this->options['socket']
		);

		// Attempt to connect to the server.
		if (!$this->connection)
		{
			throw new JDatabaseExceptionConnecting('Could not connect to MySQL
server.');
		}

		// Set sql_mode to non_strict mode
		mysqli_query($this->connection, "SET @@SESSION.sql_mode =
'';");

		// If auto-select is enabled select the given database.
		if ($this->options['select'] &&
!empty($this->options['database']))
		{
			$this->select($this->options['database']);
		}

		// Pre-populate the UTF-8 Multibyte compatibility flag based on server
version
		$this->utf8mb4 = $this->serverClaimsUtf8mb4Support();

		// Set the character set (needed for MySQL 4.1.2+).
		$this->utf = $this->setUtf();

		// Disable query cache and turn profiling ON in debug mode.
		if ($this->debug)
		{
			mysqli_query($this->connection, 'SET query_cache_type =
0;');

			if ($this->hasProfiling())
			{
				mysqli_query($this->connection, 'SET profiling_history_size =
100, profiling = 1;');
			}
		}
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		// Close the connection.
		if ($this->connection instanceof mysqli &&
$this->connection->stat() !== false)
		{
			foreach ($this->disconnectHandlers as $h)
			{
				call_user_func_array($h, array( &$this));
			}

			mysqli_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   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$this->connect();

		$result = mysqli_real_escape_string($this->getConnection(), $text);

		if ($extra)
		{
			$result = addcslashes($result, '%_');
		}

		return $result;
	}

	/**
	 * Test to see if the MySQL connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return function_exists('mysqli_connect');
	}

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return  boolean  True if connected to the database engine.
	 *
	 * @since   3.0.0
	 */
	public function connected()
	{
		if (is_object($this->connection))
		{
			return mysqli_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  JDatabaseDriverMysqli  Returns this object to support
chaining.
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->connect();

		$query = $this->getQuery(true);

		$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $query->quoteName($tableName));

		$this->execute();

		return $this;
	}

	/**
	 * 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.
	 *
	 * @since   3.0.0
	 */
	public function getAffectedRows()
	{
		$this->connect();

		return mysqli_affected_rows($this->connection);
	}

	/**
	 * Method to get the database collation.
	 *
	 * @return  mixed  The collation in use by the database (string) or
boolean false if not supported.
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function getCollation()
	{
		$this->connect();

		// Attempt to get the database collation by accessing the server system
variable.
		$this->setQuery('SHOW VARIABLES LIKE
"collation_database"');
		$result = $this->loadObject();

		if (property_exists($result, 'Value'))
		{
			return $result->Value;
		}
		else
		{
			return false;
		}
	}

	/**
	 * 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()
	{
		$this->connect();

		// Attempt to get the database collation by accessing the server system
variable.
		$this->setQuery('SHOW VARIABLES LIKE
"collation_connection"');
		$result = $this->loadObject();

		if (property_exists($result, 'Value'))
		{
			return $result->Value;
		}
		else
		{
			return false;
		}
	}

	/**
	 * 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 an INSERT, UPDATE, REPLACE
or DELETE query, use getAffectedRows().
	 *
	 * @param   resource  $cursor  An optional database cursor resource to
extract the row count from.
	 *
	 * @return  integer   The number of returned rows.
	 *
	 * @since   3.0.0
	 */
	public function getNumRows($cursor = null)
	{
		return mysqli_num_rows($cursor ? $cursor : $this->cursor);
	}

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableCreate($tables)
	{
		$this->connect();

		$result = array();

		// Sanitize input to an array and iterate over the list.
		settype($tables, 'array');

		foreach ($tables as $table)
		{
			// Set the query to get the table CREATE statement.
			$this->setQuery('SHOW CREATE table ' .
$this->quoteName($this->escape($table)));
			$row = $this->loadRow();

			// Populate the result array based on the create statements.
			$result[$table] = $row[1];
		}

		return $result;
	}

	/**
	 * 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   3.0.1
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$result = array();

		// Set the query to get the table fields statement.
		$this->setQuery('SHOW FULL COLUMNS FROM ' .
$this->quoteName($this->escape($table)));
		$fields = $this->loadObjectList();

		// If we only want the type as the value add just that to the list.
		if ($typeOnly)
		{
			foreach ($fields as $field)
			{
				$result[$field->Field] = preg_replace('/[(0-9)]/',
'', $field->Type);
			}
		}
		// If we want the whole field data object add that to the list.
		else
		{
			foreach ($fields as $field)
			{
				$result[$field->Field] = $field;
			}
		}

		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   3.0.1
	 * @throws  RuntimeException
	 */
	public function getTableKeys($table)
	{
		$this->connect();

		// Get the details columns information.
		$this->setQuery('SHOW KEYS FROM ' .
$this->quoteName($table));
		$keys = $this->loadObjectList();

		return $keys;
	}

	/**
	 * Method to get an array of all tables in the database.
	 *
	 * @return  array  An array of all the tables in the database.
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function getTableList()
	{
		$this->connect();

		// Set the query to get the tables statement.
		$this->setQuery('SHOW TABLES');
		$tables = $this->loadColumn();

		return $tables;
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.0.0
	 */
	public function getVersion()
	{
		$this->connect();

		return mysqli_get_server_info($this->connection);
	}

	/**
	 * Method to get the auto-incremented value from the last INSERT
statement.
	 *
	 * @return  mixed  The value of the auto-increment field from the last
inserted row.
	 *                 If the value is greater than maximal int value, it will
return a string.
	 *
	 * @since   3.0.0
	 */
	public function insertid()
	{
		$this->connect();

		return mysqli_insert_id($this->connection);
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $table  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriverMysqli  Returns this object to support
chaining.
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function lockTable($table)
	{
		$this->setQuery('LOCK TABLES ' . $this->quoteName($table)
. ' WRITE')->execute();

		return $this;
	}

	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function execute()
	{
		$this->connect();

		// 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 JDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
		{
			$query .= ' LIMIT ' . $this->offset . ', ' .
$this->limit;
		}

		if (!is_object($this->connection))
		{
			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
			throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
		}

		// Increment the query counter.
		$this->count++;

		// Reset the error values.
		$this->errorNum = 0;
		$this->errorMsg = '';
		$memoryBefore   = null;

		// If debugging is enabled then let's log the query.
		if ($this->debug)
		{
			// Add the query to the object queue.
			$this->log[] = $query;

			JLog::add($query, JLog::DEBUG, 'databasequery');

			$this->timings[] = microtime(true);

			if (is_object($this->cursor))
			{
				// Avoid warning if result already freed by third-party library
				@$this->freeResult();
			}

			$memoryBefore = memory_get_usage();
		}

		// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
		$this->cursor = @mysqli_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();
			}

			$this->callStacks[count($this->callStacks) -
1][0]['memory'] = array(
				$memoryBefore,
				memory_get_usage(),
				is_object($this->cursor) ? $this->getNumRows() : null,
			);
		}

		// If an error occurred handle it.
		if (!$this->cursor)
		{
			// Get the error number and message before we execute any more queries.
			$this->errorNum = $this->getErrorNumber();
			$this->errorMsg = $this->getErrorMessage();

			// 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)
				{
					// Get the error number and message.
					$this->errorNum = $this->getErrorNumber();
					$this->errorMsg = $this->getErrorMessage();

					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

					throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum, $e);
				}

				// Since we were able to reconnect, run the query again.
				return $this->execute();
			}
			// The server was not disconnected.
			else
			{
				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

				throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
			}
		}

		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 MySQL.
	 * @param   string  $prefix    Not used by MySQL.
	 *
	 * @return  JDatabaseDriverMysqli  Returns this object to support
chaining.
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		$this->setQuery('RENAME TABLE ' . $oldTable . ' TO
' . $newTable)->execute();

		return $this;
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		if (!$database)
		{
			return false;
		}

		if (!mysqli_select_db($this->connection, $database))
		{
			throw new JDatabaseExceptionConnecting('Could not connect to MySQL
database.');
		}

		return true;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		// If UTF is not supported return false immediately
		if (!$this->utf)
		{
			return false;
		}

		// Make sure we're connected to the server
		$this->connect();

		// Which charset should I use, plain utf8 or multibyte utf8mb4?
		$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';

		$result = @$this->connection->set_charset($charset);

		/**
		 * If I could not set the utf8mb4 charset then the server doesn't
support utf8mb4 despite claiming otherwise.
		 * This happens on old MySQL server versions (less than 5.5.3) using the
mysqlnd PHP driver. Since mysqlnd
		 * masks the server version and reports only its own we can not be sure
if the server actually does support
		 * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the
utf8mb4 charset is undefined in this case we
		 * catch the error and determine that utf8mb4 is not supported!
		 */
		if (!$result && $this->utf8mb4)
		{
			$this->utf8mb4 = false;
			$result = @$this->connection->set_charset('utf8');
		}

		return $result;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.0.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   3.0.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--;
		}
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.0.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   3.0.0
	 */
	protected function fetchArray($cursor = null)
	{
		return mysqli_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   3.0.0
	 */
	protected function fetchAssoc($cursor = null)
	{
		return mysqli_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   3.0.0
	 */
	protected function fetchObject($cursor = null, $class =
'stdClass')
	{
		return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $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   3.0.0
	 */
	protected function freeResult($cursor = null)
	{
		mysqli_free_result($cursor ? $cursor : $this->cursor);

		if ((! $cursor) || ($cursor === $this->cursor))
		{
			$this->cursor = null;
		}
	}

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriverMysqli  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function unlockTables()
	{
		$this->setQuery('UNLOCK TABLES')->execute();

		return $this;
	}

	/**
	 * Internal function to check if profiling is available
	 *
	 * @return  boolean
	 *
	 * @since   3.1.3
	 */
	private function hasProfiling()
	{
		try
		{
			$res = mysqli_query($this->connection, "SHOW VARIABLES LIKE
'have_profiling'");
			$row = mysqli_fetch_assoc($res);

			return isset($row);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	/**
	 * Does the database server claim to have support for UTF-8 Multibyte
(utf8mb4) collation?
	 *
	 * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL
server). mysqlnd supports utf8mb4 since 5.0.9.
	 *
	 * @return  boolean
	 *
	 * @since   CMS 3.5.0
	 */
	private function serverClaimsUtf8mb4Support()
	{
		$client_version = mysqli_get_client_info();
		$server_version = $this->getVersion();

		if (version_compare($server_version, '5.5.3',
'<'))
		{
			return false;
		}
		else
		{
			if (strpos($client_version, 'mysqlnd') !== false)
			{
				$client_version = preg_replace('/^\D+([\d.]+).*/',
'$1', $client_version);

				return version_compare($client_version, '5.0.9',
'>=');
			}
			else
			{
				return version_compare($client_version, '5.5.3',
'>=');
			}
		}
	}

	/**
	 * Return the actual SQL Error number
	 *
	 * @return  integer  The SQL Error number
	 *
	 * @since   3.4.6
	 */
	protected function getErrorNumber()
	{
		return (int) mysqli_errno($this->connection);
	}

	/**
	 * Return the actual SQL Error message
	 *
	 * @return  string  The SQL Error message
	 *
	 * @since   3.4.6
	 */
	protected function getErrorMessage()
	{
		$errorMessage = (string) mysqli_error($this->connection);

		// Replace the Databaseprefix with `#__` if we are not in Debug
		if (!$this->debug)
		{
			$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
		}

		return $errorMessage;
	}
}
PK��[����=�=driver/oracle.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Oracle database driver
 *
 * @link   https://www.php.net/pdo
 * @since  3.0.0
 */
class JDatabaseDriverOracle extends JDatabaseDriverPdo
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'oracle';

	/**
	 * The type of the database server family supported by this driver.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType = 'oracle';

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc.  The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nameQuote = '"';

	/**
	 * Returns the current dateformat
	 *
	 * @var   string
	 * @since 3.0.0
	 */
	protected $dateformat;

	/**
	 * Returns the current character set
	 *
	 * @var   string
	 * @since 3.0.0
	 */
	protected $charset;

	/**
	 * Constructor.
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since   3.0.0
	 */
	public function __construct($options)
	{
		$options['driver'] = 'oci';
		$options['charset']    = (isset($options['charset']))
? $options['charset']   : 'AL32UTF8';
		$options['dateformat'] =
(isset($options['dateformat'])) ?
$options['dateformat'] : 'RRRR-MM-DD HH24:MI:SS';

		$this->charset = $options['charset'];
		$this->dateformat = $options['dateformat'];

		// Finalize initialisation
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		parent::connect();

		if (isset($this->options['schema']))
		{
			$this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' .
$this->quoteName($this->options['schema']))->execute();
		}

		$this->setDateFormat($this->dateformat);
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		// Close the connection.
		$this->freeResult();

		$this->connection = null;
	}

	/**
	 * Drops a table from the database.
	 *
	 * Note: The IF EXISTS flag is unused in the Oracle driver.
	 *
	 * @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  JDatabaseDriverOracle  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->connect();

		$query = $this->getQuery(true)
			->setQuery('DROP TABLE :tableName');
		$query->bind(':tableName', $tableName);

		$this->setQuery($query);

		$this->execute();

		return $this;
	}

	/**
	 * 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   3.0.0
	 */
	public function getCollation()
	{
		return $this->charset;
	}

	/**
	 * 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 $this->charset;
	}

	/**
	 * Get a query to run and verify the database is operational.
	 *
	 * @return  string  The query to check the health of the DB.
	 *
	 * @since   3.0.1
	 */
	public function getConnectedQuery()
	{
		return 'SELECT 1 FROM dual';
	}

	/**
	 * Returns the current date format
	 * This method should be useful in the case that
	 * somebody actually wants to use a different
	 * date format and needs to check what the current
	 * one is to see if it needs to be changed.
	 *
	 * @return string The current date format
	 *
	 * @since 3.0.0
	 */
	public function getDateFormat()
	{
		return $this->dateformat;
	}

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * Note: You must have the correct privileges before this method
	 * will return usable results!
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableCreate($tables)
	{
		$this->connect();

		$result = array();
		$query = $this->getQuery(true)
			->select('dbms_metadata.get_ddl(:type, :tableName)')
			->from('dual')
			->bind(':type', 'TABLE');

		// Sanitize input to an array and iterate over the list.
		settype($tables, 'array');

		foreach ($tables as $table)
		{
			$query->bind(':tableName', $table);
			$this->setQuery($query);
			$statement = (string) $this->loadResult();
			$result[$table] = $statement;
		}

		return $result;
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$columns = array();
		$query = $this->getQuery(true);

		$fieldCasing = $this->getOption(PDO::ATTR_CASE);

		$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);

		$table = strtoupper($table);

		$query->select('*');
		$query->from('ALL_TAB_COLUMNS');
		$query->where('table_name = :tableName');

		$prefixedTable = str_replace('#__',
strtoupper($this->tablePrefix), $table);
		$query->bind(':tableName', $prefixedTable);
		$this->setQuery($query);
		$fields = $this->loadObjectList();

		if ($typeOnly)
		{
			foreach ($fields as $field)
			{
				$columns[$field->COLUMN_NAME] = $field->DATA_TYPE;
			}
		}
		else
		{
			foreach ($fields as $field)
			{
				$columns[$field->COLUMN_NAME] = $field;
				$columns[$field->COLUMN_NAME]->Default = null;
			}
		}

		$this->setOption(PDO::ATTR_CASE, $fieldCasing);

		return $columns;
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableKeys($table)
	{
		$this->connect();

		$query = $this->getQuery(true);

		$fieldCasing = $this->getOption(PDO::ATTR_CASE);

		$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);

		$table = strtoupper($table);
		$query->select('*')
			->from('ALL_CONSTRAINTS')
			->where('table_name = :tableName')
			->bind(':tableName', $table);

		$this->setQuery($query);
		$keys = $this->loadObjectList();

		$this->setOption(PDO::ATTR_CASE, $fieldCasing);

		return $keys;
	}

	/**
	 * Method to get an array of all tables in the database (schema).
	 *
	 * @param   string   $databaseName         The database (schema) name
	 * @param   boolean  $includeDatabaseName  Whether to include the schema
name in the results
	 *
	 * @return  array    An array of all the tables in the database.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableList($databaseName = null, $includeDatabaseName =
false)
	{
		$this->connect();

		$query = $this->getQuery(true);

		if ($includeDatabaseName)
		{
			$query->select('owner, table_name');
		}
		else
		{
			$query->select('table_name');
		}

		$query->from('all_tables');

		if ($databaseName)
		{
			$query->where('owner = :database')
				->bind(':database', $databaseName);
		}

		$query->order('table_name');

		$this->setQuery($query);

		if ($includeDatabaseName)
		{
			$tables = $this->loadAssocList();
		}
		else
		{
			$tables = $this->loadColumn();
		}

		return $tables;
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.0.0
	 */
	public function getVersion()
	{
		$this->connect();

		$this->setQuery("select value from nls_database_parameters where
parameter = 'NLS_RDBMS_VERSION'");

		return $this->loadResult();
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		return true;
	}

	/**
	 * Sets the Oracle Date Format for the session
	 * Default date format for Oracle is = DD-MON-RR
	 * The default date format for this driver is:
	 * 'RRRR-MM-DD HH24:MI:SS' since it is the format
	 * that matches the MySQL one used within most Joomla
	 * tables.
	 *
	 * @param   string  $dateFormat  Oracle Date Format String
	 *
	 * @return boolean
	 *
	 * @since  3.0.0
	 */
	public function setDateFormat($dateFormat = 'DD-MON-RR')
	{
		$this->connect();

		$this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT =
'$dateFormat'");

		if (!$this->execute())
		{
			return false;
		}

		$this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT =
'$dateFormat'");

		if (!$this->execute())
		{
			return false;
		}

		$this->dateformat = $dateFormat;

		return true;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * Returns false automatically for the Oracle driver since
	 * you can only set the character set when the connection
	 * is created.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		return false;
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $table  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriverOracle  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function lockTable($table)
	{
		$this->setQuery('LOCK TABLE ' . $this->quoteName($table)
. ' IN EXCLUSIVE MODE')->execute();

		return $this;
	}

	/**
	 * 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 Oracle.
	 * @param   string  $prefix    Not used by Oracle.
	 *
	 * @return  JDatabaseDriverOracle  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		$this->setQuery('RENAME ' . $oldTable . ' TO ' .
$newTable)->execute();

		return $this;
	}

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriverOracle  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function unlockTables()
	{
		$this->setQuery('COMMIT')->execute();

		return $this;
	}

	/**
	 * Test to see if the PDO ODBC connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return class_exists('PDO') && in_array('oci',
PDO::getAvailableDrivers());
	}

	/**
	 * 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   1.7.0
	 */
	public function replacePrefix($query, $prefix = '#__')
	{
		$startPos = 0;
		$quoteChar = "'";
		$literal = '';

		$query = trim($query);
		$n = strlen($query);

		while ($startPos < $n)
		{
			$ip = strpos($query, $prefix, $startPos);

			if ($ip === false)
			{
				break;
			}

			$j = strpos($query, "'", $startPos);

			if ($j === false)
			{
				$j = $n;
			}

			$literal .= str_replace($prefix, $this->tablePrefix, substr($query,
$startPos, $j - $startPos));
			$startPos = $j;

			$j = $startPos + 1;

			if ($j >= $n)
			{
				break;
			}

			// Quote comes first, find end of quote
			while (true)
			{
				$k = strpos($query, $quoteChar, $j);
				$escaped = false;

				if ($k === false)
				{
					break;
				}

				$l = $k - 1;

				while ($l >= 0 && $query[$l] == '\\')
				{
					$l--;
					$escaped = !$escaped;
				}

				if ($escaped)
				{
					$j = $k + 1;
					continue;
				}

				break;
			}

			if ($k === false)
			{
				// Error in the query - no end quote; ignore it
				break;
			}

			$literal .= substr($query, $startPos, $k - $startPos + 1);
			$startPos = $k + 1;
		}

		if ($startPos < $n)
		{
			$literal .= substr($query, $startPos, $n - $startPos);
		}

		return $literal;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionCommit($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionCommit($toSavepoint);
		}
		else
		{
			$this->transactionDepth--;
		}
	}

	/**
	 * Method to roll back a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, rollback to the last
savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionRollback($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionRollback($toSavepoint);
		}
		else
		{
			$savepoint = 'SP_' . ($this->transactionDepth - 1);
			$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));

			if ($this->execute())
			{
				$this->transactionDepth--;
			}
		}
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionStart($asSavepoint = false)
	{
		$this->connect();

		if (!$asSavepoint || !$this->transactionDepth)
		{
			return parent::transactionStart($asSavepoint);
		}

		$savepoint = 'SP_' . $this->transactionDepth;
		$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));

		if ($this->execute())
		{
			$this->transactionDepth++;
		}
	}

	/**
	 * Get the query strings to alter the character set and collation of a
table.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string[]  The queries required to alter the table's
character set and collation
	 *
	 * @since   CMS 3.5.0
	 */
	public function getAlterTableCharacterSet($tableName)
	{
		return array();
	}

	/**
	 * Return the query string to create new Database.
	 * Each database driver, other than MySQL, need to override this member to
return correct string.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 *                   This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 */
	protected function getCreateDatabaseQuery($options, $utf)
	{
		return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
	}
}
PK��[:�֍�g�gdriver/pdo.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform PDO Database Driver Class
 *
 * @link   https://www.php.net/pdo
 * @since  3.0.0
 */
abstract class JDatabaseDriverPdo extends JDatabaseDriver
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'pdo';

	/**
	 * @var    PDO  The database connection resource.
	 * @since  3.0.0
	 */
	protected $connection;

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc.  The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nameQuote = "'";

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nullDate = '0000-00-00 00:00:00';

	/**
	 * @var    resource  The prepared statement.
	 * @since  3.0.0
	 */
	protected $prepared;

	/**
	 * Contains the current query execution status
	 *
	 * @var array
	 * @since 3.0.0
	 */
	protected $executed = false;

	/**
	 * Constructor.
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since   3.0.0
	 */
	public function __construct($options)
	{
		// Get some basic values from the options.
		$options['driver'] = (isset($options['driver'])) ?
$options['driver'] : 'odbc';
		$options['dsn'] = (isset($options['dsn'])) ?
$options['dsn'] : '';
		$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
		$options['database'] = (isset($options['database']))
? $options['database'] : '';
		$options['user'] = (isset($options['user'])) ?
$options['user'] : '';
		$options['password'] = (isset($options['password']))
? $options['password'] : '';
		$options['driverOptions'] =
(isset($options['driverOptions'])) ?
$options['driverOptions'] : array();

		$hostParts = explode(':', $options['host']);

		if (!empty($hostParts[1]))
		{
			$options['host'] = $hostParts[0];
			$options['port'] = $hostParts[1];
		}

		// Finalize initialisation
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		// Make sure the PDO extension for PHP is installed and enabled.
		if (!self::isSupported())
		{
			throw new JDatabaseExceptionUnsupported('PDO Extension is not
available.', 1);
		}

		$replace = array();
		$with = array();

		// Find the correct PDO DSN Format to use:
		switch ($this->options['driver'])
		{
			case 'cubrid':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 33000;

				$format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);

				break;

			case 'dblib':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;

				$format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);

				break;

			case 'firebird':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 3050;

				$format = 'firebird:dbname=#DBNAME#';

				$replace = array('#DBNAME#');
				$with = array($this->options['database']);

				break;

			case 'ibm':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 56789;

				if (!empty($this->options['dsn']))
				{
					$format = 'ibm:DSN=#DSN#';

					$replace = array('#DSN#');
					$with = array($this->options['dsn']);
				}
				else
				{
					$format =
'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#';

					$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
					$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
				}

				break;

			case 'informix':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1526;
				$this->options['protocol'] =
(isset($this->options['protocol'])) ?
$this->options['protocol'] : 'onsoctcp';

				if (!empty($this->options['dsn']))
				{
					$format = 'informix:DSN=#DSN#';

					$replace = array('#DSN#');
					$with = array($this->options['dsn']);
				}
				else
				{
					$format =
'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#';

					$replace = array('#HOST#', '#PORT#',
'#DBNAME#', '#SERVER#', '#PROTOCOL#');
					$with = array($this->options['host'],
$this->options['port'],
$this->options['database'],
$this->options['server'],
$this->options['protocol']);
				}

				break;

			case 'mssql':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;

				$format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);

				break;

			// The pdomysql case is a special case within the CMS environment
			case 'pdomysql':
			case 'mysql':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 3306;

				$format =
'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#', '#CHARSET#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database'],
$this->options['charset']);

				break;

			case 'oci':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1521;
				$this->options['charset'] =
(isset($this->options['charset'])) ?
$this->options['charset'] : 'AL32UTF8';

				if (!empty($this->options['dsn']))
				{
					$format = 'oci:dbname=#DSN#';

					$replace = array('#DSN#');
					$with = array($this->options['dsn']);
				}
				else
				{
					$format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#';

					$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
					$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
				}

				$format .= ';charset=' .
$this->options['charset'];

				break;

			case 'odbc':
				$format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#';

				$replace = array('#DSN#', '#USER#',
'#PASSWORD#');
				$with = array($this->options['dsn'],
$this->options['user'],
$this->options['password']);

				break;

			case 'pgsql':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 5432;

				$format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);

				break;

			case 'sqlite':
				if (isset($this->options['version']) &&
$this->options['version'] == 2)
				{
					$format = 'sqlite2:#DBNAME#';
				}
				else
				{
					$format = 'sqlite:#DBNAME#';
				}

				$replace = array('#DBNAME#');
				$with = array($this->options['database']);

				break;

			case 'sybase':
				$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;

				$format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';

				$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
				$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);

				break;
		}

		// Create the connection string:
		$connectionString = str_replace($replace, $with, $format);

		try
		{
			$this->connection = new PDO(
				$connectionString,
				$this->options['user'],
				$this->options['password'],
				$this->options['driverOptions']
			);
		}
		catch (PDOException $e)
		{
			throw new JDatabaseExceptionConnecting('Could not connect to PDO:
' . $e->getMessage(), 2, $e);
		}
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		foreach ($this->disconnectHandlers as $h)
		{
			call_user_func_array($h, array( &$this));
		}

		$this->freeResult();
		$this->connection = null;
	}

	/**
	 * Method to escape a string for usage in an SQL statement.
	 *
	 * Oracle escaping reference:
	 *
http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
	 *
	 * SQLite escaping notes:
	 * http://www.sqlite.org/faq.html#q14
	 *
	 * Method body is as implemented by the Zend Framework
	 *
	 * Note: Using query objects with bound variables is
	 * preferable to the below.
	 *
	 * @param   string   $text   The string to be escaped.
	 * @param   boolean  $extra  Unused optional parameter to provide extra
escaping.
	 *
	 * @return  string  The escaped string.
	 *
	 * @since   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$text = str_replace("'", "''", $text);

		return addcslashes($text, "\000\n\r\\\032");
	}

	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 * @throws  Exception
	 */
	public function execute()
	{
		$this->connect();

		// Take a local copy so that we don't modify the original query and
cause issues later
		$query = $this->replacePrefix((string) $this->sql);

		if (!is_object($this->connection))
		{
			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
			throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
		}

		// 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;

			JLog::add($query, JLog::DEBUG, 'databasequery');

			$this->timings[] = microtime(true);
		}

		// Execute the query.
		$this->executed = false;

		if ($this->prepared instanceof PDOStatement)
		{
			// Bind the variables:
			if ($this->sql instanceof JDatabaseQueryPreparable)
			{
				$bounded = $this->sql->getBounded();

				foreach ($bounded as $key => $obj)
				{
					$this->prepared->bindParam($key, $obj->value,
$obj->dataType, $obj->length, $obj->driverOptions);
				}
			}

			$this->executed = $this->prepared->execute();
		}

		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->executed)
		{
			// Get the error number and message before we execute any more queries.
			$errorNum = $this->getErrorNumber();
			$errorMsg = $this->getErrorMessage();

			// 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)
				{
					// Get the error number and message.
					$this->errorNum = $this->getErrorNumber();
					$this->errorMsg = $this->getErrorMessage();

					// Throw the normal query exception.
					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

					throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum, $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.
				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

				throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
			}
		}

		return $this->prepared;
	}

	/**
	 * Retrieve a PDO database connection attribute
	 *
	 * Usage: $db->getOption(PDO::ATTR_CASE);
	 *
	 * @param   mixed  $key  One of the PDO::ATTR_* Constants
	 *
	 * @return  mixed
	 *
	 * @link    https://www.php.net/manual/en/pdo.getattribute.php
	 * @since   3.0.0
	 */
	public function getOption($key)
	{
		$this->connect();

		return $this->connection->getAttribute($key);
	}

	/**
	 * Get a query to run and verify the database is operational.
	 *
	 * @return  string  The query to check the health of the DB.
	 *
	 * @since   3.0.1
	 */
	public function getConnectedQuery()
	{
		return 'SELECT 1';
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.9.0
	 */
	public function getVersion()
	{
		$this->connect();

		return $this->getOption(PDO::ATTR_SERVER_VERSION);
	}

	/**
	 * Sets an attribute on the PDO database handle.
	 *
	 * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
	 *
	 * @param   integer  $key    One of the PDO::ATTR_* Constants
	 * @param   mixed    $value  One of the associated PDO Constants related
to the particular attribute key.
	 *
	 * @return  boolean
	 *
	 * @link   https://www.php.net/manual/en/pdo.setattribute.php
	 * @since   3.0.0
	 */
	public function setOption($key, $value)
	{
		$this->connect();

		return $this->connection->setAttribute($key, $value);
	}

	/**
	 * Test to see if the PDO extension is available.
	 * Override as needed to check for specific PDO Drivers.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return defined('PDO::ATTR_DRIVER_NAME');
	}

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return  boolean  True if connected to the database engine.
	 *
	 * @since   3.0.0
	 */
	public function connected()
	{
		// Flag to prevent recursion into this function.
		static $checkingConnected = false;

		if ($checkingConnected)
		{
			// Reset this flag and throw an exception.
			$checkingConnected = true;
			die('Recursion trying to check if connected.');
		}

		// Backup the query state.
		$query = $this->sql;
		$limit = $this->limit;
		$offset = $this->offset;
		$prepared = $this->prepared;

		try
		{
			// Set the checking connection flag.
			$checkingConnected = true;

			// Run a simple query to check the connection.
			$this->setQuery($this->getConnectedQuery());
			$status = (bool) $this->loadResult();
		}
		// If we catch an exception here, we must not be connected.
		catch (Exception $e)
		{
			$status = false;
		}

		// Restore the query state.
		$this->sql = $query;
		$this->limit = $limit;
		$this->offset = $offset;
		$this->prepared = $prepared;
		$checkingConnected = false;

		return $status;
	}

	/**
	 * Get the number of affected rows for the previous executed SQL
statement.
	 * Only applicable for DELETE, INSERT, or UPDATE statements.
	 *
	 * @return  integer  The number of affected rows.
	 *
	 * @since   3.0.0
	 */
	public function getAffectedRows()
	{
		$this->connect();

		if ($this->prepared instanceof PDOStatement)
		{
			return $this->prepared->rowCount();
		}
		else
		{
			return 0;
		}
	}

	/**
	 * Get the number of returned rows for the previous executed SQL
statement.
	 * Only applicable for DELETE, INSERT, or UPDATE statements.
	 *
	 * @param   resource  $cursor  An optional database cursor resource to
extract the row count from.
	 *
	 * @return  integer   The number of returned rows.
	 *
	 * @since   3.0.0
	 */
	public function getNumRows($cursor = null)
	{
		$this->connect();

		if ($cursor instanceof PDOStatement)
		{
			return $cursor->rowCount();
		}
		elseif ($this->prepared instanceof PDOStatement)
		{
			return $this->prepared->rowCount();
		}
		else
		{
			return 0;
		}
	}

	/**
	 * Method to get the auto-incremented value from the last INSERT
statement.
	 *
	 * @return  string  The value of the auto-increment field from the last
inserted row.
	 *
	 * @since   3.0.0
	 */
	public function insertid()
	{
		$this->connect();

		// Error suppress this to prevent PDO warning us that the driver
doesn't support this operation.
		return @$this->connection->lastInsertId();
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		return true;
	}

	/**
	 * Sets the SQL statement string for later execution.
	 *
	 * @param   mixed    $query          The SQL statement to set either as a
JDatabaseQuery object or a string.
	 * @param   integer  $offset         The affected row offset to set.
	 * @param   integer  $limit          The maximum affected rows to set.
	 * @param   array    $driverOptions  The optional PDO driver options.
	 *
	 * @return  JDatabaseDriver  This object to support method chaining.
	 *
	 * @since   3.0.0
	 */
	public function setQuery($query, $offset = null, $limit = null,
$driverOptions = array())
	{
		$this->connect();

		$this->freeResult();

		if (is_string($query))
		{
			// Allows taking advantage of bound variables in a direct query:
			$query = $this->getQuery(true)->setQuery($query);
		}

		if ($query instanceof JDatabaseQueryLimitable &&
!is_null($offset) && !is_null($limit))
		{
			$query = $query->processLimit($query, $limit, $offset);
		}

		// Create a stringified version of the query (with prefixes replaced):
		$sql = $this->replacePrefix((string) $query);

		// Use the stringified version in the prepare call:
		$this->prepared = $this->connection->prepare($sql,
$driverOptions);

		// Store reference to the original JDatabaseQuery instance within the
class.
		// This is important since binding variables depends on it within
execute():
		parent::setQuery($query, $offset, $limit);

		return $this;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		return false;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionCommit($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth == 1)
		{
			$this->connection->commit();
		}

		$this->transactionDepth--;
	}

	/**
	 * Method to roll back a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, rollback to the last
savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionRollback($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth == 1)
		{
			$this->connection->rollBack();
		}

		$this->transactionDepth--;
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionStart($asSavepoint = false)
	{
		$this->connect();

		if (!$asSavepoint || !$this->transactionDepth)
		{
			$this->connection->beginTransaction();
		}

		$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   3.0.0
	 */
	protected function fetchArray($cursor = null)
	{
		if (!empty($cursor) && $cursor instanceof PDOStatement)
		{
			return $cursor->fetch(PDO::FETCH_NUM);
		}

		if ($this->prepared instanceof PDOStatement)
		{
			return $this->prepared->fetch(PDO::FETCH_NUM);
		}
	}

	/**
	 * 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   3.0.0
	 */
	protected function fetchAssoc($cursor = null)
	{
		if (!empty($cursor) && $cursor instanceof PDOStatement)
		{
			return $cursor->fetch(PDO::FETCH_ASSOC);
		}

		if ($this->prepared instanceof PDOStatement)
		{
			return $this->prepared->fetch(PDO::FETCH_ASSOC);
		}
	}

	/**
	 * 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   Unused, only necessary so method signature
will be the same as parent.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	protected function fetchObject($cursor = null, $class =
'stdClass')
	{
		if (!empty($cursor) && $cursor instanceof PDOStatement)
		{
			return $cursor->fetchObject($class);
		}

		if ($this->prepared instanceof PDOStatement)
		{
			return $this->prepared->fetchObject($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   3.0.0
	 */
	protected function freeResult($cursor = null)
	{
		$this->executed = false;

		if ($cursor instanceof PDOStatement)
		{
			$cursor->closeCursor();
			$cursor = null;
		}

		if ($this->prepared instanceof PDOStatement)
		{
			$this->prepared->closeCursor();
			$this->prepared = null;
		}
	}

	/**
	 * Method to get the next row in the result set from the database query as
an object.
	 *
	 * @param   string  $class  The class name to use for the returned row
object.
	 *
	 * @return  mixed   The result of the query as an array, false if there
are no more rows.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 * @deprecated  4.0 (CMS)  Use getIterator() instead
	 */
	public function loadNextObject($class = 'stdClass')
	{
		JLog::add(__METHOD__ . '() is deprecated. Use
JDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
		$this->connect();

		// Execute the query and get the result set cursor.
		if (!$this->executed)
		{
			if (!($this->execute()))
			{
				return $this->errorNum ? null : false;
			}
		}

		// Get the next row from the result set as an object of type $class.
		if ($row = $this->fetchObject(null, $class))
		{
			return $row;
		}

		// Free up system resources and return.
		$this->freeResult();

		return false;
	}

	/**
	 * Method to get the next row in the result set from the database query as
an array.
	 *
	 * @return  mixed  The result of the query as an array, false if there are
no more rows.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function loadNextAssoc()
	{
		$this->connect();

		// Execute the query and get the result set cursor.
		if (!$this->executed)
		{
			if (!($this->execute()))
			{
				return $this->errorNum ? null : false;
			}
		}

		// Get the next row from the result set as an object of type $class.
		if ($row = $this->fetchAssoc())
		{
			return $row;
		}

		// Free up system resources and return.
		$this->freeResult();

		return false;
	}

	/**
	 * Method to get the next row in the result set from the database query as
an array.
	 *
	 * @return  mixed  The result of the query as an array, false if there are
no more rows.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 * @deprecated  4.0 (CMS)  Use getIterator() instead
	 */
	public function loadNextRow()
	{
		JLog::add(__METHOD__ . '() is deprecated. Use
JDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
		$this->connect();

		// Execute the query and get the result set cursor.
		if (!$this->executed)
		{
			if (!($this->execute()))
			{
				return $this->errorNum ? null : false;
			}
		}

		// Get the next row from the result set as an object of type $class.
		if ($row = $this->fetchArray())
		{
			return $row;
		}

		// Free up system resources and return.
		$this->freeResult();

		return false;
	}

	/**
	 * PDO does not support serialize
	 *
	 * @return  array
	 *
	 * @since   3.1.4
	 */
	public function __sleep()
	{
		$serializedProperties = array();

		$reflect = new ReflectionClass($this);

		// Get properties of the current class
		$properties = $reflect->getProperties();

		foreach ($properties as $property)
		{
			// Do not serialize properties that are PDO
			if ($property->isStatic() == false &&
!($this->{$property->name} instanceof PDO))
			{
				$serializedProperties[] = $property->name;
			}
		}

		return $serializedProperties;
	}

	/**
	 * Wake up after serialization
	 *
	 * @return  array
	 *
	 * @since   3.1.4
	 */
	public function __wakeup()
	{
		// Get connection back
		$this->__construct($this->options);
	}

	/**
	 * Return the actual SQL Error number
	 *
	 * @return  integer  The SQL Error number
	 *
	 * @since   3.4.6
	 */
	protected function getErrorNumber()
	{
		return (int) $this->connection->errorCode();
	}

	/**
	 * Return the actual SQL Error message
	 *
	 * @return  string  The SQL Error message
	 *
	 * @since   3.4.6
	 */
	protected function getErrorMessage()
	{
		// The SQL Error Information
		$errorInfo = implode(', ',
$this->connection->errorInfo());

		// Replace the Databaseprefix with `#__` if we are not in Debug
		if (!$this->debug)
		{
			$errorInfo = str_replace($this->tablePrefix, '#__',
$errorInfo);
		}

		return $errorInfo;
	}
}
PK��[	�CQ5Q5driver/pdomysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL database driver supporting PDO based connections
 *
 * @link   https://www.php.net/manual/en/ref.pdo-mysql.php
 * @since  3.4
 */
class JDatabaseDriverPdomysql extends JDatabaseDriverPdo
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.4
	 */
	public $name = 'pdomysql';

	/**
	 * The type of the database server family supported by this driver.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType = 'mysql';

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc. The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.4
	 */
	protected $nameQuote = '`';

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  3.4
	 */
	protected $nullDate = '0000-00-00 00:00:00';

	/**
	 * The minimum supported database version.
	 *
	 * @var    string
	 * @since  3.4
	 */
	protected static $dbMinimum = '5.0.4';

	/**
	 * Constructor.
	 *
	 * @param   array  $options  Array of database options with keys: host,
user, password, database, select.
	 *
	 * @since   3.4
	 */
	public function __construct($options)
	{
		// Get some basic values from the options.
		$options['driver']  = 'mysql';

		if (!isset($options['charset']) ||
$options['charset'] == 'utf8')
		{
			$options['charset'] = 'utf8mb4';
		}

		/**
		 * Pre-populate the UTF-8 Multibyte compatibility flag. Unfortunately PDO
won't report the server version
		 * unless we're connected to it, and we cannot connect to it unless
we know if it supports utf8mb4, which requires
		 * us knowing the server version. Because of this chicken and egg issue,
we _assume_ it's supported and we'll just
		 * catch any problems at connection time.
		 */
		$this->utf8mb4 = ($options['charset'] ==
'utf8mb4');

		// Finalize initialisation.
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		try
		{
			// Try to connect to MySQL
			parent::connect();
		}
		catch (\RuntimeException $e)
		{
			// If the connection failed, but not because of the wrong character set,
then bubble up the exception.
			if (!$this->utf8mb4)
			{
				throw $e;
			}

			/*
			 * Otherwise, try connecting again without using
			 * utf8mb4 and see if maybe that was the problem. If the
			 * connection succeeds, then we will have learned that the
			 * client end of the connection does not support utf8mb4.
  			 */
			$this->utf8mb4 = false;
			$this->options['charset'] = 'utf8';

			parent::connect();
		}

		if ($this->utf8mb4)
		{
			/*
 			 * At this point we know the client supports utf8mb4.  Now
 			 * we must check if the server supports utf8mb4 as well.
 			 */
			$serverVersion =
$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
			$this->utf8mb4 = version_compare($serverVersion, '5.5.3',
'>=');

			if (!$this->utf8mb4)
			{
				// Reconnect with the utf8 character set.
				parent::disconnect();
				$this->options['charset'] = 'utf8';
				parent::connect();
			}
		}

		$this->connection->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
		$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

		// Set sql_mode to non_strict mode
		$this->connection->query("SET @@SESSION.sql_mode =
'';");

		// Disable query cache and turn profiling ON in debug mode.
		if ($this->debug)
		{
			$this->connection->query('SET query_cache_type = 0;');

			if ($this->hasProfiling())
			{
				$this->connection->query('SET profiling_history_size = 100,
profiling = 1;');
			}
		}
	}

	/**
	 * Test to see if the MySQL connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.4
	 */
	public static function isSupported()
	{
		return class_exists('PDO') &&
in_array('mysql', PDO::getAvailableDrivers());
	}

	/**
	 * 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  JDatabaseDriverPdomysql  Returns this object to support
chaining.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->connect();

		$query = $this->getQuery(true);

		$query->setQuery('DROP TABLE ' . ($ifExists ? 'IF
EXISTS ' : '') . $this->quoteName($tableName));

		$this->setQuery($query);

		$this->execute();

		return $this;
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		$this->setQuery('USE ' . $this->quoteName($database));

		$this->execute();

		return $this;
	}

	/**
	 * 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 (string) or
boolean false if not supported.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function getCollation()
	{
		$this->connect();

		// Attempt to get the database collation by accessing the server system
variable.
		$this->setQuery('SHOW VARIABLES LIKE
"collation_database"');
		$result = $this->loadObject();

		if (property_exists($result, 'Value'))
		{
			return $result->Value;
		}
		else
		{
			return false;
		}
	}

	/**
	 * 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()
	{
		$this->connect();

		// Attempt to get the database collation by accessing the server system
variable.
		$this->setQuery('SHOW VARIABLES LIKE
"collation_connection"');
		$result = $this->loadObject();

		if (property_exists($result, 'Value'))
		{
			return $result->Value;
		}
		else
		{
			return false;
		}
	}

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function getTableCreate($tables)
	{
		$this->connect();

		// Initialise variables.
		$result = array();

		// Sanitize input to an array and iterate over the list.
		settype($tables, 'array');

		foreach ($tables as $table)
		{
			$this->setQuery('SHOW CREATE TABLE ' .
$this->quoteName($table));

			$row = $this->loadRow();

			// Populate the result array based on the create statements.
			$result[$table] = $row[1];
		}

		return $result;
	}

	/**
	 * 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   3.4
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$result = array();

		// Set the query to get the table fields statement.
		$this->setQuery('SHOW FULL COLUMNS FROM ' .
$this->quoteName($table));

		$fields = $this->loadObjectList();

		// If we only want the type as the value add just that to the list.
		if ($typeOnly)
		{
			foreach ($fields as $field)
			{
				$result[$field->Field] = preg_replace('/[(0-9)]/',
'', $field->Type);
			}
		}
		// If we want the whole field data object add that to the list.
		else
		{
			foreach ($fields as $field)
			{
				$result[$field->Field] = $field;
			}
		}

		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   3.4
	 * @throws  RuntimeException
	 */
	public function getTableKeys($table)
	{
		$this->connect();

		// Get the details columns information.
		$this->setQuery('SHOW KEYS FROM ' .
$this->quoteName($table));

		$keys = $this->loadObjectList();

		return $keys;
	}

	/**
	 * Method to get an array of all tables in the database.
	 *
	 * @return  array  An array of all the tables in the database.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function getTableList()
	{
		$this->connect();

		// Set the query to get the tables statement.
		$this->setQuery('SHOW TABLES');
		$tables = $this->loadColumn();

		return $tables;
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $table  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriverPdomysql  Returns this object to support
chaining.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function lockTable($table)
	{
		$this->setQuery('LOCK TABLES ' . $this->quoteName($table)
. ' WRITE')->execute();

		return $this;
	}

	/**
	 * 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 MySQL.
	 * @param   string  $prefix    Not used by MySQL.
	 *
	 * @return  JDatabaseDriverPdomysql  Returns this object to support
chaining.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		$this->setQuery('RENAME TABLE ' .
$this->quoteName($oldTable) . ' TO ' .
$this->quoteName($newTable));

		$this->execute();

		return $this;
	}

	/**
	 * Method to escape a string for usage in an SQL statement.
	 *
	 * Oracle escaping reference:
	 *
http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
	 *
	 * SQLite escaping notes:
	 * http://www.sqlite.org/faq.html#q14
	 *
	 * Method body is as implemented by the Zend Framework
	 *
	 * Note: Using query objects with bound variables is
	 * preferable to the below.
	 *
	 * @param   string   $text   The string to be escaped.
	 * @param   boolean  $extra  Unused optional parameter to provide extra
escaping.
	 *
	 * @return  string  The escaped string.
	 *
	 * @since   3.4
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$this->connect();

		$result = substr($this->connection->quote($text), 1, -1);

		if ($extra)
		{
			$result = addcslashes($result, '%_');
		}

		return $result;
	}

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriverPdomysql  Returns this object to support
chaining.
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function unlockTables()
	{
		$this->setQuery('UNLOCK TABLES')->execute();

		return $this;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function transactionCommit($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionCommit($toSavepoint);
		}
		else
		{
			$this->transactionDepth--;
		}
	}

	/**
	 * Method to roll back a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, rollback to the last
savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function transactionRollback($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionRollback($toSavepoint);
		}
		else
		{
			$savepoint = 'SP_' . ($this->transactionDepth - 1);
			$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));

			if ($this->execute())
			{
				$this->transactionDepth--;
			}
		}
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @throws  RuntimeException
	 */
	public function transactionStart($asSavepoint = false)
	{
		$this->connect();

		if (!$asSavepoint || !$this->transactionDepth)
		{
			parent::transactionStart($asSavepoint);
		}
		else
		{
			$savepoint = 'SP_' . $this->transactionDepth;
			$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));

			if ($this->execute())
			{
				$this->transactionDepth++;
			}
		}
	}

	/**
	 * Internal function to check if profiling is available.
	 *
	 * @return  boolean
	 *
	 * @since   3.9.1
	 */
	private function hasProfiling()
	{
		$result = $this->setQuery("SHOW VARIABLES LIKE
'have_profiling'")->loadAssoc();

		return isset($result);
	}
}
PK��[��ggdriver/pgsql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL PDO Database Driver
 *
 * @link   https://www.php.net/manual/en/ref.pdo-mysql.php
 * @since  3.9.0
 */
class JDatabaseDriverPgsql extends JDatabaseDriverPdo
{
	/**
	 * The database driver name
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	public $name = 'pgsql';

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc. The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	protected $nameQuote = '"';

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	protected $nullDate = '1970-01-01 00:00:00';

	/**
	 * The minimum supported database version.
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	protected static $dbMinimum = '8.3.18';

	/**
	 * Operator used for concatenation
	 *
	 * @var    string
	 * @since  3.9.0
	 */
	protected $concat_operator = '||';

	/**
	 * Database object constructor
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since	3.9.0
	 */
	public function __construct($options)
	{
		$options['driver']   = 'pgsql';
		$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'] : '';
		$options['port']     = isset($options['port']) ?
$options['port'] : null;

		// Finalize initialization
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.9.0
	 * @throws  \RuntimeException
	 */
	public function connect()
	{
		if ($this->getConnection())
		{
			return;
		}

		parent::connect();

		$this->setQuery('SET standard_conforming_strings =
off')->execute();
	}

	/**
	 * 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	true
	 *
	 * @since   3.9.0
	 * @throws  \RuntimeException
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $this->quoteName($tableName))->execute();

		return true;
	}

	/**
	 * 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   3.9.0
	 * @throws  \RuntimeException
	 */
	public function getCollation()
	{
		$this->setQuery('SHOW LC_COLLATE');
		$array = $this->loadAssocList();

		return $array[0]['lc_collate'];
	}

	/**
	 * Method to get the database connection collation in use by sampling a
text field of a table in the database.
	 *
	 * @return  mixed  The collation in use by the database connection
(string) or boolean false if not supported.
	 *
	 * @since   3.9.0
	 * @throws  \RuntimeException
	 */
	public function getConnectionCollation()
	{
		$this->setQuery('SHOW LC_COLLATE');
		$array = $this->loadAssocList();

		return $array[0]['lc_collate'];
	}

	/**
	 * Internal function to get the name of the default schema for the current
PostgreSQL connection.
	 * That is the schema where tables are created by Joomla.
	 *
	 * @return  string
	 *
	 * @since   3.9.24
	 */
	private function getDefaultSchema()
	{

		// Supported since PostgreSQL 7.3
		$this->setQuery('SELECT (current_schemas(false))[1]');
		return $this->loadResult();

	}
	
	/**
	 * 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 string because this function is not supported
by PostgreSQL.
	 *
	 * @since   3.9.0
	 * @throws  \RuntimeException
	 */
	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   3.9.0
	 * @throws  \RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$result = array();

		$tableSub = $this->replacePrefix($table);

		$defaultSchema = $this->getDefaultSchema();
		
		$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 = ' . $this->quote($defaultSchema) . ')
				)
			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   3.9.0
	 * @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, true))
		{
			// 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'
			);

			return $this->loadObjectList();
		}

		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   3.9.0
	 * @throws  \RuntimeException
	 */
	public function getTableList()
	{
		$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);

		return $this->loadColumn();
	}

	/**
	 * 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   3.9.0
	 * @throws  \RuntimeException
	 */
	public function getTableSequences($table)
	{
		// To check if table exists and prevent SQL injection
		$tableList = $this->getTableList();

		if (in_array($table, $tableList, true))
		{
			$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')
				->leftJoin("pg_depend d ON d.objid = s.oid AND d.classid =
'pg_class'::regclass AND d.refclassid =
'pg_class'::regclass")
				->leftJoin('pg_class t ON t.oid = d.refobjid')
				->leftJoin('pg_namespace n ON n.oid = t.relnamespace')
				->leftJoin('pg_attribute a ON a.attrelid = t.oid AND a.attnum =
d.refobjsubid')
				->leftJoin('information_schema.sequences AS info ON
info.sequence_name = s.relname')
				->where('s.relkind = ' . $this->quote('S') .
' AND d.deptype = ' . $this->quote('a') . ' AND
t.relname = ' . $this->quote($table));
			$this->setQuery($query);

			return $this->loadObjectList();
		}

		return false;
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $tableName  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriverPgsql  Returns this object to support chaining.
	 *
	 * @since   3.9.0
	 * @throws  \RuntimeException
	 */
	public function lockTable($tableName)
	{
		$this->transactionStart();
		$this->setQuery('LOCK TABLE ' .
$this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE
MODE')->execute();

		return $this;
	}

	/**
	 * 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  JDatabaseDriverPgsql  Returns this object to support chaining.
	 *
	 * @since   3.9.0
	 * @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, true))
		{
			// Origin Table not found
			throw new \RuntimeException('Table not found in Postgresql
database.');
		}

		// Rename indexes
		$subQuery = $this->getQuery(true)
			->select('indexrelid')
			->from('pg_index, pg_class')
			->where('pg_class.relname = ' . $this->quote($oldTable))
			->where('pg_class.oid = pg_index.indrelid');

		$this->setQuery(
			$this->getQuery(true)
				->select('relname')
				->from('pg_class')
				->where('oid IN (' . (string) $subQuery . ')')
		);

		$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))->execute();
		}

		// Rename sequences
		$subQuery = $this->getQuery(true)
			->select('oid')
			->from('pg_namespace')
			->where('nspname NOT LIKE ' .
$this->quote('pg_%'))
			->where('nspname != ' .
$this->quote('information_schema'));

		$this->setQuery(
			$this->getQuery(true)
				->select('relname')
				->from('pg_class')
				->where('relkind = ' . $this->quote('S'))
				->where('relnamespace IN (' . (string) $subQuery .
')')
				->where('relname LIKE ' .
$this->quote("%$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))->execute();
		}

		// Rename table
		$this->setQuery('ALTER TABLE ' . $this->escape($oldTable)
. ' RENAME TO ' . $this->escape($newTable))->execute();

		return true;
	}

	/**
	 * 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  $fieldName   The table field's name.
	 * @param   string  $fieldValue  The variable value to quote and return.
	 *
	 * @return  string  The quoted string.
	 *
	 * @since   3.9.0
	 */
	public function sqlValue($columns, $fieldName, $fieldValue)
	{
		switch ($columns[$fieldName])
		{
			case 'boolean':
				$val = 'NULL';

				if ($fieldValue === 't' || $fieldValue === true ||
$fieldValue === 1 || $fieldValue === '1')
				{
					$val = 'TRUE';
				}
				elseif ($fieldValue === 'f' || $fieldValue === false ||
$fieldValue === 0 || $fieldValue === '0')
				{
					$val = 'FALSE';
				}

				break;

			case 'bigint':
			case 'bigserial':
			case 'integer':
			case 'money':
			case 'numeric':
			case 'real':
			case 'smallint':
			case 'serial':
			case 'numeric,':
				$val = $fieldValue === '' ? 'NULL' : $fieldValue;
				break;

			case 'date':
			case 'timestamp without time zone':
				if (empty($fieldValue))
				{
					$fieldValue = $this->getNullDate();
				}

				$val = $this->quote($fieldValue);

				break;

			default:
				$val = $this->quote($fieldValue);
				break;
		}

		return $val;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.9.0
	 * @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   3.9.0
	 * @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   3.9.0
	 * @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++;
		}
	}

	/**
	 * 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   3.9.0
	 * @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)
		{
			// Skip columns that don't exist in the table.
			if (!array_key_exists($k, $columns))
			{
				continue;
			}

			// Only process non-null scalars.
			if (is_array($v) || is_object($v) || $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);

		$query->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   3.9.0
	 */
	public static function isSupported()
	{
		return class_exists('PDO') &&
in_array('pgsql', PDO::getAvailableDrivers(), true);
	}

	/**
	 * Returns an array containing database's table list.
	 *
	 * @return  array  The database's table list.
	 *
	 * @since   3.9.0
	 */
	public function showTables()
	{
		$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);

		return $this->loadColumn();
	}

	/**
	 * 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   3.9.0
	 */
	public function getStringPositionSql($substring, $string)
	{
		$this->setQuery("SELECT POSITION($substring IN $string)");
		$position = $this->loadRow();

		return $position['position'];
	}

	/**
	 * Generate a random value
	 *
	 * @return  float  The random generated number
	 *
	 * @since   3.9.0
	 */
	public function getRandom()
	{
		$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   3.9.0
	 */
	public function getAlterDbCharacterSet($dbName)
	{
		return 'ALTER DATABASE ' . $this->quoteName($dbName) .
' SET CLIENT_ENCODING TO ' . $this->quote('UTF8');
	}

	/**
	 * 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   3.9.0
	 */
	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  $sql     The SQL statement to prepare.
	 * @param   string  $prefix  The common table prefix.
	 *
	 * @return  string  The processed SQL statement.
	 *
	 * @since   3.9.0
	 */
	public function replacePrefix($sql, $prefix = '#__')
	{
		$sql = trim($sql);

		if (strpos($sql, '\''))
		{
			// Sequence name quoted with ' ' but need to be replaced
			if (strpos($sql, 'currval'))
			{
				$sql = explode('currval', $sql);

				for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax;
$nIndex += 2)
				{
					$sql[$nIndex] = str_replace($prefix, $this->tablePrefix,
$sql[$nIndex]);
				}

				$sql = implode('currval', $sql);
			}

			// Sequence name quoted with ' ' but need to be replaced
			if (strpos($sql, 'nextval'))
			{
				$sql = explode('nextval', $sql);

				for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax;
$nIndex += 2)
				{
					$sql[$nIndex] = str_replace($prefix, $this->tablePrefix,
$sql[$nIndex]);
				}

				$sql = implode('nextval', $sql);
			}

			// Sequence name quoted with ' ' but need to be replaced
			if (strpos($sql, 'setval'))
			{
				$sql = explode('setval', $sql);

				for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax;
$nIndex += 2)
				{
					$sql[$nIndex] = str_replace($prefix, $this->tablePrefix,
$sql[$nIndex]);
				}

				$sql = implode('setval', $sql);
			}

			$explodedQuery = explode('\'', $sql);

			for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex <
$nIndexMax; $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, $sql);
		}

		return $replacedQuery;
	}

	/**
	 * Unlocks tables in the database, this command does not exist in
PostgreSQL, it is automatically done on commit or rollback.
	 *
	 * @return  JDatabaseDriverPgsql  Returns this object to support chaining.
	 *
	 * @since   3.9.0
	 * @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
	 *
	 * @since   3.9.0
	 * @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)
		{
			// Skip columns that don't exist in the table.
			if (! array_key_exists($k, $columns))
			{
				continue;
			}

			// Only process scalars that are not internal fields.
			if (is_array($v) || is_object($v) || $k[0] === '_')
			{
				continue;
			}

			// Set the primary key to the WHERE clause instead of a field to update.
			if (in_array($k, $key, true))
			{
				$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 do not want to update nulls then ignore
this field.
				if (!$nulls)
				{
					continue;
				}

				// If the value is null and we want to update nulls then set it.
				$val = 'NULL';
			}
			else
			// The field is not null so we prep it for update.
			{
				$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();
	}

	/**
	 * Quotes a binary string to database requirements for use in database
queries.
	 *
	 * @param   mixed  $data  A binary string to quote.
	 *
	 * @return  string  The binary quoted input string.
	 *
	 * @since   3.9.12
	 */
	public function quoteBinary($data)
	{
		return "decode('" . bin2hex($data) . "',
'hex')";
	}
}
PK��[�t�֠֠driver/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL database driver
 *
 * @since       3.0.0
 * @deprecated  4.0  Use PDO PostgreSQL instead
 */
class JDatabaseDriverPostgresql extends JDatabaseDriver
{
	/**
	 * The database driver name
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	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  3.0.0
	 */
	protected $nameQuote = '"';

	/**
	 * The null/zero date string
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nullDate = '1970-01-01 00:00:00';

	/**
	 * The minimum supported database version.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected static $dbMinimum = '8.3.18';

	/**
	 * Operator used for concatenation
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $concat_operator = '||';

	/**
	 * JDatabaseDriverPostgresqlQuery object returned by getQuery
	 *
	 * @var    JDatabaseQueryPostgresql
	 * @since  3.0.0
	 */
	protected $queryObject = null;

	/**
	 * Database object constructor
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since	3.0.0
	 */
	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'] : '';
		$options['port'] = (isset($options['port'])) ?
$options['port'] : null;

		// Finalize initialization
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		// Make sure the postgresql extension for PHP is installed and enabled.
		if (!self::isSupported())
		{
			throw new JDatabaseExceptionUnsupported('The pgsql extension for
PHP is not installed or enabled.');
		}

		/*
		 * pg_connect() takes the port as separate argument. Therefore, we
		 * have to extract it from the host string (if provided).
		 */

		// Check for empty port
		if (!$this->options['port'])
		{
			// Port is empty or not set via options, check for port annotation (:)
in the host string
			$tmp = substr(strstr($this->options['host'],
':'), 1);

			if (!empty($tmp))
			{
				// Get the port number
				if (is_numeric($tmp))
				{
					$this->options['port'] = $tmp;
				}

				// Extract the host name
				$this->options['host'] =
substr($this->options['host'], 0,
strlen($this->options['host']) - (strlen($tmp) + 1));

				// This will take care of the following notation: ":5432"
				if ($this->options['host'] === '')
				{
					$this->options['host'] = 'localhost';
				}
			}
			// No port annotation (:) found, setting port to default PostgreSQL port
5432
			else
			{
				$this->options['port'] = '5432';
			}
		}

		// Build the DSN for the connection.
		$dsn = '';

		if (!empty($this->options['host']))
		{
			$dsn .= "host={$this->options['host']}
port={$this->options['port']} ";
		}

		$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 JDatabaseExceptionConnecting('Error connecting to PGSQL
database.');
		}

		pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT);
		pg_query($this->connection, 'SET
standard_conforming_strings=off');
		pg_query($this->connection, 'SET
escape_string_warning=off');
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	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   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$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   3.0.0
	 */
	public static function test()
	{
		return function_exists('pg_connect');
	}

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return	boolean
	 *
	 * @since	3.0.0
	 */
	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   3.0.0
	 * @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   3.0.0
	 */
	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   3.0.0
	 * @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 an 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   3.0.0
	 */
	public function getNumRows($cur = null)
	{
		$this->connect();

		return pg_num_rows((int) $cur ? $cur : $this->cursor);
	}

	/**
	 * Get the current or query, or new JDatabaseQuery object.
	 *
	 * @param   boolean  $new    False to return the last query set, True to
return a new JDatabaseQuery object.
	 * @param   boolean  $asObj  False to return last query as string, true to
get JDatabaseQueryPostgresql object.
	 *
	 * @return  JDatabaseQuery  The current query object or a new object
extending the JDatabaseQuery class.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getQuery($new = false, $asObj = false)
	{
		if ($new)
		{
			$this->queryObject = new JDatabaseQueryPostgresql($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   3.0.0
	 */
	public function getTableCreate($tables)
	{
		return '';
	}

	/**
	 * Retrieves field information about a given table.
	 *
	 * @param   string   $table     The name of the database table. For
PostgreSQL may start with a schema.
	 * @param   boolean  $typeOnly  True to only return field types.
	 *
	 * @return  array  An array of fields for the database table.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$result = array();
		$tableSub = $this->replacePrefix($table);
		$fn = explode('.', $tableSub);

		if (count($fn) === 2)
		{
			$schema = $fn[0];
			$tableSub = $fn[1];
		}
		else
		{
			$schema = $this->getDefaultSchema();
		}

		$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 = ' . $this->quote($schema) . ')
				)
			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   3.0.0
	 * @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   3.0.0
	 * @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   3.0.0
	 * @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   3.0.0
	 */
	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   3.0.0
	 */
	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  JDatabaseDriverPostgresql  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @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   3.0.0
	 * @throws  RuntimeException
	 */
	public function execute()
	{
		$this->connect();

		// 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 JDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
		{
			$query .= ' LIMIT ' . $this->limit . ' OFFSET ' .
$this->offset;
		}

		if (!is_resource($this->connection))
		{
			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
			throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
		}

		// 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;

			JLog::add($query, JLog::DEBUG, 'databasequery');

			$this->timings[] = microtime(true);

			if (is_object($this->cursor))
			{
				// Avoid warning if result already freed by third-party library
				@$this->freeResult();
			}

			$memoryBefore = memory_get_usage();
		}

		// 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();
			}

			$this->callStacks[count($this->callStacks) -
1][0]['memory'] = array(
				$memoryBefore,
				memory_get_usage(),
				is_resource($this->cursor) ? $this->getNumRows($this->cursor)
: null,
			);
		}

		// 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();

			// 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();

					// Throw the normal query exception.
					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

					throw new JDatabaseExceptionExecuting($query, $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.
				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

				throw new JDatabaseExceptionExecuting($query, $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  JDatabaseDriverPostgresql  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @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   3.0.0
	 */
	public function select($database)
	{
		return true;
	}

	/**
	 * Custom settings for UTF support
	 *
	 * @return  integer  Zero on success, -1 on failure
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		$this->connect();

		if (!function_exists('pg_set_client_encoding'))
		{
			return -1;
		}

		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  $fieldName   The table field's name.
	 * @param   string  $fieldValue  The variable value to quote and return.
	 *
	 * @return  string  The quoted string.
	 *
	 * @since   3.0.0
	 */
	public function sqlValue($columns, $fieldName, $fieldValue)
	{
		switch ($columns[$fieldName])
		{
			case 'boolean':
				$val = 'NULL';

				if ($fieldValue == 't')
				{
					$val = 'TRUE';
				}
				elseif ($fieldValue == 'f')
				{
					$val = 'FALSE';
				}

				break;

			case 'bigint':
			case 'bigserial':
			case 'integer':
			case 'money':
			case 'numeric':
			case 'real':
			case 'smallint':
			case 'serial':
			case 'numeric,':
				$val = strlen($fieldValue) == 0 ? 'NULL' : $fieldValue;
				break;

			case 'date':
			case 'timestamp without time zone':
				if (empty($fieldValue))
				{
					$fieldValue = $this->getNullDate();
				}

				$val = $this->quote($fieldValue);
				break;

			default:
				$val = $this->quote($fieldValue);
				break;
		}

		return $val;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @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   3.0.0
	 * @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   3.0.0
	 * @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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 * @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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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, $nIndexMax = count($query); $nIndex < $nIndexMax;
$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, $nIndexMax = count($query); $nIndex < $nIndexMax;
$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, $nIndexMax = count($query); $nIndex < $nIndexMax;
$nIndex += 2)
				{
					$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
				}

				$query = implode('setval', $query);
			}

			$explodedQuery = explode('\'', $query);

			for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex <
$nIndexMax; $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   3.0.0
	 */
	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   3.0.0
	 */
	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  JDatabaseDriverPostgresql  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @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   3.0.0
	 * @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
	 *
	 * @throws  \JDatabaseExceptionExecuting  Thrown if the global cursor is
false indicating a query failed
	 */
	protected function getErrorNumber()
	{
		if ($this->cursor === false)
		{
			$this->errorMsg = pg_last_error($this->connection);

			throw new JDatabaseExceptionExecuting($this->sql,
$this->errorMsg);
		}

		return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE)
. ' ';
	}

	/**
	 * Return the actual SQL Error message
	 *
	 * @return  string  The SQL Error message
	 *
	 * @since   3.4.6
	 */
	protected function getErrorMessage()
	{
		$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);
		}

		return $errorMessage;
	}

	/**
	 * Get the query strings to alter the character set and collation of a
table.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string[]  The queries required to alter the table's
character set and collation
	 *
	 * @since   CMS 3.5.0
	 */
	public function getAlterTableCharacterSet($tableName)
	{
		return array();
	}

	/**
	 * Return the query string to create new Database.
	 * Each database driver, other than MySQL, need to override this member to
return correct string.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 *                   This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 */
	protected function getCreateDatabaseQuery($options, $utf)
	{
		return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
	}

	/**
	 * Quotes a binary string to database requirements for use in database
queries.
	 *
	 * @param   mixed  $data  A binary string to quote.
	 *
	 * @return  string  The binary quoted input string.
	 *
	 * @since   3.9.12
	 */
	public function quoteBinary($data)
	{
		return "decode('" . bin2hex($data) . "',
'hex')";
	}

	/**
	 * Internal function to get the name of the default schema for the current
PostgreSQL connection.
	 * That is the schema where tables are created by Joomla.
	 *
	 * @return  string
	 *
	 * @since   3.9.24
	 */
	private function getDefaultSchema()
	{

		// Supported since PostgreSQL 7.3
		$this->setQuery('SELECT (current_schemas(false))[1]');
		return $this->loadResult();

	}
}
PK��[�KKXRRdriver/sqlazure.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQL Server database driver
 *
 * @link  
https://azure.microsoft.com/en-us/documentation/services/sql-database/
 * @since  3.0.0
 */
class JDatabaseDriverSqlazure extends JDatabaseDriverSqlsrv
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'sqlazure';
}
PK��[�s��//driver/sqlite.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQLite database driver
 *
 * @link   https://www.php.net/pdo
 * @since  3.0.0
 */
class JDatabaseDriverSqlite extends JDatabaseDriverPdo
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'sqlite';

	/**
	 * The type of the database server family supported by this driver.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType = 'sqlite';

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc. The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nameQuote = '`';

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		parent::connect();

		$this->connection->sqliteCreateFunction(
			'ROW_NUMBER',
			function($init = null)
			{
				static $rownum, $partition;

				if ($init !== null)
				{
					$rownum = $init;
					$partition = null;

					return $rownum;
				}

				$args = func_get_args();
				array_shift($args);

				$partitionBy = $args ? implode(',', $args) : null;

				if ($partitionBy === null || $partitionBy === $partition)
				{
					$rownum++;
				}
				else
				{
					$rownum    = 1;
					$partition = $partitionBy;
				}

				return $rownum;
			}
		);
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		$this->freeResult();

		$this->connection = null;
	}

	/**
	 * 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  JDatabaseDriverSqlite  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->connect();

		$query = $this->getQuery(true);

		$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $query->quoteName($tableName));

		$this->execute();

		return $this;
	}

	/**
	 * Method to escape a string for usage in an SQLite statement.
	 *
	 * Note: Using query objects with bound variables is
	 * preferable to the below.
	 *
	 * @param   string   $text   The string to be escaped.
	 * @param   boolean  $extra  Unused optional parameter to provide extra
escaping.
	 *
	 * @return  string  The escaped string.
	 *
	 * @since   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		return SQLite3::escapeString($text);
	}

	/**
	 * 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   3.0.0
	 */
	public function getCollation()
	{
		return $this->charset;
	}

	/**
	 * 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 $this->charset;
	}

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * Note: Doesn't appear to have support in SQLite
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableCreate($tables)
	{
		$this->connect();

		// Sanitize input to an array and iterate over the list.
		settype($tables, 'array');

		return $tables;
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$this->connect();

		$columns = array();
		$query = $this->getQuery(true);

		$fieldCasing = $this->getOption(PDO::ATTR_CASE);

		$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);

		$table = strtoupper($table);

		$query->setQuery('pragma table_info(' . $table .
')');

		$this->setQuery($query);
		$fields = $this->loadObjectList();

		if ($typeOnly)
		{
			foreach ($fields as $field)
			{
				$columns[$field->NAME] = $field->TYPE;
			}
		}
		else
		{
			foreach ($fields as $field)
			{
				// Do some dirty translation to MySQL output.
				// TODO: Come up with and implement a standard across databases.
				$columns[$field->NAME] = (object) array(
					'Field' => $field->NAME,
					'Type' => $field->TYPE,
					'Null' => ($field->NOTNULL == '1' ?
'NO' : 'YES'),
					'Default' => $field->DFLT_VALUE,
					'Key' => ($field->PK != '0' ?
'PRI' : ''),
				);
			}
		}

		$this->setOption(PDO::ATTR_CASE, $fieldCasing);

		return $columns;
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableKeys($table)
	{
		$this->connect();

		$keys = array();
		$query = $this->getQuery(true);

		$fieldCasing = $this->getOption(PDO::ATTR_CASE);

		$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);

		$table = strtoupper($table);
		$query->setQuery('pragma table_info( ' . $table .
')');

		// $query->bind(':tableName', $table);

		$this->setQuery($query);
		$rows = $this->loadObjectList();

		foreach ($rows as $column)
		{
			if ($column->PK == 1)
			{
				$keys[$column->NAME] = $column;
			}
		}

		$this->setOption(PDO::ATTR_CASE, $fieldCasing);

		return $keys;
	}

	/**
	 * Method to get an array of all tables in the database (schema).
	 *
	 * @return  array   An array of all the tables in the database.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableList()
	{
		$this->connect();

		$type = 'table';

		$query = $this->getQuery(true)
			->select('name')
			->from('sqlite_master')
			->where('type = :type')
			->bind(':type', $type)
			->order('name');

		$this->setQuery($query);

		$tables = $this->loadColumn();

		return $tables;
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.0.0
	 */
	public function getVersion()
	{
		$this->connect();

		$this->setQuery('SELECT sqlite_version()');

		return $this->loadResult();
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		return true;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * Returns false automatically for the Oracle driver since
	 * you can only set the character set when the connection
	 * is created.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		$this->connect();

		return false;
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $table  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriverSqlite  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function lockTable($table)
	{
		return $this;
	}

	/**
	 * 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 Sqlite.
	 * @param   string  $prefix    Not used by Sqlite.
	 *
	 * @return  JDatabaseDriverSqlite  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		$this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME
TO ' . $newTable)->execute();

		return $this;
	}

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriverSqlite  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function unlockTables()
	{
		return $this;
	}

	/**
	 * Test to see if the PDO ODBC connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return class_exists('PDO') &&
in_array('sqlite', PDO::getAvailableDrivers());
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionCommit($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionCommit($toSavepoint);
		}
		else
		{
			$this->transactionDepth--;
		}
	}

	/**
	 * Method to roll back a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, rollback to the last
savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionRollback($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			parent::transactionRollback($toSavepoint);
		}
		else
		{
			$savepoint = 'SP_' . ($this->transactionDepth - 1);
			$this->setQuery('ROLLBACK TO ' .
$this->quoteName($savepoint));

			if ($this->execute())
			{
				$this->transactionDepth--;
			}
		}
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.1.4
	 * @throws  RuntimeException
	 */
	public function transactionStart($asSavepoint = false)
	{
		$this->connect();

		if (!$asSavepoint || !$this->transactionDepth)
		{
			parent::transactionStart($asSavepoint);
		}

		$savepoint = 'SP_' . $this->transactionDepth;
		$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));

		if ($this->execute())
		{
			$this->transactionDepth++;
		}
	}

	/**
	 * Get the query strings to alter the character set and collation of a
table.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string[]  The queries required to alter the table's
character set and collation
	 *
	 * @since   CMS 3.5.0
	 */
	public function getAlterTableCharacterSet($tableName)
	{
		return array();
	}

	/**
	 * Return the query string to create new Database.
	 * Each database driver, other than MySQL, need to override this member to
return correct string.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 *                   This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 */
	protected function getCreateDatabaseQuery($options, $utf)
	{
		return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
	}
}
PK��[�)�$l$ldriver/sqlsrv.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQL Server database driver
 *
 * @link   https://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx
 * @since  3.0.0
 */
class JDatabaseDriverSqlsrv extends JDatabaseDriver
{
	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	public $name = 'sqlsrv';

	/**
	 * The type of the database server family supported by this driver.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType = 'mssql';

	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc.  The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nameQuote = '[]';

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $nullDate = '1900-01-01 00:00:00';

	/**
	 * @var    string  The minimum supported database version.
	 * @since  3.0.0
	 */
	protected static $dbMinimum = '10.50.1600.1';

	/**
	 * Test to see if the SQLSRV connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   3.0.0
	 */
	public static function isSupported()
	{
		return function_exists('sqlsrv_connect');
	}

	/**
	 * Constructor.
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since   3.0.0
	 */
	public function __construct($options)
	{
		// Get some basic values from the 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'] : '';
		$options['select'] = (isset($options['select'])) ?
(bool) $options['select'] : true;

		// Finalize initialisation
		parent::__construct($options);
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function connect()
	{
		if ($this->connection)
		{
			return;
		}

		// Build the connection configuration array.
		$config = array(
			'Database' => $this->options['database'],
			'uid' => $this->options['user'],
			'pwd' => $this->options['password'],
			'CharacterSet' => 'UTF-8',
			'ReturnDatesAsStrings' => true,
		);

		// Make sure the SQLSRV extension for PHP is installed and enabled.
		if (!self::isSupported())
		{
			throw new JDatabaseExceptionUnsupported('The sqlsrv extension for
PHP is not installed or enabled..');
		}

		// Attempt to connect to the server.
		if (!($this->connection = @
sqlsrv_connect($this->options['host'], $config)))
		{
			throw new JDatabaseExceptionConnecting('Database sqlsrv_connect
failed, ' . print_r(sqlsrv_errors(), true));
		}

		// Make sure that DB warnings are not returned as errors.
		sqlsrv_configure('WarningsReturnAsErrors', 0);

		// If auto-select is enabled select the given database.
		if ($this->options['select'] &&
!empty($this->options['database']))
		{
			$this->select($this->options['database']);
		}

		// Set charactersets.
		$this->utf = $this->setUtf();

		// Set QUOTED_IDENTIFIER always ON
		sqlsrv_query($this->connection, 'SET QUOTED_IDENTIFIER ON');
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public function disconnect()
	{
		// Close the connection.
		if (is_resource($this->connection))
		{
			foreach ($this->disconnectHandlers as $h)
			{
				call_user_func_array($h, array( &$this));
			}

			sqlsrv_close($this->connection);
		}

		$this->connection = null;
	}

	/**
	 * Get table constraints
	 *
	 * @param   string  $tableName  The name of the database table.
	 *
	 * @return  array  Any constraints available for the table.
	 *
	 * @since   3.0.0
	 */
	protected function getTableConstraints($tableName)
	{
		$this->connect();

		$query = $this->getQuery(true);

		$this->setQuery(
			'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME = ' . $query->quote($tableName)
		);

		return $this->loadColumn();
	}

	/**
	 * Rename constraints.
	 *
	 * @param   array   $constraints  Array(strings) of table constraints
	 * @param   string  $prefix       A string
	 * @param   string  $backup       A string
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	protected function renameConstraints($constraints = array(), $prefix =
null, $backup = null)
	{
		$this->connect();

		foreach ($constraints as $constraint)
		{
			$this->setQuery('sp_rename ' . $constraint . ','
. str_replace($prefix, $backup, $constraint));
			$this->execute();
		}
	}

	/**
	 * Method to escape a string for usage in an SQL statement.
	 *
	 * The escaping for MSSQL isn't handled in the driver though that
would be nice.  Because of this we need
	 * to handle the escaping ourselves.
	 *
	 * @param   string   $text   The string to be escaped.
	 * @param   boolean  $extra  Optional parameter to provide extra escaping.
	 *
	 * @return  string  The escaped string.
	 *
	 * @since   3.0.0
	 */
	public function escape($text, $extra = false)
	{
		if (is_int($text))
		{
			return $text;
		}

		if (is_float($text))
		{
			// Force the dot as a decimal point.
			return str_replace(',', '.', $text);
		}

		$result = str_replace("'", "''",
$text);

		// SQL Server does not accept NULL byte in query string
		$result = str_replace("\0", "' + CHAR(0) +
N'", $result);

		// Fix for SQL Server escape sequence, see
https://support.microsoft.com/en-us/kb/164291
		$result = str_replace(
			array("\\\n",     "\\\r",    
"\\\\\r\r\n"),
			array("\\\\\n\n", "\\\\\r\r",
"\\\\\r\n\r\n"),
			$result
		);

		if ($extra)
		{
			// Escape special chars
			$result = str_replace(
				array('[',   '_',   '%'),
				array('[[]', '[_]', '[%]'),
				$result
			);
		}

		return $result;
	}

	/**
	 * Quotes and optionally escapes a string to database requirements for use
in database queries.
	 *
	 * @param   mixed    $text    A string or an array of strings to quote.
	 * @param   boolean  $escape  True (default) to escape the string, false
to leave it unchanged.
	 *
	 * @return  string  The quoted input string.
	 *
	 * @note    Accepting an array of strings was added in 3.1.4.
	 * @since   1.7.0
	 */
	public function quote($text, $escape = true)
	{
		if (is_array($text))
		{
			return parent::quote($text, $escape);
		}

		// To support unicode on MSSQL we have to add prefix N
		return 'N\'' . ($escape ? $this->escape($text) : $text)
. '\'';
	}

	/**
	 * Quotes a binary string to database requirements for use in database
queries.
	 *
	 * @param   mixed  $data  A binary string to quote.
	 *
	 * @return  string  The binary quoted input string.
	 *
	 * @since   3.9.12
	 */
	public function quoteBinary($data)
	{
		// ODBC syntax for hexadecimal literals
		return '0x' . bin2hex($data);
	}

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return  boolean  True if connected to the database engine.
	 *
	 * @since   3.0.0
	 */
	public function connected()
	{
		// TODO: Run a blank query here
		return true;
	}

	/**
	 * 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  JDatabaseDriverSqlsrv  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 */
	public function dropTable($tableName, $ifExists = true)
	{
		$this->connect();

		$query = $this->getQuery(true);

		if ($ifExists)
		{
			$this->setQuery(
				'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME = ' . $query->quote($tableName) . ') DROP TABLE
' . $tableName
			);
		}
		else
		{
			$this->setQuery('DROP TABLE ' . $tableName);
		}

		$this->execute();

		return $this;
	}

	/**
	 * Get the number of affected rows for the previous executed SQL
statement.
	 *
	 * @return  integer  The number of affected rows.
	 *
	 * @since   3.0.0
	 */
	public function getAffectedRows()
	{
		$this->connect();

		return sqlsrv_rows_affected($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   3.0.0
	 */
	public function getCollation()
	{
		// TODO: Not fake this
		return 'MSSQL UTF-8 (UCS2)';
	}

	/**
	 * 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()
	{
		// TODO: Not fake this
		return 'MSSQL UTF-8 (UCS2)';
	}

	/**
	 * Get the number of returned rows for the previous executed SQL
statement.
	 *
	 * @param   resource  $cursor  An optional database cursor resource to
extract the row count from.
	 *
	 * @return  integer   The number of returned rows.
	 *
	 * @since   3.0.0
	 */
	public function getNumRows($cursor = null)
	{
		$this->connect();

		return sqlsrv_num_rows($cursor ? $cursor : $this->cursor);
	}

	/**
	 * Retrieves field information about the given tables.
	 *
	 * @param   mixed    $table     A table name
	 * @param   boolean  $typeOnly  True to only return field types.
	 *
	 * @return  array  An array of fields.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableColumns($table, $typeOnly = true)
	{
		$result = array();

		$table_temp = $this->replacePrefix((string) $table);

		// Set the query to get the table fields statement.
		$this->setQuery(
			'SELECT column_name as Field, data_type as Type, is_nullable as
\'Null\', column_default as \'Default\'' .
			' FROM information_schema.columns WHERE table_name = ' .
$this->quote($table_temp)
		);
		$fields = $this->loadObjectList();

		// If we only want the type as the value add just that to the list.
		if ($typeOnly)
		{
			foreach ($fields as $field)
			{
				$result[$field->Field] = preg_replace('/[(0-9)]/',
'', $field->Type);
			}
		}
		// If we want the whole field data object add that to the list.
		else
		{
			foreach ($fields as $field)
			{
				$field->Default =
preg_replace("/(^(\(\(|\('|\(N'|\()|(('\)|(?<!\()\)\)|\))$))/i",
'', $field->Default);
				$result[$field->Field] = $field;
			}
		}

		return $result;
	}

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * This is unsupported by MSSQL.
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableCreate($tables)
	{
		$this->connect();

		return '';
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableKeys($table)
	{
		$this->connect();

		// TODO To implement.
		return array();
	}

	/**
	 * Method to get an array of all tables in the database.
	 *
	 * @return  array  An array of all the tables in the database.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getTableList()
	{
		$this->connect();

		// Set the query to get the tables statement.
		$this->setQuery('SELECT name FROM ' .
$this->getDatabase() . '.sys.Tables WHERE type =
\'U\';');
		$tables = $this->loadColumn();

		return $tables;
	}

	/**
	 * Get the version of the database connector.
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   3.0.0
	 */
	public function getVersion()
	{
		$this->connect();

		$version = sqlsrv_server_info($this->connection);

		return $version['SQLServerVersion'];
	}

	/**
	 * 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   3.0.0
	 * @throws  RuntimeException
	 */
	public function insertObject($table, &$object, $key = null)
	{
		$fields = array();
		$values = array();
		$statement = 'INSERT INTO ' . $this->quoteName($table) .
' (%s) VALUES (%s)';

		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;
			}

			if (!$this->checkFieldExists($table, $k))
			{
				continue;
			}

			if ($k[0] == '_')
			{
				// Internal field
				continue;
			}

			if ($k == $key && $key == 0)
			{
				continue;
			}

			$fields[] = $this->quoteName($k);
			$values[] = $this->Quote($v);
		}
		// Set the query and execute the insert.
		$this->setQuery(sprintf($statement, implode(',', $fields),
implode(',', $values)));

		if (!$this->execute())
		{
			return false;
		}

		$id = $this->insertid();

		if ($key && $id)
		{
			$object->$key = $id;
		}

		return true;
	}

	/**
	 * Method to get the auto-incremented value from the last INSERT
statement.
	 *
	 * @return  integer  The value of the auto-increment field from the last
inserted row.
	 *
	 * @since   3.0.0
	 */
	public function insertid()
	{
		$this->connect();

		// TODO: SELECT IDENTITY
		$this->setQuery('SELECT @@IDENTITY');

		return (int) $this->loadResult();
	}

	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 * @throws  Exception
	 */
	public function execute()
	{
		$this->connect();

		// 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 JDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
		{
			$query = $this->limit($query, $this->limit, $this->offset);
		}

		if (!is_resource($this->connection))
		{
			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
			throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
		}

		// 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;

			JLog::add($query, JLog::DEBUG, 'databasequery');

			$this->timings[] = microtime(true);
		}

		// SQLSrv_num_rows requires a static or keyset cursor.
		if (strncmp(ltrim(strtoupper($query)), 'SELECT',
strlen('SELECT')) == 0)
		{
			$array = array('Scrollable' => SQLSRV_CURSOR_KEYSET);
		}
		else
		{
			$array = array();
		}

		// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
		$this->cursor = @sqlsrv_query($this->connection, $query, array(),
$array);

		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();

			// 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)
				{
					// Get the error number and message.
					$this->errorNum = $this->getErrorNumber();
					$this->errorMsg = $this->getErrorMessage();

					// Throw the normal query exception.
					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

					throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum, $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.
				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');

				throw new JDatabaseExceptionExecuting($query, $this->errorMsg,
$this->errorNum);
			}
		}

		return $this->cursor;
	}

	/**
	 * 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   3.0.0
	 */
	public function replacePrefix($query, $prefix = '#__')
	{
		$query = trim($query);

		if (strpos($query, "'"))
		{
			$parts = explode("'", $query);

			for ($nIndex = 0, $size = count($parts); $nIndex < $size; $nIndex =
$nIndex + 2)
			{
				if (strpos($parts[$nIndex], $prefix) !== false)
				{
					$parts[$nIndex] = str_replace($prefix, $this->tablePrefix,
$parts[$nIndex]);
				}
			}

			$query = implode("'", $parts);
		}
		else
		{
			$query = str_replace($prefix, $this->tablePrefix, $query);
		}

		return $query;
	}

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function select($database)
	{
		$this->connect();

		if (!$database)
		{
			return false;
		}

		if (!sqlsrv_query($this->connection, 'USE ' . $database,
null, array('scrollable' => SQLSRV_CURSOR_STATIC)))
		{
			throw new JDatabaseExceptionConnecting('Could not connect to SQL
Server database.');
		}

		return true;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   3.0.0
	 */
	public function setUtf()
	{
		return false;
	}

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionCommit($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			if ($this->setQuery('COMMIT TRANSACTION')->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   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionRollback($toSavepoint = false)
	{
		$this->connect();

		if (!$toSavepoint || $this->transactionDepth <= 1)
		{
			if ($this->setQuery('ROLLBACK TRANSACTION')->execute())
			{
				$this->transactionDepth = 0;
			}

			return;
		}

		$savepoint = 'SP_' . ($this->transactionDepth - 1);
		$this->setQuery('ROLLBACK TRANSACTION ' .
$this->quoteName($savepoint));

		if ($this->execute())
		{
			$this->transactionDepth--;
		}
	}

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function transactionStart($asSavepoint = false)
	{
		$this->connect();

		if (!$asSavepoint || !$this->transactionDepth)
		{
			if ($this->setQuery('BEGIN TRANSACTION')->execute())
			{
				$this->transactionDepth = 1;
			}

			return;
		}

		$savepoint = 'SP_' . $this->transactionDepth;
		$this->setQuery('BEGIN TRANSACTION ' .
$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   3.0.0
	 */
	protected function fetchArray($cursor = null)
	{
		return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor,
SQLSRV_FETCH_NUMERIC);
	}

	/**
	 * 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   3.0.0
	 */
	protected function fetchAssoc($cursor = null)
	{
		return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor,
SQLSRV_FETCH_ASSOC);
	}

	/**
	 * 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   3.0.0
	 */
	protected function fetchObject($cursor = null, $class =
'stdClass')
	{
		// Class has to be loaded for sqlsrv on windows platform
		class_exists($class);

		return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $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   3.0.0
	 */
	protected function freeResult($cursor = null)
	{
		sqlsrv_free_stmt($cursor ? $cursor : $this->cursor);
	}

	/**
	 * Method to check and see if a field exists in a table.
	 *
	 * @param   string  $table  The table in which to verify the field.
	 * @param   string  $field  The field to verify.
	 *
	 * @return  boolean  True if the field exists in the table.
	 *
	 * @since   3.0.0
	 */
	protected function checkFieldExists($table, $field)
	{
		$this->connect();

		$table = $this->replacePrefix((string) $table);
		$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_NAME = '$table' AND COLUMN_NAME = '$field' ORDER
BY ORDINAL_POSITION";
		$this->setQuery($query);

		if ($this->loadResult())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	/**
	 * Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior
for scrolling through a result set.
	 *
	 * @param   string   $query   The SQL statement to process.
	 * @param   integer  $limit   The maximum affected rows to set.
	 * @param   integer  $offset  The affected row offset to set.
	 *
	 * @return  string   The processed SQL statement.
	 *
	 * @since   3.0.0
	 */
	protected function limit($query, $limit, $offset)
	{
		if ($limit)
		{
			$total = $offset + $limit;

			$position = stripos($query, 'SELECT');
			$distinct = stripos($query, 'SELECT DISTINCT');

			if ($position === $distinct)
			{
				$query = substr_replace($query, 'SELECT DISTINCT TOP ' .
(int) $total, $position, 15);
			}
			else
			{
				$query = substr_replace($query, 'SELECT TOP ' . (int) $total,
$position, 6);
			}
		}

		if (!$offset)
		{
			return $query;
		}

		return PHP_EOL
			. 'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0))
AS RowNumber FROM ('
			. $query
			. PHP_EOL . ') AS A) AS A WHERE RowNumber > ' . (int)
$offset;
	}

	/**
	 * 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    Table prefix
	 * @param   string  $prefix    For the table - used to rename constraints
in non-mysql databases
	 *
	 * @return  JDatabaseDriverSqlsrv  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		$constraints = array();

		if (!is_null($prefix) && !is_null($backup))
		{
			$constraints = $this->getTableConstraints($oldTable);
		}

		if (!empty($constraints))
		{
			$this->renameConstraints($constraints, $prefix, $backup);
		}

		$this->setQuery("sp_rename '" . $oldTable .
"', '" . $newTable . "'");

		return $this->execute();
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $tableName  The name of the table to lock.
	 *
	 * @return  JDatabaseDriverSqlsrv  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function lockTable($tableName)
	{
		return $this;
	}

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriverSqlsrv  Returns this object to support
chaining.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function unlockTables()
	{
		return $this;
	}

	/**
	 * Return the actual SQL Error number
	 *
	 * @return  integer  The SQL Error number
	 *
	 * @since   3.4.6
	 */
	protected function getErrorNumber()
	{
		$errors = sqlsrv_errors();

		return $errors[0]['code'];
	}

	/**
	 * Return the actual SQL Error message
	 *
	 * @return  string  The SQL Error message
	 *
	 * @since   3.4.6
	 */
	protected function getErrorMessage()
	{
		$errors       = sqlsrv_errors();
		$errorMessage = (string) $errors[0]['message'];

		// Replace the Databaseprefix with `#__` if we are not in Debug
		if (!$this->debug)
		{
			$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
		}

		return $errorMessage;
	}

	/**
	 * Get the query strings to alter the character set and collation of a
table.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string[]  The queries required to alter the table's
character set and collation
	 *
	 * @since   CMS 3.5.0
	 */
	public function getAlterTableCharacterSet($tableName)
	{
		return array();
	}

	/**
	 * Return the query string to create new Database.
	 * Each database driver, other than MySQL, need to override this member to
return correct string.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 *                   This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 */
	protected function getCreateDatabaseQuery($options, $utf)
	{
		return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
	}
}
PK��[��UD�D�
driver.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Driver Class
 *
 * @since  3.0.0
 *
 * @method   string|array  q()   q($text, $escape = true)  Alias for quote
method
 * @method   string|array  qn()  qn($name, $as = null)     Alias for
quoteName method
 */
abstract class JDatabaseDriver extends JDatabase implements
JDatabaseInterface
{
	/**
	 * The name of the database.
	 *
	 * @var    string
	 * @since  2.5.0
	 */
	private $_database;

	/**
	 * The name of the database driver.
	 *
	 * @var    string
	 * @since  1.7.0
	 */
	public $name;

	/**
	 * The type of the database server family supported by this driver.
Examples: mysql, oracle, postgresql, mssql,
	 * sqlite.
	 *
	 * @var    string
	 * @since  CMS 3.5.0
	 */
	public $serverType;

	/**
	 * @var    resource  The database connection resource.
	 * @since  1.7.0
	 */
	protected $connection;

	/**
	 * @var    integer  The number of SQL statements executed by the database
driver.
	 * @since  1.7.0
	 */
	protected $count = 0;

	/**
	 * @var    resource  The database connection cursor from the last query.
	 * @since  1.7.0
	 */
	protected $cursor;

	/**
	 * @var    boolean  The database driver debugging state.
	 * @since  1.7.0
	 */
	protected $debug = false;

	/**
	 * @var    integer  The affected row limit for the current SQL statement.
	 * @since  1.7.0
	 */
	protected $limit = 0;

	/**
	 * @var    array  The log of executed SQL statements by the database
driver.
	 * @since  1.7.0
	 */
	protected $log = array();

	/**
	 * @var    array  The log of executed SQL statements timings (start and
stop microtimes) by the database driver.
	 * @since  CMS 3.1.2
	 */
	protected $timings = array();

	/**
	 * @var    array  The log of executed SQL statements timings (start and
stop microtimes) by the database driver.
	 * @since  CMS 3.1.2
	 */
	protected $callStacks = array();

	/**
	 * @var    string  The character(s) used to quote SQL statement names such
as table names or field names,
	 *                 etc.  The child classes should define this as
necessary.  If a single character string the
	 *                 same character is used for both sides of the quoted
name, else the first character will be
	 *                 used for the opening quote and the second for the
closing quote.
	 * @since  1.7.0
	 */
	protected $nameQuote;

	/**
	 * @var    string  The null or zero representation of a timestamp for the
database driver.  This should be
	 *                 defined in child classes to hold the appropriate value
for the engine.
	 * @since  1.7.0
	 */
	protected $nullDate;

	/**
	 * @var    integer  The affected row offset to apply for the current SQL
statement.
	 * @since  1.7.0
	 */
	protected $offset = 0;

	/**
	 * @var    array  Passed in upon instantiation and saved.
	 * @since  1.7.0
	 */
	protected $options;

	/**
	 * @var    JDatabaseQuery|string  The current SQL statement to execute.
	 * @since  1.7.0
	 */
	protected $sql;

	/**
	 * @var    string  The common database table prefix.
	 * @since  1.7.0
	 */
	protected $tablePrefix;

	/**
	 * @var    boolean  True if the database engine supports UTF-8 character
encoding.
	 * @since  1.7.0
	 */
	protected $utf = true;

	/**
	 * @var    boolean  True if the database engine supports UTF-8 Multibyte
(utf8mb4) character encoding.
	 * @since  CMS 3.5.0
	 */
	protected $utf8mb4 = false;

	/**
	 * @var         integer  The database error number
	 * @since       1.7.0
	 * @deprecated  3.0.0
	 */
	protected $errorNum = 0;

	/**
	 * @var         string  The database error message
	 * @since       1.7.0
	 * @deprecated  3.0.0
	 */
	protected $errorMsg;

	/**
	 * @var    array  JDatabaseDriver instances container.
	 * @since  1.7.0
	 */
	protected static $instances = array();

	/**
	 * @var    string  The minimum supported database version.
	 * @since  3.0.0
	 */
	protected static $dbMinimum;

	/**
	 * @var    integer  The depth of the current transaction.
	 * @since  3.1.4
	 */
	protected $transactionDepth = 0;

	/**
	 * @var    callable[]  List of callables to call just before disconnecting
database
	 * @since  CMS 3.1.2
	 */
	protected $disconnectHandlers = array();

	/**
	 * Get a list of available database connectors.  The list will only be
populated with connectors that both
	 * the class exists and the static test method returns true.  This gives
us the ability to have a multitude
	 * of connector classes that are self-aware as to whether or not they are
able to be used on a given system.
	 *
	 * @return  array  An array of available database connectors.
	 *
	 * @since   1.7.0
	 */
	public static function getConnectors()
	{
		$connectors = array();

		// Get an iterator and loop trough the driver classes.
		$iterator = new DirectoryIterator(__DIR__ . '/driver');

		/* @type  $file  DirectoryIterator */
		foreach ($iterator as $file)
		{
			$fileName = $file->getFilename();

			// Only load for php files.
			if (!$file->isFile() || $file->getExtension() != 'php')
			{
				continue;
			}

			// Derive the class name from the type.
			$class = str_ireplace('.php', '',
'JDatabaseDriver' . ucfirst(trim($fileName)));

			// If the class doesn't exist we have nothing left to do but look
at the next type. We did our best.
			if (!class_exists($class))
			{
				continue;
			}

			// Sweet!  Our class exists, so now we just need to know if it passes
its test method.
			if ($class::isSupported())
			{
				// Connector names should not have file extensions.
				$connectors[] = str_ireplace('.php', '',
$fileName);
			}
		}

		return $connectors;
	}

	/**
	 * Method to return a JDatabaseDriver instance based on the given options.
 There are three global options and then
	 * the rest are specific to the database driver.  The 'driver'
option defines which JDatabaseDriver class is
	 * used for the connection -- the default is 'mysqli'.  The
'database' option determines which database is to
	 * be used for the connection.  The 'select' option determines
whether the connector should automatically select
	 * the chosen database.
	 *
	 * Instances are unique to the given options and new objects are only
created when a unique options array is
	 * passed into the method.  This ensures that we don't end up with
unnecessary database connection resources.
	 *
	 * @param   array  $options  Parameters to be passed to the database
driver.
	 *
	 * @return  JDatabaseDriver  A database object.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public static function getInstance($options = array())
	{
		// Sanitize the database connector options.
		$options['driver']   = (isset($options['driver'])) ?
preg_replace('/[^A-Z0-9_\.-]/i', '',
$options['driver']) : 'mysqli';
		$options['database'] = (isset($options['database']))
? $options['database'] : null;
		$options['select']   = (isset($options['select'])) ?
$options['select'] : true;

		// If the selected driver is `mysql` and we are on PHP 7 or greater,
switch to the `mysqli` driver.
		if ($options['driver'] === 'mysql' &&
PHP_MAJOR_VERSION >= 7)
		{
			// Check if we have support for the other MySQL drivers
			$mysqliSupported   = JDatabaseDriverMysqli::isSupported();
			$pdoMysqlSupported = JDatabaseDriverPdomysql::isSupported();

			// If neither is supported, then the user cannot use MySQL; throw an
exception
			if (!$mysqliSupported && !$pdoMysqlSupported)
			{
				throw new JDatabaseExceptionUnsupported(
					'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver.'
					. ' Also, this system does not support MySQLi or PDO MySQL. 
Cannot instantiate database driver.'
				);
			}

			// Prefer MySQLi as it is a closer replacement for the removed MySQL
driver, otherwise use the PDO driver
			if ($mysqliSupported)
			{
				JLog::add(
					'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver.  Trying `mysqli` instead.',
					JLog::WARNING,
					'deprecated'
				);

				$options['driver'] = 'mysqli';
			}
			else
			{
				JLog::add(
					'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver.  Trying `pdomysql` instead.',
					JLog::WARNING,
					'deprecated'
				);

				$options['driver'] = 'pdomysql';
			}
		}

		// Get the options signature for the database connector.
		$signature = md5(serialize($options));

		// If we already have a database connector instance for these options
then just use that.
		if (empty(self::$instances[$signature]))
		{
			// Derive the class name from the driver.
			$class = 'JDatabaseDriver' .
ucfirst(strtolower($options['driver']));

			// If the class still doesn't exist we have nothing left to do but
throw an exception.  We did our best.
			if (!class_exists($class))
			{
				throw new JDatabaseExceptionUnsupported(sprintf('Unable to load
Database Driver: %s', $options['driver']));
			}

			// Create our new JDatabaseDriver connector based on the options given.
			try
			{
				$instance = new $class($options);
			}
			catch (RuntimeException $e)
			{
				throw new JDatabaseExceptionConnecting(sprintf('Unable to connect
to the Database: %s', $e->getMessage()), $e->getCode(), $e);
			}

			// Set the new connector to the global instances based on signature.
			self::$instances[$signature] = $instance;
		}

		return self::$instances[$signature];
	}

	/**
	 * Splits a string of multiple queries into an array of individual
queries.
	 * Single line or line end comments and multi line comments are stripped
off.
	 *
	 * @param   string  $sql  Input SQL string with which to split into
individual queries.
	 *
	 * @return  array  The queries from the input string separated into an
array.
	 *
	 * @since   1.7.0
	 */
	public static function splitSql($sql)
	{
		$start = 0;
		$open = false;
		$comment = false;
		$endString = '';
		$end = strlen($sql);
		$queries = array();
		$query = '';

		for ($i = 0; $i < $end; $i++)
		{
			$current = substr($sql, $i, 1);
			$current2 = substr($sql, $i, 2);
			$current3 = substr($sql, $i, 3);
			$lenEndString = strlen($endString);
			$testEnd = substr($sql, $i, $lenEndString);

			if ($current == '"' || $current == "'" ||
$current2 == '--'
				|| ($current2 == '/*' && $current3 != '/*!'
&& $current3 != '/*+')
				|| ($current == '#' && $current3 != '#__')
				|| ($comment && $testEnd == $endString))
			{
				// Check if quoted with previous backslash
				$n = 2;

				while (substr($sql, $i - $n + 1, 1) == '\\' && $n
< $i)
				{
					$n++;
				}

				// Not quoted
				if ($n % 2 == 0)
				{
					if ($open)
					{
						if ($testEnd == $endString)
						{
							if ($comment)
							{
								$comment = false;
								if ($lenEndString > 1)
								{
									$i += ($lenEndString - 1);
									$current = substr($sql, $i, 1);
								}
								$start = $i + 1;
							}
							$open = false;
							$endString = '';
						}
					}
					else
					{
						$open = true;
						if ($current2 == '--')
						{
							$endString = "\n";
							$comment = true;
						}
						elseif ($current2 == '/*')
						{
							$endString = '*/';
							$comment = true;
						}
						elseif ($current == '#')
						{
							$endString = "\n";
							$comment = true;
						}
						else
						{
							$endString = $current;
						}
						if ($comment && $start < $i)
						{
							$query = $query . substr($sql, $start, ($i - $start));
						}
					}
				}
			}

			if ($comment)
			{
				$start = $i + 1;
			}

			if (($current == ';' && !$open) || $i == $end - 1)
			{
				if ($start <= $i)
				{
					$query = $query . substr($sql, $start, ($i - $start + 1));
				}
				$query = trim($query);

				if ($query)
				{
					if (($i == $end - 1) && ($current != ';'))
					{
						$query = $query . ';';
					}
					$queries[] = $query;
				}

				$query = '';
				$start = $i + 1;
			}
		}

		return $queries;
	}

	/**
	 * Magic method to provide method alias support for quote() and
quoteName().
	 *
	 * @param   string  $method  The called method.
	 * @param   array   $args    The array of arguments passed to the method.
	 *
	 * @return  mixed  The aliased method's return value or null.
	 *
	 * @since   1.7.0
	 */
	public function __call($method, $args)
	{
		if (empty($args))
		{
			return;
		}

		switch ($method)
		{
			case 'q':
				return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
				break;
			case 'qn':
				return $this->quoteName($args[0], isset($args[1]) ? $args[1] :
null);
				break;
		}
	}

	/**
	 * Constructor.
	 *
	 * @param   array  $options  List of options used to configure the
connection
	 *
	 * @since   1.7.0
	 */
	public function __construct($options)
	{
		// Initialise object variables.
		$this->_database = (isset($options['database'])) ?
$options['database'] : '';

		$this->tablePrefix = (isset($options['prefix'])) ?
$options['prefix'] : 'jos_';
		$this->count = 0;
		$this->errorNum = 0;
		$this->log = array();

		// Set class options.
		$this->options = $options;
	}

	/**
	 * Alter database's character set, obtaining query string from
protected member.
	 *
	 * @param   string  $dbName  The database name that will be altered
	 *
	 * @return  string  The query that alter the database query string
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function alterDbCharacterSet($dbName)
	{
		if (is_null($dbName))
		{
			throw new RuntimeException('Database name must not be null.');
		}

		$this->setQuery($this->getAlterDbCharacterSet($dbName));

		return $this->execute();
	}

	/**
	 * Alter a table's character set, obtaining an array of queries to do
so from a protected method. The conversion is
	 * wrapped in a transaction, if supported by the database driver.
Otherwise the table will be locked before the
	 * conversion. This prevents data corruption.
	 *
	 * @param   string   $tableName  The name of the table to alter
	 * @param   boolean  $rethrow    True to rethrow database exceptions.
Default: false (exceptions are suppressed)
	 *
	 * @return  boolean  True if successful
	 *
	 * @since   CMS 3.5.0
	 * @throws  RuntimeException  If the table name is empty
	 * @throws  Exception  Relayed from the database layer if a database error
occurs and $rethrow == true
	 */
	public function alterTableCharacterSet($tableName, $rethrow = false)
	{
		if (is_null($tableName))
		{
			throw new RuntimeException('Table name must not be null.');
		}

		$queries = $this->getAlterTableCharacterSet($tableName);

		if (empty($queries))
		{
			return false;
		}

		$hasTransaction = true;

		try
		{
			$this->transactionStart();
		}
		catch (Exception $e)
		{
			$hasTransaction = false;
			$this->lockTable($tableName);
		}

		foreach ($queries as $query)
		{
			try
			{
				$this->setQuery($query)->execute();
			}
			catch (Exception $e)
			{
				if ($hasTransaction)
				{
					$this->transactionRollback();
				}
				else
				{
					$this->unlockTables();
				}

				if ($rethrow)
				{
					throw $e;
				}

				return false;
			}
		}

		if ($hasTransaction)
		{
			try
			{
				$this->transactionCommit();
			}
			catch (Exception $e)
			{
				$this->transactionRollback();

				if ($rethrow)
				{
					throw $e;
				}

				return false;
			}
		}
		else
		{
			$this->unlockTables();
		}

		return true;
	}

	/**
	 * Connects to the database if needed.
	 *
	 * @return  void  Returns void if the database connected successfully.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	abstract public function connect();

	/**
	 * Determines if the connection to the server is active.
	 *
	 * @return  boolean  True if connected to the database engine.
	 *
	 * @since   1.7.0
	 */
	abstract public function connected();

	/**
	 * Create a new database using information from $options object, obtaining
query string
	 * from protected member.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 * 									This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 * @throws  RuntimeException
	 */
	public function createDatabase($options, $utf = true)
	{
		if (is_null($options))
		{
			throw new RuntimeException('$options object must not be
null.');
		}
		elseif (empty($options->db_name))
		{
			throw new RuntimeException('$options object must have db_name
set.');
		}
		elseif (empty($options->db_user))
		{
			throw new RuntimeException('$options object must have db_user
set.');
		}

		$this->setQuery($this->getCreateDatabaseQuery($options, $utf));

		return $this->execute();
	}

	/**
	 * Destructor.
	 *
	 * @since   3.8.0
	 */
	public function __destruct()
	{
		$this->disconnect();
	}

	/**
	 * Disconnects the database.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	abstract public function disconnect();

	/**
	 * Adds a function callable just before disconnecting the database.
Parameter of the callable is $this JDatabaseDriver
	 *
	 * @param   callable  $callable  Function to call in disconnect() method
just before disconnecting from database
	 *
	 * @return  void
	 *
	 * @since   CMS 3.1.2
	 */
	public function addDisconnectHandler($callable)
	{
		$this->disconnectHandlers[] = $callable;
	}

	/**
	 * Drops a table from the database.
	 *
	 * @param   string   $table     The name of the database table to drop.
	 * @param   boolean  $ifExists  Optionally specify that the table must
exist before it is dropped.
	 *
	 * @return  JDatabaseDriver     Returns this object to support chaining.
	 *
	 * @since   2.5.0
	 * @throws  RuntimeException
	 */
	abstract public function dropTable($table, $ifExists = true);

	/**
	 * Escapes 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   1.7.0
	 */
	abstract public function escape($text, $extra = false);

	/**
	 * 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   1.7.0
	 */
	abstract protected function fetchArray($cursor = null);

	/**
	 * 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   1.7.0
	 */
	abstract protected function fetchAssoc($cursor = null);

	/**
	 * 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   1.7.0
	 */
	abstract protected function fetchObject($cursor = null, $class =
'stdClass');

	/**
	 * 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   1.7.0
	 */
	abstract protected function freeResult($cursor = null);

	/**
	 * Get the number of affected rows for the previous executed SQL
statement.
	 *
	 * @return  integer  The number of affected rows.
	 *
	 * @since   1.7.0
	 */
	abstract public function getAffectedRows();

	/**
	 * Return 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   3.0.1
	 */
	public function getAlterDbCharacterSet($dbName)
	{
		$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';

		return 'ALTER DATABASE ' . $this->quoteName($dbName) .
' CHARACTER SET `' . $charset . '`';
	}

	/**
	 * Get the query strings to alter the character set and collation of a
table.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string[]  The queries required to alter the table's
character set and collation
	 *
	 * @since   CMS 3.5.0
	 */
	public function getAlterTableCharacterSet($tableName)
	{
		$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
		$collation = $charset . '_unicode_ci';

		$quotedTableName = $this->quoteName($tableName);

		$queries = array();
		$queries[] = "ALTER TABLE $quotedTableName CONVERT TO CHARACTER SET
$charset COLLATE $collation";

		/**
		 * We also need to convert each text column, modifying their character
set and collation. This allows us to
		 * change, for example, a utf8_bin collated column to a utf8mb4_bin
collated column.
		 */
		$sql = "SHOW FULL COLUMNS FROM $quotedTableName";
		$this->setQuery($sql);
		$columns = $this->loadAssocList();
		$columnMods = array();

		if (is_array($columns))
		{
			foreach ($columns as $column)
			{
				// Make sure we are redefining only columns which do support a
collation
				$col = (object) $column;

				if (empty($col->Collation))
				{
					continue;
				}

				// Default new collation: utf8_unicode_ci or utf8mb4_unicode_ci
				$newCollation = $charset . '_unicode_ci';
				$collationParts = explode('_', $col->Collation);

				/**
				 * If the collation is in the form charset_collationType_ci or
charset_collationType we have to change
				 * the charset but leave the collationType intact (e.g. utf8_bin must
become utf8mb4_bin, NOT
				 * utf8mb4_general_ci).
				 */
				if (count($collationParts) >= 2)
				{
					$ci = array_pop($collationParts);
					$collationType = array_pop($collationParts);
					$newCollation = $charset . '_' . $collationType .
'_' . $ci;

					/**
					 * When the last part of the old collation is not _ci we have a
charset_collationType format,
					 * something like utf8_bin. Therefore the new collation only has *two*
parts.
					 */
					if ($ci != 'ci')
					{
						$newCollation = $charset . '_' . $ci;
					}
				}

				// If the old and new collation is the same we don't have to
change the collation type
				if (strtolower($newCollation) == strtolower($col->Collation))
				{
					continue;
				}

				$null = $col->Null == 'YES' ? 'NULL' : 'NOT
NULL';
				$default = is_null($col->Default) ? '' : "DEFAULT
'" . $this->q($col->Default) . "'";
				$columnMods[] = "MODIFY COLUMN `{$col->Field}` {$col->Type}
CHARACTER SET $charset COLLATE $newCollation $null $default";
			}
		}

		if (count($columnMods))
		{
			$queries[] = "ALTER TABLE $quotedTableName " .
				implode(',', $columnMods) .
				" CHARACTER SET $charset COLLATE $collation";
		}

		return $queries;
	}

	/**
	 * Automatically downgrade a CREATE TABLE or ALTER TABLE query from
utf8mb4 (UTF-8 Multibyte) to plain utf8. Used
	 * when the server doesn't support UTF-8 Multibyte.
	 *
	 * @param   string  $query  The query to convert
	 *
	 * @return  string  The converted query
	 */
	public function convertUtf8mb4QueryToUtf8($query)
	{
		if ($this->hasUTF8mb4Support())
		{
			return $query;
		}

		// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
		if (!preg_match('/^(ALTER|CREATE)\s+TABLE\s+/i', $query))
		{
			return $query;
		}

		// Don't do preg replacement if string does not exist
		if (stripos($query, 'utf8mb4') === false)
		{
			return $query;
		}

		// Replace utf8mb4 with utf8 if not within single or double quotes or
name quotes
		return
preg_replace('/[`"\'][^`"\']*[`"\'](*SKIP)(*FAIL)|utf8mb4/i',
'utf8', $query);
	}

	/**
	 * Return the query string to create new Database.
	 * Each database driver, other than MySQL, need to override this member to
return correct string.
	 *
	 * @param   stdClass  $options  Object used to pass user and database name
to database driver.
	 *                   This object must have "db_name" and
"db_user" set.
	 * @param   boolean   $utf      True if the database supports the UTF-8
character set.
	 *
	 * @return  string  The query that creates database
	 *
	 * @since   3.0.1
	 */
	protected function getCreateDatabaseQuery($options, $utf)
	{
		if ($utf)
		{
			$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
			$collation = $charset . '_unicode_ci';

			return 'CREATE DATABASE ' .
$this->quoteName($options->db_name) . ' CHARACTER SET `' .
$charset . '` COLLATE `' . $collation . '`';
		}

		return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
	}

	/**
	 * 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   1.7.0
	 */
	abstract public function getCollation();

	/**
	 * 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 '';
	}

	/**
	 * Method that provides access to the underlying database connection.
Useful for when you need to call a
	 * proprietary method such as postgresql's lo_* methods.
	 *
	 * @return  resource  The underlying database connection resource.
	 *
	 * @since   1.7.0
	 */
	public function getConnection()
	{
		return $this->connection;
	}

	/**
	 * Get the total number of SQL statements executed by the database driver.
	 *
	 * @return  integer
	 *
	 * @since   1.7.0
	 */
	public function getCount()
	{
		return $this->count;
	}

	/**
	 * Gets the name of the database used by this connection.
	 *
	 * @return  string
	 *
	 * @since   2.5.0
	 */
	protected function getDatabase()
	{
		return $this->_database;
	}

	/**
	 * Returns a PHP date() function compliant date format for the database
driver.
	 *
	 * @return  string  The format string.
	 *
	 * @since   1.7.0
	 */
	public function getDateFormat()
	{
		return 'Y-m-d H:i:s';
	}

	/**
	 * Get the database driver SQL statement log.
	 *
	 * @return  array  SQL statements executed by the database driver.
	 *
	 * @since   1.7.0
	 */
	public function getLog()
	{
		return $this->log;
	}

	/**
	 * Get the database driver SQL statement log.
	 *
	 * @return  array  SQL statements executed by the database driver.
	 *
	 * @since   CMS 3.1.2
	 */
	public function getTimings()
	{
		return $this->timings;
	}

	/**
	 * Get the database driver SQL statement log.
	 *
	 * @return  array  SQL statements executed by the database driver.
	 *
	 * @since   CMS 3.1.2
	 */
	public function getCallStacks()
	{
		return $this->callStacks;
	}

	/**
	 * Get the minimum supported database version.
	 *
	 * @return  string  The minimum version number for the database driver.
	 *
	 * @since   3.0.0
	 */
	public function getMinimum()
	{
		return static::$dbMinimum;
	}

	/**
	 * Get the null or zero representation of a timestamp for the database
driver.
	 *
	 * @return  string  Null or zero representation of a timestamp.
	 *
	 * @since   1.7.0
	 */
	public function getNullDate()
	{
		return $this->nullDate;
	}

	/**
	 * Get the number of returned rows for the previous executed SQL
statement.
	 *
	 * @param   resource  $cursor  An optional database cursor resource to
extract the row count from.
	 *
	 * @return  integer   The number of returned rows.
	 *
	 * @since   1.7.0
	 */
	abstract public function getNumRows($cursor = null);

	/**
	 * Get the common table prefix for the database driver.
	 *
	 * @return  string  The common database table prefix.
	 *
	 * @since   1.7.0
	 */
	public function getPrefix()
	{
		return $this->tablePrefix;
	}

	/**
	 * Gets an exporter class object.
	 *
	 * @return  JDatabaseExporter  An exporter object.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getExporter()
	{
		// Derive the class name from the driver.
		$class = 'JDatabaseExporter' . ucfirst($this->name);

		// Make sure we have an exporter class for this driver.
		if (!class_exists($class))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported('Database Exporter not
found.');
		}

		$o = new $class;
		$o->setDbo($this);

		return $o;
	}

	/**
	 * Gets an importer class object.
	 *
	 * @return  JDatabaseImporter  An importer object.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getImporter()
	{
		// Derive the class name from the driver.
		$class = 'JDatabaseImporter' . ucfirst($this->name);

		// Make sure we have an importer class for this driver.
		if (!class_exists($class))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported('Database Importer not
found');
		}

		$o = new $class;
		$o->setDbo($this);

		return $o;
	}

	/**
	 * Get the name of the database driver. If $this->name is not set it
will try guessing the driver name from the
	 * class name.
	 *
	 * @return  string
	 *
	 * @since   CMS 3.5.0
	 */
	public function getName()
	{
		if (empty($this->name))
		{
			$className = get_class($this);
			$className = str_replace('JDatabaseDriver', '',
$className);
			$this->name = strtolower($className);
		}

		return $this->name;
	}

	/**
	 * Get the server family type, e.g. mysql, postgresql, oracle, sqlite,
mssql. If $this->serverType is not set it
	 * will attempt guessing the server family type from the driver name. If
this is not possible the driver name will
	 * be returned instead.
	 *
	 * @return  string
	 *
	 * @since   CMS 3.5.0
	 */
	public function getServerType()
	{
		if (empty($this->serverType))
		{
			$name = $this->getName();

			if (stristr($name, 'mysql') !== false)
			{
				$this->serverType = 'mysql';
			}
			elseif (stristr($name, 'postgre') !== false)
			{
				$this->serverType = 'postgresql';
			}
			elseif (stristr($name, 'pgsql') !== false)
			{
				$this->serverType = 'postgresql';
			}
			elseif (stristr($name, 'oracle') !== false)
			{
				$this->serverType = 'oracle';
			}
			elseif (stristr($name, 'sqlite') !== false)
			{
				$this->serverType = 'sqlite';
			}
			elseif (stristr($name, 'sqlsrv') !== false)
			{
				$this->serverType = 'mssql';
			}
			elseif (stristr($name, 'mssql') !== false)
			{
				$this->serverType = 'mssql';
			}
			else
			{
				$this->serverType = $name;
			}
		}

		return $this->serverType;
	}

	/**
	 * Get the current query object or a new JDatabaseQuery object.
	 *
	 * @param   boolean  $new  False to return the current query object, True
to return a new JDatabaseQuery object.
	 *
	 * @return  JDatabaseQuery  The current query object or a new object
extending the JDatabaseQuery class.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function getQuery($new = false)
	{
		if ($new)
		{
			// Derive the class name from the driver.
			$class = 'JDatabaseQuery' . ucfirst($this->name);

			// Make sure we have a query class for this driver.
			if (!class_exists($class))
			{
				// If it doesn't exist we are at an impasse so throw an exception.
				throw new JDatabaseExceptionUnsupported('Database Query Class not
found.');
			}

			return new $class($this);
		}
		else
		{
			return $this->sql;
		}
	}

	/**
	 * Get a new iterator on the current query.
	 *
	 * @param   string  $column  An option column to use as the iterator key.
	 * @param   string  $class   The class of object that is returned.
	 *
	 * @return  JDatabaseIterator  A new database iterator.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getIterator($column = null, $class = 'stdClass')
	{
		// Derive the class name from the driver.
		$iteratorClass = 'JDatabaseIterator' . ucfirst($this->name);

		// Make sure we have an iterator class for this driver.
		if (!class_exists($iteratorClass))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported(sprintf('class *%s* is not
defined', $iteratorClass));
		}

		// Return a new iterator
		return new $iteratorClass($this->execute(), $column, $class);
	}

	/**
	 * Retrieves field information about the given tables.
	 *
	 * @param   string   $table     The name of the database table.
	 * @param   boolean  $typeOnly  True (default) to only return field types.
	 *
	 * @return  array  An array of fields by table.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function getTableColumns($table, $typeOnly = true);

	/**
	 * Shows the table CREATE statement that creates the given tables.
	 *
	 * @param   mixed  $tables  A table name or a list of table names.
	 *
	 * @return  array  A list of the create SQL for the tables.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function getTableCreate($tables);

	/**
	 * Retrieves keys information about the given table.
	 *
	 * @param   string  $table  The name of the table.
	 *
	 * @return  array   An array of keys for the table.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function getTableKeys($table);

	/**
	 * Method to get an array of all tables in the database.
	 *
	 * @return  array  An array of all the tables in the database.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function getTableList();

	/**
	 * Determine whether or not the database engine supports UTF-8 character
encoding.
	 *
	 * @return  boolean  True if the database engine supports UTF-8 character
encoding.
	 *
	 * @since   1.7.0
	 * @deprecated 4.0 - Use hasUTFSupport() instead
	 */
	public function getUTFSupport()
	{
		JLog::add('JDatabaseDriver::getUTFSupport() is deprecated. Use
JDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING,
'deprecated');

		return $this->hasUTFSupport();
	}

	/**
	 * Determine whether or not the database engine supports UTF-8 character
encoding.
	 *
	 * @return  boolean  True if the database engine supports UTF-8 character
encoding.
	 *
	 * @since   3.0.0
	 */
	public function hasUTFSupport()
	{
		return $this->utf;
	}

	/**
	 * Determine whether the database engine support the UTF-8 Multibyte
(utf8mb4) character encoding. This applies to
	 * MySQL databases.
	 *
	 * @return  boolean  True if the database engine supports UTF-8 Multibyte.
	 *
	 * @since   CMS 3.5.0
	 */
	public function hasUTF8mb4Support()
	{
		return $this->utf8mb4;
	}

	/**
	 * Get the version of the database connector
	 *
	 * @return  string  The database connector version.
	 *
	 * @since   1.7.0
	 */
	abstract public function getVersion();

	/**
	 * Method to get the auto-incremented value from the last INSERT
statement.
	 *
	 * @return  mixed  The value of the auto-increment field from the last
inserted row.
	 *
	 * @since   1.7.0
	 */
	abstract public function insertid();

	/**
	 * 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   1.7.0
	 * @throws  RuntimeException
	 */
	public function insertObject($table, &$object, $key = null)
	{
		$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.
			if ($k[0] == '_')
			{
				continue;
			}

			// Prepare and sanitize the fields and values for the database query.
			$fields[] = $this->quoteName($k);
			$values[] = $this->quote($v);
		}

		// Create the base insert statement.
		$query = $this->getQuery(true)
			->insert($this->quoteName($table))
			->columns($fields)
			->values(implode(',', $values));

		// Set the query and execute the insert.
		$this->setQuery($query);

		if (!$this->execute())
		{
			return false;
		}

		// Update the primary key if it exists.
		$id = $this->insertid();

		if ($key && $id && is_string($key))
		{
			$object->$key = $id;
		}

		return true;
	}

	/**
	 * Method to check whether the installed database version is supported by
the database driver
	 *
	 * @return  boolean  True if the database version is supported
	 *
	 * @since   3.0.0
	 */
	public function isMinimumVersion()
	{
		return version_compare($this->getVersion(), static::$dbMinimum) >=
0;
	}

	/**
	 * Method to get the first row of the result set from the database query
as an associative array
	 * of ['field_name' => 'row_value'].
	 *
	 * @return  mixed  The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadAssoc()
	{
		$this->connect();

		$ret = null;

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get the first row from the result set as an associative array.
		if ($array = $this->fetchAssoc($cursor))
		{
			$ret = $array;
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $ret;
	}

	/**
	 * Method to get an array of the result set rows from the database query
where each row is an associative array
	 * of ['field_name' => 'row_value'].  The array of
rows can optionally be keyed by a field name, but defaults to
	 * a sequential numeric array.
	 *
	 * NOTE: Choosing to key the result array by a non-unique field name can
result in unwanted
	 * behavior and should be avoided.
	 *
	 * @param   string  $key     The name of a field on which to key the
result array.
	 * @param   string  $column  An optional column name. Instead of the whole
row, only this column value will be in
	 * the result array.
	 *
	 * @return  mixed   The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadAssocList($key = null, $column = null)
	{
		$this->connect();

		$array = array();

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get all of the rows from the result set.
		while ($row = $this->fetchAssoc($cursor))
		{
			$value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) :
$row;

			if ($key)
			{
				$array[$row[$key]] = $value;
			}
			else
			{
				$array[] = $value;
			}
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $array;
	}

	/**
	 * Method to get an array of values from the
<var>$offset</var> field in each row of the result set from
	 * the database query.
	 *
	 * @param   integer  $offset  The row offset to use to build the result
array.
	 *
	 * @return  mixed    The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadColumn($offset = 0)
	{
		$this->connect();

		$array = array();

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get all of the rows from the result set as arrays.
		while ($row = $this->fetchArray($cursor))
		{
			$array[] = $row[$offset];
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $array;
	}

	/**
	 * Method to get the next row in the result set from the database query as
an object.
	 *
	 * @param   string  $class  The class name to use for the returned row
object.
	 *
	 * @return  mixed   The result of the query as an array, false if there
are no more rows.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 * @deprecated  4.0 - Use getIterator() instead
	 */
	public function loadNextObject($class = 'stdClass')
	{
		JLog::add(__METHOD__ . '() is deprecated. Use
JDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
		$this->connect();

		static $cursor = null;

		// Execute the query and get the result set cursor.
		if (is_null($cursor))
		{
			if (!($cursor = $this->execute()))
			{
				return $this->errorNum ? null : false;
			}
		}

		// Get the next row from the result set as an object of type $class.
		if ($row = $this->fetchObject($cursor, $class))
		{
			return $row;
		}

		// Free up system resources and return.
		$this->freeResult($cursor);
		$cursor = null;

		return false;
	}

	/**
	 * Method to get the next row in the result set from the database query as
an array.
	 *
	 * @return  mixed  The result of the query as an array, false if there are
no more rows.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 * @deprecated  4.0 (CMS)  Use JDatabaseDriver::getIterator() instead
	 */
	public function loadNextRow()
	{
		JLog::add(__METHOD__ . '() is deprecated. Use
JDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
		$this->connect();

		static $cursor = null;

		// Execute the query and get the result set cursor.
		if (is_null($cursor))
		{
			if (!($cursor = $this->execute()))
			{
				return $this->errorNum ? null : false;
			}
		}

		// Get the next row from the result set as an object of type $class.
		if ($row = $this->fetchArray($cursor))
		{
			return $row;
		}

		// Free up system resources and return.
		$this->freeResult($cursor);
		$cursor = null;

		return false;
	}

	/**
	 * Method to get the first row of the result set from the database query
as an object.
	 *
	 * @param   string  $class  The class name to use for the returned row
object.
	 *
	 * @return  mixed   The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadObject($class = 'stdClass')
	{
		$this->connect();

		$ret = null;

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get the first row from the result set as an object of type $class.
		if ($object = $this->fetchObject($cursor, $class))
		{
			$ret = $object;
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $ret;
	}

	/**
	 * Method to get an array of the result set rows from the database query
where each row is an object.  The array
	 * of objects can optionally be keyed by a field name, but defaults to a
sequential numeric array.
	 *
	 * NOTE: Choosing to key the result array by a non-unique field name can
result in unwanted
	 * behavior and should be avoided.
	 *
	 * @param   string  $key    The name of a field on which to key the result
array.
	 * @param   string  $class  The class name to use for the returned row
objects.
	 *
	 * @return  mixed   The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadObjectList($key = '', $class =
'stdClass')
	{
		$this->connect();

		$array = array();

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get all of the rows from the result set as objects of type $class.
		while ($row = $this->fetchObject($cursor, $class))
		{
			if ($key)
			{
				$array[$row->$key] = $row;
			}
			else
			{
				$array[] = $row;
			}
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $array;
	}

	/**
	 * Method to get the first field of the first row of the result set from
the database query.
	 *
	 * @return  mixed  The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadResult()
	{
		$this->connect();

		$ret = null;

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get the first row from the result set as an array.
		if ($row = $this->fetchArray($cursor))
		{
			$ret = $row[0];
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $ret;
	}

	/**
	 * Method to get the first row of the result set from the database query
as an array.  Columns are indexed
	 * numerically so the first column in the result set would be accessible
via <var>$row[0]</var>, etc.
	 *
	 * @return  mixed  The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadRow()
	{
		$this->connect();

		$ret = null;

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get the first row from the result set as an array.
		if ($row = $this->fetchArray($cursor))
		{
			$ret = $row;
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $ret;
	}

	/**
	 * Method to get an array of the result set rows from the database query
where each row is an array.  The array
	 * of objects can optionally be keyed by a field offset, but defaults to a
sequential numeric array.
	 *
	 * NOTE: Choosing to key the result array by a non-unique field can result
in unwanted
	 * behavior and should be avoided.
	 *
	 * @param   integer  $index  The index of a field on which to key the
result array.
	 *
	 * @return  mixed   The return value or null if the query failed.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	public function loadRowList($index = null)
	{
		$this->connect();

		$array = array();

		// Execute the query and get the result set cursor.
		if (!($cursor = $this->execute()))
		{
			return;
		}

		// Get all of the rows from the result set as arrays.
		while ($row = $this->fetchArray($cursor))
		{
			if ($index !== null)
			{
				$array[$row[$index]] = $row;
			}
			else
			{
				$array[] = $row;
			}
		}

		// Free up system resources and return.
		$this->freeResult($cursor);

		return $array;
	}

	/**
	 * Locks a table in the database.
	 *
	 * @param   string  $tableName  The name of the table to unlock.
	 *
	 * @return  JDatabaseDriver     Returns this object to support chaining.
	 *
	 * @since   2.5.0
	 * @throws  RuntimeException
	 */
	abstract public function lockTable($tableName);

	/**
	 * Quotes and optionally escapes a string to database requirements for use
in database queries.
	 *
	 * @param   mixed    $text    A string or an array of strings to quote.
	 * @param   boolean  $escape  True (default) to escape the string, false
to leave it unchanged.
	 *
	 * @return  string|array  The quoted input.
	 *
	 * @note    Accepting an array of strings was added in 12.3.
	 * @since   1.7.0
	 */
	public function quote($text, $escape = true)
	{
		if (is_array($text))
		{
			foreach ($text as $k => $v)
			{
				$text[$k] = $this->quote($v, $escape);
			}

			return $text;
		}
		else
		{
			return '\'' . ($escape ? $this->escape($text) : $text)
. '\'';
		}
	}

	/**
	 * Quotes a binary string to database requirements for use in database
queries.
	 *
	 * @param   mixed  $data  A binary string to quote.
	 *
	 * @return  string  The binary quoted input string.
	 *
	 * @since   3.9.12
	 */
	public function quoteBinary($data)
	{
		// SQL standard syntax for hexadecimal literals
		return "X'" . bin2hex($data) . "'";
	}

	/**
	 * Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
	 * risks and reserved word conflicts.
	 *
	 * @param   mixed  $name  The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
	 *                        Each type supports dot-notation name.
	 * @param   mixed  $as    The AS query part associated to $name. It can be
string or array, in latter case it has to be
	 *                        same length of $name; if is null there will not
be any AS part for string or array element.
	 *
	 * @return  mixed  The quote wrapped name, same type of $name.
	 *
	 * @since   1.7.0
	 */
	public function quoteName($name, $as = null)
	{
		if (is_string($name))
		{
			$quotedName = $this->quoteNameStr(explode('.', $name));

			$quotedAs = '';

			if (!is_null($as))
			{
				settype($as, 'array');
				$quotedAs .= ' AS ' . $this->quoteNameStr($as);
			}

			return $quotedName . $quotedAs;
		}
		else
		{
			$fin = array();

			if (is_null($as))
			{
				foreach ($name as $str)
				{
					$fin[] = $this->quoteName($str);
				}
			}
			elseif (is_array($name) && (count($name) == count($as)))
			{
				$count = count($name);

				for ($i = 0; $i < $count; $i++)
				{
					$fin[] = $this->quoteName($name[$i], $as[$i]);
				}
			}

			return $fin;
		}
	}

	/**
	 * Quote strings coming from quoteName call.
	 *
	 * @param   array  $strArr  Array of strings coming from quoteName
dot-explosion.
	 *
	 * @return  string  Dot-imploded string of quoted parts.
	 *
	 * @since 1.7.3
	 */
	protected function quoteNameStr($strArr)
	{
		$parts = array();
		$q = $this->nameQuote;

		foreach ($strArr as $part)
		{
			if (is_null($part))
			{
				continue;
			}

			if (strlen($q) == 1)
			{
				$parts[] = $q . str_replace($q, $q . $q, $part) . $q;
			}
			else
			{
				$parts[] = $q[0] . str_replace($q[1], $q[1] . $q[1], $part) . $q[1];
			}
		}

		return implode('.', $parts);
	}

	/**
	 * This function replaces a string identifier
<var>$prefix</var> with the string held is the
	 * <var>tablePrefix</var> class variable.
	 *
	 * @param   string  $sql     The SQL statement to prepare.
	 * @param   string  $prefix  The common table prefix.
	 *
	 * @return  string  The processed SQL statement.
	 *
	 * @since   1.7.0
	 */
	public function replacePrefix($sql, $prefix = '#__')
	{
		$startPos = 0;
		$literal = '';

		$sql = trim($sql);
		$n = strlen($sql);

		while ($startPos < $n)
		{
			$ip = strpos($sql, $prefix, $startPos);

			if ($ip === false)
			{
				break;
			}

			$j = strpos($sql, "'", $startPos);
			$k = strpos($sql, '"', $startPos);

			if (($k !== false) && (($k < $j) || ($j === false)))
			{
				$quoteChar = '"';
				$j = $k;
			}
			else
			{
				$quoteChar = "'";
			}

			if ($j === false)
			{
				$j = $n;
			}

			$literal .= str_replace($prefix, $this->tablePrefix, substr($sql,
$startPos, $j - $startPos));
			$startPos = $j;

			$j = $startPos + 1;

			if ($j >= $n)
			{
				break;
			}

			// Quote comes first, find end of quote
			while (true)
			{
				$k = strpos($sql, $quoteChar, $j);
				$escaped = false;

				if ($k === false)
				{
					break;
				}

				$l = $k - 1;

				while ($l >= 0 && $sql[$l] == '\\')
				{
					$l--;
					$escaped = !$escaped;
				}

				if ($escaped)
				{
					$j = $k + 1;
					continue;
				}

				break;
			}

			if ($k === false)
			{
				// Error in the query - no end quote; ignore it
				break;
			}

			$literal .= substr($sql, $startPos, $k - $startPos + 1);
			$startPos = $k + 1;
		}

		if ($startPos < $n)
		{
			$literal .= substr($sql, $startPos, $n - $startPos);
		}

		return $literal;
	}

	/**
	 * 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    Table prefix
	 * @param   string  $prefix    For the table - used to rename constraints
in non-mysql databases
	 *
	 * @return  JDatabaseDriver    Returns this object to support chaining.
	 *
	 * @since   2.5.0
	 * @throws  RuntimeException
	 */
	abstract public function renameTable($oldTable, $newTable, $backup = null,
$prefix = null);

	/**
	 * Select a database for use.
	 *
	 * @param   string  $database  The name of the database to select for use.
	 *
	 * @return  boolean  True if the database was successfully selected.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function select($database);

	/**
	 * Sets the database debugging state for the driver.
	 *
	 * @param   boolean  $level  True to enable debugging.
	 *
	 * @return  boolean  The old debugging level.
	 *
	 * @since   1.7.0
	 * @deprecated  4.0  This will be removed in Joomla 4 without replacement
	 */
	public function setDebug($level)
	{
		$previous = $this->debug;
		$this->debug = (bool) $level;

		return $previous;
	}

	/**
	 * Sets the SQL statement string for later execution.
	 *
	 * @param   mixed    $query   The SQL statement to set either as a
JDatabaseQuery object or a string.
	 * @param   integer  $offset  The affected row offset to set.
	 * @param   integer  $limit   The maximum affected rows to set.
	 *
	 * @return  JDatabaseDriver  This object to support method chaining.
	 *
	 * @since   1.7.0
	 */
	public function setQuery($query, $offset = 0, $limit = 0)
	{
		$this->sql = $query;

		if ($query instanceof JDatabaseQueryLimitable)
		{
			if (!$limit && $query->limit)
			{
				$limit = $query->limit;
			}

			if (!$offset && $query->offset)
			{
				$offset = $query->offset;
			}

			$query->setLimit($limit, $offset);
		}
		else
		{
			$this->limit = (int) max(0, $limit);
			$this->offset = (int) max(0, $offset);
		}

		return $this;
	}

	/**
	 * Set the connection to use UTF-8 character encoding.
	 *
	 * @return  boolean  True on success.
	 *
	 * @since   1.7.0
	 */
	abstract public function setUtf();

	/**
	 * Method to commit a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
	 *
	 * @return  void
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function transactionCommit($toSavepoint = false);

	/**
	 * Method to roll back a transaction.
	 *
	 * @param   boolean  $toSavepoint  If true, rollback to the last
savepoint.
	 *
	 * @return  void
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function transactionRollback($toSavepoint = false);

	/**
	 * Method to initialize a transaction.
	 *
	 * @param   boolean  $asSavepoint  If true and a transaction is already
active, a savepoint will be created.
	 *
	 * @return  void
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	abstract public function transactionStart($asSavepoint = false);

	/**
	 * Method to truncate a table.
	 *
	 * @param   string  $table  The table to truncate
	 *
	 * @return  void
	 *
	 * @since   1.7.3
	 * @throws  RuntimeException
	 */
	public function truncateTable($table)
	{
		$this->setQuery('TRUNCATE TABLE ' .
$this->quoteName($table));
		$this->execute();
	}

	/**
	 * 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|string  $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   1.7.0
	 * @throws  RuntimeException
	 */
	public function updateObject($table, &$object, $key, $nulls = false)
	{
		$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) || is_object($v) || $k[0] === '_')
			{
				continue;
			}

			// Set the primary key to the WHERE clause instead of a field to update.
			if (in_array($k, $key))
			{
				$where[] = $this->quoteName($k) . ($v === null ? ' IS
NULL' : ' = ' . $this->quote($v));
				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->quote($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();
	}

	/**
	 * Execute the SQL statement.
	 *
	 * @return  mixed  A database cursor resource on success, boolean false on
failure.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	abstract public function execute();

	/**
	 * Unlocks tables in the database.
	 *
	 * @return  JDatabaseDriver  Returns this object to support chaining.
	 *
	 * @since   2.5.0
	 * @throws  RuntimeException
	 */
	abstract public function unlockTables();
}
PK��[�ۄ���exception/connecting.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Exception class defining an error connecting to the database platform
 *
 * @since  3.6
 */
class JDatabaseExceptionConnecting extends RuntimeException
{
}
PK��[>�����exception/executing.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Exception class defining an error executing a statement
 *
 * @since  3.6
 */
class JDatabaseExceptionExecuting extends RuntimeException
{
	/**
	 * The SQL statement that was executed.
	 *
	 * @var    string
	 * @since  3.6
	 */
	private $query;

	/**
	 * Construct the exception
	 *
	 * @param   string     $query     The SQL statement that was executed.
	 * @param   string     $message   The Exception message to throw.
[optional]
	 * @param   integer    $code      The Exception code. [optional]
	 * @param   Exception  $previous  The previous exception used for the
exception chaining. [optional]
	 *
	 * @since   3.6
	 */
	public function __construct($query, $message = '', $code = 0,
Exception $previous = null)
	{
		parent::__construct($message, $code, $previous);

		$this->query = $query;
	}

	/**
	 * Get the SQL statement that was executed
	 *
	 * @return  string
	 *
	 * @since   3.6
	 */
	public function getQuery()
	{
		return $this->query;
	}
}
PK��[n��u��exception/unsupported.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Exception class defining an unsupported database object
 *
 * @since  3.6
 */
class JDatabaseExceptionUnsupported extends RuntimeException
{
}
PK��[+��%exporter/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL export driver.
 *
 * @since       1.7.0
 * @deprecated  4.0  Use MySQLi or PDO MySQL instead
 */
class JDatabaseExporterMysql extends JDatabaseExporterMysqli
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseExporterMysql  Method supports chaining.
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverMysql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[�{c���exporter/mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQLi export driver.
 *
 * @since  1.7.0
 */
class JDatabaseExporterMysqli extends JDatabaseExporter
{
	/**
	 * Builds the XML data for the tables to export.
	 *
	 * @return  string  An XML string
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXml()
	{
		$buffer = array();

		$buffer[] = '<?xml version="1.0"?>';
		$buffer[] = '<mysqldump
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
		$buffer[] = ' <database name="">';

		$buffer = array_merge($buffer, $this->buildXmlStructure());

		$buffer[] = ' </database>';
		$buffer[] = '</mysqldump>';

		return implode("\n", $buffer);
	}

	/**
	 * Builds the XML structure to export.
	 *
	 * @return  array  An array of XML lines (strings).
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXmlStructure()
	{
		$buffer = array();

		foreach ($this->from as $table)
		{
			// Replace the magic prefix if found.
			$table = $this->getGenericTableName($table);

			// Get the details columns information.
			$fields = $this->db->getTableColumns($table, false);
			$keys = $this->db->getTableKeys($table);

			$buffer[] = '  <table_structure name="' . $table .
'">';

			foreach ($fields as $field)
			{
				$buffer[] = '   <field Field="' . $field->Field .
'"' . ' Type="' . $field->Type .
'"' . ' Null="' . $field->Null .
'"' . ' Key="' .
					$field->Key . '"' . (isset($field->Default) ?
' Default="' . $field->Default . '"' :
'') . ' Extra="' . $field->Extra .
'"' .
					' />';
			}

			foreach ($keys as $key)
			{
				$buffer[] = '   <key Table="' . $table .
'"' . ' Non_unique="' . $key->Non_unique .
'"' . ' Key_name="' . $key->Key_name .
'"' .
					' Seq_in_index="' . $key->Seq_in_index .
'"' . ' Column_name="' . $key->Column_name
. '"' . ' Collation="' . $key->Collation .
'"' .
					' Null="' . $key->Null . '"' . '
Index_type="' . $key->Index_type . '"' .
					' Comment="' . htmlspecialchars($key->Comment,
ENT_COMPAT, 'UTF-8') . '"' . ' />';
			}

			$buffer[] = '  </table_structure>';
		}

		return $buffer;
	}

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseExporterMysqli  Method supports chaining.
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverMysqli))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[�U��exporter/pdomysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL export driver for the PDO based MySQL database driver.
 *
 * @package     Joomla.Platform
 * @subpackage  Database
 * @since       3.4
 */
class JDatabaseExporterPdomysql extends JDatabaseExporter
{
	/**
	 * Builds the XML data for the tables to export.
	 *
	 * @return  string  An XML string
	 *
	 * @since   3.4
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXml()
	{
		$buffer   = array();

		$buffer[] = '<?xml version="1.0"?>';
		$buffer[] = '<mysqldump
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
		$buffer[] = ' <database name="">';

		$buffer   = array_merge($buffer, $this->buildXmlStructure());

		$buffer[] = ' </database>';
		$buffer[] = '</mysqldump>';

		return implode("\n", $buffer);
	}

	/**
	 * Builds the XML structure to export.
	 *
	 * @return  array  An array of XML lines (strings).
	 *
	 * @since   3.4
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXmlStructure()
	{
		$buffer = array();

		foreach ($this->from as $table)
		{
			// Replace the magic prefix if found.
			$table = $this->getGenericTableName($table);

			// Get the details columns information.
			$fields = $this->db->getTableColumns($table, false);
			$keys   = $this->db->getTableKeys($table);

			$buffer[] = '  <table_structure name="' . $table .
'">';

			foreach ($fields as $field)
			{
				$buffer[] = '   <field Field="' . $field->Field .
'"' . ' Type="' . $field->Type .
'"' . ' Null="' . $field->Null .
'"' . ' Key="' .
					$field->Key . '"' . (isset($field->Default) ?
' Default="' . $field->Default . '"' :
'') . ' Extra="' . $field->Extra .
'"' .
					' />';
			}

			foreach ($keys as $key)
			{
				$buffer[] = '   <key Table="' . $table .
'"' . ' Non_unique="' . $key->Non_unique .
'"' . ' Key_name="' . $key->Key_name .
'"' .
					' Seq_in_index="' . $key->Seq_in_index .
'"' . ' Column_name="' . $key->Column_name
. '"' . ' Collation="' . $key->Collation .
'"' .
					' Null="' . $key->Null . '"' . '
Index_type="' . $key->Index_type . '"' .
					' Comment="' . htmlspecialchars($key->Comment,
ENT_COMPAT, 'UTF-8') . '"' . ' />';
			}

			$buffer[] = '  </table_structure>';
		}

		return $buffer;
	}

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseExporterPdomysql  Method supports chaining.
	 *
	 * @since   3.4
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPdomysql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[THF{��exporter/pgsql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PDO PostgreSQL Database Exporter.
 *
 * @since  3.9.0
 */
class JDatabaseExporterPgsql extends JDatabaseExporterPostgresql
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseExporterPgsql  Method supports chaining.
	 *
	 * @since   3.9.0
	 * @throws  \Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPgsql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[��vexporter/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL export driver.
 *
 * @since       3.0.0
 * @deprecated  4.0  Use PDO PostgreSQL instead
 *
 * @property-read  JDatabaseDriverPostgresql  $db  The database connector
to use for exporting structure and/or data.
 */
class JDatabaseExporterPostgresql extends JDatabaseExporter
{
	/**
	 * Builds the XML data for the tables to export.
	 *
	 * @return  string  An XML string
	 *
	 * @since   3.0.0
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXml()
	{
		$buffer = array();

		$buffer[] = '<?xml version="1.0"?>';
		$buffer[] = '<postgresqldump
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
		$buffer[] = ' <database name="">';

		$buffer = array_merge($buffer, $this->buildXmlStructure());

		$buffer[] = ' </database>';
		$buffer[] = '</postgresqldump>';

		return implode("\n", $buffer);
	}

	/**
	 * Builds the XML structure to export.
	 *
	 * @return  array  An array of XML lines (strings).
	 *
	 * @since   3.0.0
	 * @throws  Exception if an error occurs.
	 */
	protected function buildXmlStructure()
	{
		$buffer = array();

		foreach ($this->from as $table)
		{
			// Replace the magic prefix if found.
			$table = $this->getGenericTableName($table);

			// Get the details columns information.
			$fields = $this->db->getTableColumns($table, false);
			$keys = $this->db->getTableKeys($table);
			$sequences = $this->db->getTableSequences($table);

			$buffer[] = '  <table_structure name="' . $table .
'">';

			foreach ($sequences as $sequence)
			{
				if (version_compare($this->db->getVersion(), '9.1.0')
< 0)
				{
					$sequence->start_value = null;
				}

				$buffer[] = '   <sequence Name="' .
$sequence->sequence . '"' . ' Schema="' .
$sequence->schema . '"' .
					' Table="' . $sequence->table . '"' .
' Column="' . $sequence->column . '"' .
' Type="' . $sequence->data_type . '"' .
					' Start_Value="' . $sequence->start_value .
'"' . ' Min_Value="' .
$sequence->minimum_value . '"' .
					' Max_Value="' . $sequence->maximum_value .
'"' . ' Increment="' .
$sequence->increment . '"' .
					' Cycle_option="' . $sequence->cycle_option .
'"' .
					' />';
			}

			foreach ($fields as $field)
			{
				$buffer[] = '   <field Field="' .
$field->column_name . '"' . ' Type="' .
$field->type . '"' . ' Null="' .
$field->null . '"' .
							(isset($field->default) ? ' Default="' .
$field->default . '"' : '') . '
Comments="' . $field->comments . '"' .
					' />';
			}

			foreach ($keys as $key)
			{
				$buffer[] = '   <key Index="' . $key->idxName .
'"' . ' is_primary="' . $key->isPrimary .
'"' . ' is_unique="' . $key->isUnique .
'"' .
					' Query="' . $key->Query . '" />';
			}

			$buffer[] = '  </table_structure>';
		}

		return $buffer;
	}

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseExporterPostgresql  Method supports chaining.
	 *
	 * @since   3.0.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPostgresql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[r�(��exporter.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Exporter Class
 *
 * @since  3.0.0
 */
abstract class JDatabaseExporter
{
	/**
	 * The type of output format (xml).
	 *
	 * @var    string
	 * @since  3.2.0
	 */
	protected $asFormat = 'xml';

	/**
	 * An array of cached data.
	 *
	 * @var    array
	 * @since  3.2.0
	 */
	protected $cache = array();

	/**
	 * The database connector to use for exporting structure and/or data.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.2.0
	 */
	protected $db = null;

	/**
	 * An array input sources (table names).
	 *
	 * @var    array
	 * @since  3.2.0
	 */
	protected $from = array();

	/**
	 * An array of options for the exporter.
	 *
	 * @var    object
	 * @since  3.2.0
	 */
	protected $options = null;

	/**
	 * Constructor.
	 *
	 * Sets up the default options for the exporter.
	 *
	 * @since   3.2.0
	 */
	public function __construct()
	{
		$this->options = new stdClass;

		$this->cache = array('columns' => array(),
'keys' => array());

		// Set up the class defaults:

		// Export with only structure
		$this->withStructure();

		// Export as xml.
		$this->asXml();

		// Default destination is a string using $output = (string) $exporter;
	}

	/**
	 * Magic function to exports the data to a string.
	 *
	 * @return  string
	 *
	 * @since   3.2.0
	 * @throws  Exception if an error is encountered.
	 */
	public function __toString()
	{
		// Check everything is ok to run first.
		$this->check();

		// Get the format.
		switch ($this->asFormat)
		{
			case 'xml':
			default:
				$buffer = $this->buildXml();
				break;
		}

		return $buffer;
	}

	/**
	 * Set the output option for the exporter to XML format.
	 *
	 * @return  JDatabaseExporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function asXml()
	{
		$this->asFormat = 'xml';

		return $this;
	}

	/**
	 * Builds the XML data for the tables to export.
	 *
	 * @return  string  An XML string
	 *
	 * @since   3.2.0
	 * @throws  Exception if an error occurs.
	 */
	abstract protected function buildXml();

	/**
	 * Builds the XML structure to export.
	 *
	 * @return  array  An array of XML lines (strings).
	 *
	 * @since   3.2.0
	 * @throws  Exception if an error occurs.
	 */
	abstract protected function buildXmlStructure();

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseDriver  Method supports chaining.
	 *
	 * @since   3.2.0
	 * @throws  Exception if an error is encountered.
	 */
	abstract public function check();

	/**
	 * Specifies a list of table names to export.
	 *
	 * @param   mixed  $from  The name of a single table, or an array of the
table names to export.
	 *
	 * @return  JDatabaseExporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 * @throws  Exception if input is not a string or array.
	 */
	public function from($from)
	{
		if (is_string($from))
		{
			$this->from = array($from);
		}
		elseif (is_array($from))
		{
			$this->from = $from;
		}
		else
		{
			throw new
Exception('JPLATFORM_ERROR_INPUT_REQUIRES_STRING_OR_ARRAY');
		}

		return $this;
	}

	/**
	 * Get the generic name of the table, converting the database prefix to
the wildcard string.
	 *
	 * @param   string  $table  The name of the table.
	 *
	 * @return  string  The name of the table with the database prefix
replaced with #__.
	 *
	 * @since   3.2.0
	 */
	protected function getGenericTableName($table)
	{
		$prefix = $this->db->getPrefix();

		// Replace the magic prefix if found.
		$table = preg_replace("|^$prefix|", '#__', $table);

		return $table;
	}

	/**
	 * Sets the database connector to use for exporting structure and/or data
from MySQL.
	 *
	 * @param   JDatabaseDriver  $db  The database connector.
	 *
	 * @return  JDatabaseExporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function setDbo(JDatabaseDriver $db)
	{
		$this->db = $db;

		return $this;
	}

	/**
	 * Sets an internal option to export the structure of the input table(s).
	 *
	 * @param   boolean  $setting  True to export the structure, false to not.
	 *
	 * @return  JDatabaseExporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function withStructure($setting = true)
	{
		$this->options->withStructure = (boolean) $setting;

		return $this;
	}
}
PK��[::2jjfactory.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Factory class
 *
 * @since  3.0.0
 */
class JDatabaseFactory
{
	/**
	 * Contains the current JDatabaseFactory instance
	 *
	 * @var    JDatabaseFactory
	 * @since  3.0.0
	 */
	private static $_instance = null;

	/**
	 * Method to return a JDatabaseDriver instance based on the given options.
There are three global options and then
	 * the rest are specific to the database driver. The 'database'
option determines which database is to
	 * be used for the connection. The 'select' option determines
whether the connector should automatically select
	 * the chosen database.
	 *
	 * Instances are unique to the given options and new objects are only
created when a unique options array is
	 * passed into the method.  This ensures that we don't end up with
unnecessary database connection resources.
	 *
	 * @param   string  $name     Name of the database driver you'd like
to instantiate
	 * @param   array   $options  Parameters to be passed to the database
driver.
	 *
	 * @return  JDatabaseDriver  A database driver object.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getDriver($name = 'mysqli', $options = array())
	{
		// Sanitize the database connector options.
		$options['driver']   =
preg_replace('/[^A-Z0-9_\.-]/i', '', $name);
		$options['database'] = (isset($options['database']))
? $options['database'] : null;
		$options['select']   = (isset($options['select'])) ?
$options['select'] : true;

		// Derive the class name from the driver.
		$class = 'JDatabaseDriver' .
ucfirst(strtolower($options['driver']));

		// If the class still doesn't exist we have nothing left to do but
throw an exception.  We did our best.
		if (!class_exists($class))
		{
			throw new JDatabaseExceptionUnsupported(sprintf('Unable to load
Database Driver: %s', $options['driver']));
		}

		// Create our new JDatabaseDriver connector based on the options given.
		try
		{
			$instance = new $class($options);
		}
		catch (RuntimeException $e)
		{
			throw new JDatabaseExceptionConnecting(sprintf('Unable to connect
to the Database: %s', $e->getMessage()), $e->getCode(), $e);
		}

		return $instance;
	}

	/**
	 * Gets an exporter class object.
	 *
	 * @param   string           $name  Name of the driver you want an
exporter for.
	 * @param   JDatabaseDriver  $db    Optional JDatabaseDriver instance
	 *
	 * @return  JDatabaseExporter  An exporter object.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getExporter($name, JDatabaseDriver $db = null)
	{
		// Derive the class name from the driver.
		$class = 'JDatabaseExporter' . ucfirst(strtolower($name));

		// Make sure we have an exporter class for this driver.
		if (!class_exists($class))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported('Database Exporter not
found.');
		}

		$o = new $class;

		if ($db instanceof JDatabaseDriver)
		{
			$o->setDbo($db);
		}

		return $o;
	}

	/**
	 * Gets an importer class object.
	 *
	 * @param   string           $name  Name of the driver you want an
importer for.
	 * @param   JDatabaseDriver  $db    Optional JDatabaseDriver instance
	 *
	 * @return  JDatabaseImporter  An importer object.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getImporter($name, JDatabaseDriver $db = null)
	{
		// Derive the class name from the driver.
		$class = 'JDatabaseImporter' . ucfirst(strtolower($name));

		// Make sure we have an importer class for this driver.
		if (!class_exists($class))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported('Database importer not
found.');
		}

		$o = new $class;

		if ($db instanceof JDatabaseDriver)
		{
			$o->setDbo($db);
		}

		return $o;
	}

	/**
	 * Gets an instance of the factory object.
	 *
	 * @return  JDatabaseFactory
	 *
	 * @since   3.0.0
	 */
	public static function getInstance()
	{
		return self::$_instance ? self::$_instance : new JDatabaseFactory;
	}

	/**
	 * Get the current query object or a new JDatabaseQuery object.
	 *
	 * @param   string           $name  Name of the driver you want an query
object for.
	 * @param   JDatabaseDriver  $db    Optional JDatabaseDriver instance
	 *
	 * @return  JDatabaseQuery  The current query object or a new object
extending the JDatabaseQuery class.
	 *
	 * @since   3.0.0
	 * @throws  RuntimeException
	 */
	public function getQuery($name, JDatabaseDriver $db = null)
	{
		// Derive the class name from the driver.
		$class = 'JDatabaseQuery' . ucfirst(strtolower($name));

		// Make sure we have a query class for this driver.
		if (!class_exists($class))
		{
			// If it doesn't exist we are at an impasse so throw an exception.
			throw new JDatabaseExceptionUnsupported('Database Query class not
found');
		}

		return new $class($db);
	}

	/**
	 * Gets an instance of a factory object to return on subsequent calls of
getInstance.
	 *
	 * @param   JDatabaseFactory  $instance  A JDatabaseFactory object.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	public static function setInstance(JDatabaseFactory $instance = null)
	{
		self::$_instance = $instance;
	}
}
PK��[�ڙimporter/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL import driver.
 *
 * @since       1.7.0
 * @deprecated  4.0  Use MySQLi or PDO MySQL instead
 */
class JDatabaseImporterMysql extends JDatabaseImporterMysqli
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporterMysql  Method supports chaining.
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverMysql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[&��;�-�-importer/mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQLi import driver.
 *
 * @since  1.7.0
 */
class JDatabaseImporterMysqli extends JDatabaseImporter
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporterMysqli  Method supports chaining.
	 *
	 * @since   1.7.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverMysqli))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}

	/**
	 * Get the SQL syntax to add a table.
	 *
	 * @param   SimpleXMLElement  $table  The table information.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException
	 */
	protected function xmlToCreate(SimpleXMLElement $table)
	{
		$existingTables = $this->db->getTableList();
		$tableName = (string) $table['name'];

		if (in_array($tableName, $existingTables))
		{
			throw new RuntimeException('The table you are trying to create
already exists');
		}

		$createTableStatement = 'CREATE TABLE ' .
$this->db->quoteName($tableName) . ' (';

		foreach ($table->xpath('field') as $field)
		{
			$createTableStatement .= $this->getColumnSQL($field) . ',
';
		}

		$newLookup = $this->getKeyLookup($table->xpath('key'));

		// Loop through each key in the new structure.
		foreach ($newLookup as $key)
		{
			$createTableStatement .= $this->getKeySQL($key) . ', ';
		}

		// Remove the comma after the last key
		$createTableStatement = rtrim($createTableStatement, ', ');

		$createTableStatement .= ')';

		return $createTableStatement;
	}

	/**
	 * Get the SQL syntax to add a column.
	 *
	 * @param   string            $table  The table name.
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getAddColumnSql($table, SimpleXMLElement $field)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' ADD COLUMN ' . $this->getColumnSql($field);
	}

	/**
	 * Get the SQL syntax to add a key.
	 *
	 * @param   string  $table  The table name.
	 * @param   array   $keys   An array of the fields pertaining to this key.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getAddKeySql($table, $keys)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' ADD ' . $this->getKeySql($keys);
	}

	/**
	 * Get alters for table if there is a difference.
	 *
	 * @param   SimpleXMLElement  $structure  The XML structure of the table.
	 *
	 * @return  array
	 *
	 * @since   1.7.0
	 */
	protected function getAlterTableSql(SimpleXMLElement $structure)
	{
		$table = $this->getRealTableName($structure['name']);
		$oldFields = $this->db->getTableColumns($table, false);
		$oldKeys = $this->db->getTableKeys($table);
		$alters = array();

		// Get the fields and keys from the XML that we are aiming for.
		$newFields = $structure->xpath('field');
		$newKeys = $structure->xpath('key');

		// Loop through each field in the new structure.
		foreach ($newFields as $field)
		{
			$fName = (string) $field['Field'];

			if (isset($oldFields[$fName]))
			{
				// The field exists, check it's the same.
				$column = $oldFields[$fName];

				// Test whether there is a change.
				$change = ((string) $field['Type'] != $column->Type) ||
((string) $field['Null'] != $column->Null)
					|| ((string) $field['Default'] != $column->Default) ||
((string) $field['Extra'] != $column->Extra);

				if ($change)
				{
					$alters[] = $this->getChangeColumnSql($table, $field);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldFields[$fName]);
			}
			else
			{
				// The field is new.
				$alters[] = $this->getAddColumnSql($table, $field);
			}
		}

		// Any columns left are orphans
		foreach ($oldFields as $name => $column)
		{
			// Delete the column.
			$alters[] = $this->getDropColumnSql($table, $name);
		}

		// Get the lookups for the old and new keys.
		$oldLookup = $this->getKeyLookup($oldKeys);
		$newLookup = $this->getKeyLookup($newKeys);

		// Loop through each key in the new structure.
		foreach ($newLookup as $name => $keys)
		{
			// Check if there are keys on this field in the existing table.
			if (isset($oldLookup[$name]))
			{
				$same = true;
				$newCount = count($newLookup[$name]);
				$oldCount = count($oldLookup[$name]);

				// There is a key on this field in the old and new tables. Are they the
same?
				if ($newCount == $oldCount)
				{
					// Need to loop through each key and do a fine grained check.
					for ($i = 0; $i < $newCount; $i++)
					{
						$same = (((string) $newLookup[$name][$i]['Non_unique'] ==
$oldLookup[$name][$i]->Non_unique)
							&& ((string) $newLookup[$name][$i]['Column_name']
== $oldLookup[$name][$i]->Column_name)
							&& ((string) $newLookup[$name][$i]['Seq_in_index']
== $oldLookup[$name][$i]->Seq_in_index)
							&& ((string) $newLookup[$name][$i]['Collation'] ==
$oldLookup[$name][$i]->Collation)
							&& ((string) $newLookup[$name][$i]['Index_type']
== $oldLookup[$name][$i]->Index_type));

						/*
						Debug.
						echo '<pre>';
						echo '<br />Non_unique:   '.
							((string) $newLookup[$name][$i]['Non_unique'] ==
$oldLookup[$name][$i]->Non_unique ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Non_unique'].' vs
'.$oldLookup[$name][$i]->Non_unique;
						echo '<br />Column_name:  '.
							((string) $newLookup[$name][$i]['Column_name'] ==
$oldLookup[$name][$i]->Column_name ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Column_name'].' vs
'.$oldLookup[$name][$i]->Column_name;
						echo '<br />Seq_in_index: '.
							((string) $newLookup[$name][$i]['Seq_in_index'] ==
$oldLookup[$name][$i]->Seq_in_index ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Seq_in_index'].' vs
'.$oldLookup[$name][$i]->Seq_in_index;
						echo '<br />Collation:    '.
							((string) $newLookup[$name][$i]['Collation'] ==
$oldLookup[$name][$i]->Collation ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Collation'].' vs
'.$oldLookup[$name][$i]->Collation;
						echo '<br />Index_type:   '.
							((string) $newLookup[$name][$i]['Index_type'] ==
$oldLookup[$name][$i]->Index_type ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Index_type'].' vs
'.$oldLookup[$name][$i]->Index_type;
						echo '<br />Same = '.($same ? 'true' :
'false');
						echo '</pre>';
						 */

						if (!$same)
						{
							// Break out of the loop. No need to check further.
							break;
						}
					}
				}
				else
				{
					// Count is different, just drop and add.
					$same = false;
				}

				if (!$same)
				{
					$alters[] = $this->getDropKeySql($table, $name);
					$alters[] = $this->getAddKeySql($table, $keys);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldLookup[$name]);
			}
			else
			{
				// This is a new key.
				$alters[] = $this->getAddKeySql($table, $keys);
			}
		}

		// Any keys left are orphans.
		foreach ($oldLookup as $name => $keys)
		{
			if (strtoupper($name) == 'PRIMARY')
			{
				$alters[] = $this->getDropPrimaryKeySql($table);
			}
			else
			{
				$alters[] = $this->getDropKeySql($table, $name);
			}
		}

		return $alters;
	}

	/**
	 * Get the syntax to alter a column.
	 *
	 * @param   string            $table  The name of the database table to
alter.
	 * @param   SimpleXMLElement  $field  The XML definition for the field.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getChangeColumnSql($table, SimpleXMLElement $field)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' CHANGE COLUMN ' . $this->db->quoteName((string)
$field['Field']) . ' '
			. $this->getColumnSql($field);
	}

	/**
	 * Get the SQL syntax for a single column that would be included in a
table create or alter statement.
	 *
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getColumnSql(SimpleXMLElement $field)
	{
		// TODO Incorporate into parent class and use $this.
		$blobs = array('text', 'smalltext',
'mediumtext', 'largetext');

		$fName = (string) $field['Field'];
		$fType = (string) $field['Type'];
		$fNull = (string) $field['Null'];
		$fDefault = isset($field['Default']) ? (string)
$field['Default'] : null;
		$fExtra = (string) $field['Extra'];

		$query = $this->db->quoteName($fName) . ' ' . $fType;

		if ($fNull == 'NO')
		{
			if (in_array($fType, $blobs) || $fDefault === null)
			{
				$query .= ' NOT NULL';
			}
			else
			{
				// TODO Don't quote numeric values.
				$query .= ' NOT NULL DEFAULT ' .
$this->db->quote($fDefault);
			}
		}
		else
		{
			if ($fDefault === null)
			{
				$query .= ' DEFAULT NULL';
			}
			else
			{
				// TODO Don't quote numeric values.
				$query .= ' DEFAULT ' . $this->db->quote($fDefault);
			}
		}

		if ($fExtra)
		{
			$query .= ' ' . strtoupper($fExtra);
		}

		return $query;
	}

	/**
	 * Get the SQL syntax to drop a key.
	 *
	 * @param   string  $table  The table name.
	 * @param   string  $name   The name of the key to drop.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getDropKeySql($table, $name)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP KEY ' . $this->db->quoteName($name);
	}

	/**
	 * Get the SQL syntax to drop a key.
	 *
	 * @param   string  $table  The table name.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getDropPrimaryKeySql($table)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP PRIMARY KEY';
	}

	/**
	 * Get the details list of keys for a table.
	 *
	 * @param   array  $keys  An array of objects that comprise the keys for
the table.
	 *
	 * @return  array  The lookup array. array({key name} => array(object,
...))
	 *
	 * @since   1.7.0
	 * @throws  Exception
	 */
	protected function getKeyLookup($keys)
	{
		// First pass, create a lookup of the keys.
		$lookup = array();

		foreach ($keys as $key)
		{
			if ($key instanceof SimpleXMLElement)
			{
				$kName = (string) $key['Key_name'];
			}
			else
			{
				$kName = $key->Key_name;
			}

			if (empty($lookup[$kName]))
			{
				$lookup[$kName] = array();
			}

			$lookup[$kName][] = $key;
		}

		return $lookup;
	}

	/**
	 * Get the SQL syntax for a key.
	 *
	 * @param   array  $columns  An array of SimpleXMLElement objects
comprising the key.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	protected function getKeySql($columns)
	{
		// TODO Error checking on array and element types.

		$kNonUnique = (string) $columns[0]['Non_unique'];
		$kName = (string) $columns[0]['Key_name'];
		$kColumn = (string) $columns[0]['Column_name'];

		$prefix = '';

		if ($kName == 'PRIMARY')
		{
			$prefix = 'PRIMARY ';
		}
		elseif ($kNonUnique == 0)
		{
			$prefix = 'UNIQUE ';
		}

		$nColumns = count($columns);
		$kColumns = array();

		if ($nColumns == 1)
		{
			$kColumns[] = $this->db->quoteName($kColumn);
		}
		else
		{
			foreach ($columns as $column)
			{
				$kColumns[] = (string) $column['Column_name'];
			}
		}

		$query = $prefix . 'KEY ' . ($kName != 'PRIMARY' ?
$this->db->quoteName($kName) : '') . ' (' .
implode(',', $kColumns) . ')';

		return $query;
	}
}
PK��[;c%�
,
,importer/pdomysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL import driver for the PDO based MySQL database driver.
 *
 * @package     Joomla.Platform
 * @subpackage  Database
 * @since       3.4
 */
class JDatabaseImporterPdomysql extends JDatabaseImporter
{
	/**
	 * Get the SQL syntax to add a column.
	 *
	 * @param   string            $table  The table name.
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getAddColumnSql($table, SimpleXMLElement $field)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' ADD COLUMN ' . $this->getColumnSql($field);

		return $sql;
	}

	/**
	 * Get the SQL syntax to add a key.
	 *
	 * @param   string  $table  The table name.
	 * @param   array   $keys   An array of the fields pertaining to this key.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getAddKeySql($table, $keys)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' ADD ' . $this->getKeySql($keys);

		return $sql;
	}

	/**
	 * Get alters for table if there is a difference.
	 *
	 * @param   SimpleXMLElement  $structure  The XML structure of the table.
	 *
	 * @return  array
	 *
	 * @since   3.4
	 */
	protected function getAlterTableSql(SimpleXMLElement $structure)
	{
		// Initialise variables.
		$table     = $this->getRealTableName($structure['name']);
		$oldFields = $this->db->getTableColumns($table);
		$oldKeys   = $this->db->getTableKeys($table);
		$alters    = array();

		// Get the fields and keys from the XML that we are aiming for.
		$newFields = $structure->xpath('field');
		$newKeys   = $structure->xpath('key');

		// Loop through each field in the new structure.
		foreach ($newFields as $field)
		{
			$fName = (string) $field['Field'];

			if (isset($oldFields[$fName]))
			{
				// The field exists, check it's the same.
				$column = $oldFields[$fName];

				// Test whether there is a change.
				$change = ((string) $field['Type'] != $column->Type) ||
((string) $field['Null'] != $column->Null)
					|| ((string) $field['Default'] != $column->Default) ||
((string) $field['Extra'] != $column->Extra);

				if ($change)
				{
					$alters[] = $this->getChangeColumnSql($table, $field);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldFields[$fName]);
			}
			else
			{
				// The field is new.
				$alters[] = $this->getAddColumnSql($table, $field);
			}
		}

		// Any columns left are orphans
		foreach ($oldFields as $name => $column)
		{
			// Delete the column.
			$alters[] = $this->getDropColumnSql($table, $name);
		}

		// Get the lookups for the old and new keys.
		$oldLookup = $this->getKeyLookup($oldKeys);
		$newLookup = $this->getKeyLookup($newKeys);

		// Loop through each key in the new structure.
		foreach ($newLookup as $name => $keys)
		{
			// Check if there are keys on this field in the existing table.
			if (isset($oldLookup[$name]))
			{
				$same     = true;
				$newCount = count($newLookup[$name]);
				$oldCount = count($oldLookup[$name]);

				// There is a key on this field in the old and new tables. Are they the
same?
				if ($newCount == $oldCount)
				{
					// Need to loop through each key and do a fine grained check.
					for ($i = 0; $i < $newCount; $i++)
					{
						$same = (((string) $newLookup[$name][$i]['Non_unique'] ==
$oldLookup[$name][$i]->Non_unique)
							&& ((string) $newLookup[$name][$i]['Column_name']
== $oldLookup[$name][$i]->Column_name)
							&& ((string) $newLookup[$name][$i]['Seq_in_index']
== $oldLookup[$name][$i]->Seq_in_index)
							&& ((string) $newLookup[$name][$i]['Collation'] ==
$oldLookup[$name][$i]->Collation)
							&& ((string) $newLookup[$name][$i]['Index_type']
== $oldLookup[$name][$i]->Index_type));

						/*
						Debug.
						echo '<pre>';
						echo '<br />Non_unique:   '.
							((string) $newLookup[$name][$i]['Non_unique'] ==
$oldLookup[$name][$i]->Non_unique ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Non_unique'].' vs
'.$oldLookup[$name][$i]->Non_unique;
						echo '<br />Column_name:  '.
							((string) $newLookup[$name][$i]['Column_name'] ==
$oldLookup[$name][$i]->Column_name ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Column_name'].' vs
'.$oldLookup[$name][$i]->Column_name;
						echo '<br />Seq_in_index: '.
							((string) $newLookup[$name][$i]['Seq_in_index'] ==
$oldLookup[$name][$i]->Seq_in_index ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Seq_in_index'].' vs
'.$oldLookup[$name][$i]->Seq_in_index;
						echo '<br />Collation:    '.
							((string) $newLookup[$name][$i]['Collation'] ==
$oldLookup[$name][$i]->Collation ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Collation'].' vs
'.$oldLookup[$name][$i]->Collation;
						echo '<br />Index_type:   '.
							((string) $newLookup[$name][$i]['Index_type'] ==
$oldLookup[$name][$i]->Index_type ? 'Pass' :
'Fail').' '.
							(string) $newLookup[$name][$i]['Index_type'].' vs
'.$oldLookup[$name][$i]->Index_type;
						echo '<br />Same = '.($same ? 'true' :
'false');
						echo '</pre>';
						 */

						if (!$same)
						{
							// Break out of the loop. No need to check further.
							break;
						}
					}
				}
				else
				{
					// Count is different, just drop and add.
					$same = false;
				}

				if (!$same)
				{
					$alters[] = $this->getDropKeySql($table, $name);
					$alters[] = $this->getAddKeySql($table, $keys);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldLookup[$name]);
			}
			else
			{
				// This is a new key.
				$alters[] = $this->getAddKeySql($table, $keys);
			}
		}

		// Any keys left are orphans.
		foreach ($oldLookup as $name => $keys)
		{
			if (strtoupper($name) == 'PRIMARY')
			{
				$alters[] = $this->getDropPrimaryKeySql($table);
			}
			else
			{
				$alters[] = $this->getDropKeySql($table, $name);
			}
		}

		return $alters;
	}

	/**
	 * Get the syntax to alter a column.
	 *
	 * @param   string            $table  The name of the database table to
alter.
	 * @param   SimpleXMLElement  $field  The XML definition for the field.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getChangeColumnSql($table, SimpleXMLElement $field)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' CHANGE COLUMN ' . $this->db->quoteName((string)
$field['Field']) . ' '
			. $this->getColumnSql($field);

		return $sql;
	}

	/**
	 * Get the SQL syntax for a single column that would be included in a
table create or alter statement.
	 *
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getColumnSql(SimpleXMLElement $field)
	{
		// Initialise variables.
		// TODO Incorporate into parent class and use $this.
		$blobs = array('text', 'smalltext',
'mediumtext', 'largetext');

		$fName    = (string) $field['Field'];
		$fType    = (string) $field['Type'];
		$fNull    = (string) $field['Null'];
		$fDefault = isset($field['Default']) ? (string)
$field['Default'] : null;
		$fExtra   = (string) $field['Extra'];

		$sql = $this->db->quoteName($fName) . ' ' . $fType;

		if ($fNull == 'NO')
		{
			if (in_array($fType, $blobs) || $fDefault === null)
			{
				$sql .= ' NOT NULL';
			}
			else
			{
				// TODO Don't quote numeric values.
				$sql .= ' NOT NULL DEFAULT ' .
$this->db->quote($fDefault);
			}
		}
		else
		{
			if ($fDefault === null)
			{
				$sql .= ' DEFAULT NULL';
			}
			else
			{
				// TODO Don't quote numeric values.
				$sql .= ' DEFAULT ' . $this->db->quote($fDefault);
			}
		}

		if ($fExtra)
		{
			$sql .= ' ' . strtoupper($fExtra);
		}

		return $sql;
	}

	/**
	 * Get the SQL syntax to drop a column.
	 *
	 * @param   string  $table  The table name.
	 * @param   string  $name   The name of the field to drop.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getDropColumnSql($table, $name)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP COLUMN ' . $this->db->quoteName($name);

		return $sql;
	}

	/**
	 * Get the SQL syntax to drop a key.
	 *
	 * @param   string  $table  The table name.
	 * @param   string  $name   The name of the key to drop.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getDropKeySql($table, $name)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP KEY ' . $this->db->quoteName($name);

		return $sql;
	}

	/**
	 * Get the SQL syntax to drop a key.
	 *
	 * @param   string  $table  The table name.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getDropPrimaryKeySql($table)
	{
		$sql = 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP PRIMARY KEY';

		return $sql;
	}

	/**
	 * Get the details list of keys for a table.
	 *
	 * @param   array  $keys  An array of objects that comprise the keys for
the table.
	 *
	 * @return  array  The lookup array. array({key name} => array(object,
...))
	 *
	 * @since   3.4
	 * @throws  Exception
	 */
	protected function getKeyLookup($keys)
	{
		// First pass, create a lookup of the keys.
		$lookup = array();

		foreach ($keys as $key)
		{
			if ($key instanceof SimpleXMLElement)
			{
				$kName = (string) $key['Key_name'];
			}
			else
			{
				$kName = $key->Key_name;
			}

			if (empty($lookup[$kName]))
			{
				$lookup[$kName] = array();
			}

			$lookup[$kName][] = $key;
		}

		return $lookup;
	}

	/**
	 * Get the SQL syntax for a key.
	 *
	 * @param   array  $columns  An array of SimpleXMLElement objects
comprising the key.
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	protected function getKeySql($columns)
	{
		// TODO Error checking on array and element types.

		$kNonUnique = (string) $columns[0]['Non_unique'];
		$kName      = (string) $columns[0]['Key_name'];
		$kColumn    = (string) $columns[0]['Column_name'];
		$prefix     = '';

		if ($kName == 'PRIMARY')
		{
			$prefix = 'PRIMARY ';
		}
		elseif ($kNonUnique == 0)
		{
			$prefix = 'UNIQUE ';
		}

		$nColumns = count($columns);
		$kColumns = array();

		if ($nColumns == 1)
		{
			$kColumns[] = $this->db->quoteName($kColumn);
		}
		else
		{
			foreach ($columns as $column)
			{
				$kColumns[] = (string) $column['Column_name'];
			}
		}

		$sql = $prefix . 'KEY ' . ($kName != 'PRIMARY' ?
$this->db->quoteName($kName) : '') . ' (' .
implode(',', $kColumns) . ')';

		return $sql;
	}

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporterPdomysql  Method supports chaining.
	 *
	 * @since   3.4
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPdomysql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[� -���importer/pgsql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PDO PostgreSQL Database Importer.
 *
 * @since  3.9.0
 */
class JDatabaseImporterPgsql extends JDatabaseImporterPostgresql
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporterPgsql  Method supports chaining.
	 *
	 * @since   3.9.0
	 * @throws  \Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPgsql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}
}
PK��[Z��38989importer/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL import driver.
 *
 * @since       3.0.0
 * @deprecated  4.0  Use PDO PostgreSQL instead
 */
class JDatabaseImporterPostgresql extends JDatabaseImporter
{
	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporterPostgresql  Method supports chaining.
	 *
	 * @since   3.0.0
	 * @throws  Exception if an error is encountered.
	 */
	public function check()
	{
		// Check if the db connector has been set.
		if (!($this->db instanceof JDatabaseDriverPostgresql))
		{
			throw new
Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
		}

		// Check if the tables have been specified.
		if (empty($this->from))
		{
			throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
		}

		return $this;
	}

	/**
	 * Get the SQL syntax to add a column.
	 *
	 * @param   string            $table  The table name.
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getAddColumnSql($table, SimpleXMLElement $field)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' ADD COLUMN ' . $this->getColumnSql($field);
	}

	/**
	 * Get the SQL syntax to add an index.
	 *
	 * @param   SimpleXMLElement  $field  The XML index definition.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getAddIndexSql(SimpleXMLElement $field)
	{
		return (string) $field['Query'];
	}

	/**
	 * Get alters for table if there is a difference.
	 *
	 * @param   SimpleXMLElement  $structure  The XML structure of the table.
	 *
	 * @return  array
	 *
	 * @since   3.0.0
	 */
	protected function getAlterTableSql(SimpleXMLElement $structure)
	{
		$table = $this->getRealTableName($structure['name']);
		$oldFields = $this->db->getTableColumns($table);
		$oldKeys = $this->db->getTableKeys($table);
		$oldSequence = $this->db->getTableSequences($table);
		$alters = array();

		// Get the fields and keys from the XML that we are aiming for.
		$newFields = $structure->xpath('field');
		$newKeys = $structure->xpath('key');
		$newSequence = $structure->xpath('sequence');

		/* Sequence section */
		$oldSeq = $this->getSeqLookup($oldSequence);
		$newSequenceLook = $this->getSeqLookup($newSequence);

		foreach ($newSequenceLook as $kSeqName => $vSeq)
		{
			if (isset($oldSeq[$kSeqName]))
			{
				// The field exists, check it's the same.
				$column = $oldSeq[$kSeqName][0];

				/* For older database version that doesn't support these fields
use default values */
				if (version_compare($this->db->getVersion(), '9.1.0')
< 0)
				{
					$column->Min_Value = '1';
					$column->Max_Value = '9223372036854775807';
					$column->Increment = '1';
					$column->Cycle_option = 'NO';
					$column->Start_Value = '1';
				}

				// Test whether there is a change.
				$change = ((string) $vSeq[0]['Type'] != $column->Type) ||
((string) $vSeq[0]['Start_Value'] != $column->Start_Value)
					|| ((string) $vSeq[0]['Min_Value'] != $column->Min_Value)
|| ((string) $vSeq[0]['Max_Value'] != $column->Max_Value)
					|| ((string) $vSeq[0]['Increment'] != $column->Increment)
|| ((string) $vSeq[0]['Cycle_option'] !=
$column->Cycle_option)
					|| ((string) $vSeq[0]['Table'] != $column->Table) ||
((string) $vSeq[0]['Column'] != $column->Column)
					|| ((string) $vSeq[0]['Schema'] != $column->Schema) ||
((string) $vSeq[0]['Name'] != $column->Name);

				if ($change)
				{
					$alters[] = $this->getChangeSequenceSql($kSeqName, $vSeq);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldSeq[$kSeqName]);
			}
			else
			{
				// The sequence is new
				$alters[] =
$this->getAddSequenceSql($newSequenceLook[$kSeqName][0]);
			}
		}

		// Any sequences left are orphans
		foreach ($oldSeq as $name => $column)
		{
			// Delete the sequence.
			$alters[] = $this->getDropSequenceSql($name);
		}

		/* Field section */
		// Loop through each field in the new structure.
		foreach ($newFields as $field)
		{
			$fName = (string) $field['Field'];

			if (isset($oldFields[$fName]))
			{
				// The field exists, check it's the same.
				$column = $oldFields[$fName];

				// Test whether there is a change.
				$change = ((string) $field['Type'] != $column->Type) ||
((string) $field['Null'] != $column->Null)
					|| ((string) $field['Default'] != $column->Default);

				if ($change)
				{
					$alters[] = $this->getChangeColumnSql($table, $field);
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldFields[$fName]);
			}
			else
			{
				// The field is new.
				$alters[] = $this->getAddColumnSql($table, $field);
			}
		}

		// Any columns left are orphans
		foreach ($oldFields as $name => $column)
		{
			// Delete the column.
			$alters[] = $this->getDropColumnSql($table, $name);
		}

		/* Index section */
		// Get the lookups for the old and new keys
		$oldLookup = $this->getIdxLookup($oldKeys);
		$newLookup = $this->getIdxLookup($newKeys);

		// Loop through each key in the new structure.
		foreach ($newLookup as $name => $keys)
		{
			// Check if there are keys on this field in the existing table.
			if (isset($oldLookup[$name]))
			{
				$same = true;
				$newCount = count($newLookup[$name]);
				$oldCount = count($oldLookup[$name]);

				// There is a key on this field in the old and new tables. Are they the
same?
				if ($newCount == $oldCount)
				{
					for ($i = 0; $i < $newCount; $i++)
					{
						// Check only query field -> different query means different index
						$same = ((string) $newLookup[$name][$i]['Query'] ==
$oldLookup[$name][$i]->Query);

						if (!$same)
						{
							// Break out of the loop. No need to check further.
							break;
						}
					}
				}
				else
				{
					// Count is different, just drop and add.
					$same = false;
				}

				if (!$same)
				{
					$alters[] = $this->getDropIndexSql($name);
					$alters[]  = (string) $newLookup[$name][0]['Query'];
				}

				// Unset this field so that what we have left are fields that need to
be removed.
				unset($oldLookup[$name]);
			}
			else
			{
				// This is a new key.
				$alters[] = (string) $newLookup[$name][0]['Query'];
			}
		}

		// Any keys left are orphans.
		foreach ($oldLookup as $name => $keys)
		{
			if ($oldLookup[$name][0]->is_primary == 'TRUE')
			{
				$alters[] = $this->getDropPrimaryKeySql($table,
$oldLookup[$name][0]->Index);
			}
			else
			{
				$alters[] = $this->getDropIndexSql($name);
			}
		}

		return $alters;
	}

	/**
	 * Get the SQL syntax to drop a sequence.
	 *
	 * @param   string  $name  The name of the sequence to drop.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getDropSequenceSql($name)
	{
		return 'DROP SEQUENCE ' . $this->db->quoteName($name);
	}

	/**
	 * Get the syntax to add a sequence.
	 *
	 * @param   SimpleXMLElement  $field  The XML definition for the sequence.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getAddSequenceSql($field)
	{
		/* For older database version that doesn't support these fields use
default values */
		if (version_compare($this->db->getVersion(), '9.1.0')
< 0)
		{
			$field['Min_Value'] = '1';
			$field['Max_Value'] = '9223372036854775807';
			$field['Increment'] = '1';
			$field['Cycle_option'] = 'NO';
			$field['Start_Value'] = '1';
		}

		return 'CREATE SEQUENCE ' . (string) $field['Name'] .
			' INCREMENT BY ' . (string) $field['Increment'] .
' MINVALUE ' . $field['Min_Value'] .
			' MAXVALUE ' . (string) $field['Max_Value'] . '
START ' . (string) $field['Start_Value'] .
			(((string) $field['Cycle_option'] == 'NO') ? '
NO' : '') . ' CYCLE' .
			' OWNED BY ' . $this->db->quoteName((string)
$field['Schema'] . '.' . (string)
$field['Table'] . '.' . (string)
$field['Column']);
	}

	/**
	 * Get the syntax to alter a sequence.
	 *
	 * @param   SimpleXMLElement  $field  The XML definition for the sequence.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getChangeSequenceSql($field)
	{
		/* For older database version that doesn't support these fields use
default values */
		if (version_compare($this->db->getVersion(), '9.1.0')
< 0)
		{
			$field['Min_Value'] = '1';
			$field['Max_Value'] = '9223372036854775807';
			$field['Increment'] = '1';
			$field['Cycle_option'] = 'NO';
			$field['Start_Value'] = '1';
		}

		return 'ALTER SEQUENCE ' . (string) $field['Name'] .
			' INCREMENT BY ' . (string) $field['Increment'] .
' MINVALUE ' . (string) $field['Min_Value'] .
			' MAXVALUE ' . (string) $field['Max_Value'] . '
START ' . (string) $field['Start_Value'] .
			' OWNED BY ' . $this->db->quoteName((string)
$field['Schema'] . '.' . (string)
$field['Table'] . '.' . (string)
$field['Column']);
	}

	/**
	 * Get the syntax to alter a column.
	 *
	 * @param   string            $table  The name of the database table to
alter.
	 * @param   SimpleXMLElement  $field  The XML definition for the field.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getChangeColumnSql($table, SimpleXMLElement $field)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' ALTER COLUMN ' . $this->db->quoteName((string)
$field['Field']) . ' '
			. $this->getAlterColumnSql($table, $field);
	}

	/**
	 * Get the SQL syntax for a single column that would be included in a
table create statement.
	 *
	 * @param   string            $table  The name of the database table to
alter.
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getAlterColumnSql($table, $field)
	{
		// TODO Incorporate into parent class and use $this.
		$blobs = array('text', 'smalltext',
'mediumtext', 'largetext');

		$fName = (string) $field['Field'];
		$fType = (string) $field['Type'];
		$fNull = (string) $field['Null'];
		$fDefault = (isset($field['Default']) &&
$field['Default'] != 'NULL') ?
						preg_match('/^[0-9]$/', $field['Default']) ?
$field['Default'] : $this->db->quote((string)
$field['Default'])
					: null;

		$query = ' TYPE ' . $fType;

		if ($fNull == 'NO')
		{
			if (in_array($fType, $blobs) || $fDefault === null)
			{
				$query .= ",\nALTER COLUMN " .
$this->db->quoteName($fName) . ' SET NOT NULL' .
						",\nALTER COLUMN " . $this->db->quoteName($fName) .
' DROP DEFAULT';
			}
			else
			{
				$query .= ",\nALTER COLUMN " .
$this->db->quoteName($fName) . ' SET NOT NULL' .
						",\nALTER COLUMN " . $this->db->quoteName($fName) .
' SET DEFAULT ' . $fDefault;
			}
		}
		else
		{
			if ($fDefault !== null)
			{
				$query .= ",\nALTER COLUMN " .
$this->db->quoteName($fName) . ' DROP NOT NULL' .
						",\nALTER COLUMN " . $this->db->quoteName($fName) .
' SET DEFAULT ' . $fDefault;
			}
		}

		/* sequence was created in other function, here is associated a default
value but not yet owner */
		if (strpos($fDefault, 'nextval') !== false)
		{
			$query .= ";\nALTER SEQUENCE " .
$this->db->quoteName($table . '_' . $fName .
'_seq') . ' OWNED BY ' .
$this->db->quoteName($table . '.' . $fName);
		}

		return $query;
	}

	/**
	 * Get the SQL syntax for a single column that would be included in a
table create statement.
	 *
	 * @param   SimpleXMLElement  $field  The XML field definition.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getColumnSql(SimpleXMLElement $field)
	{
		// TODO Incorporate into parent class and use $this.
		$blobs = array('text', 'smalltext',
'mediumtext', 'largetext');

		$fName = (string) $field['Field'];
		$fType = (string) $field['Type'];
		$fNull = (string) $field['Null'];
		$fDefault = (isset($field['Default']) &&
$field['Default'] != 'NULL') ?
						preg_match('/^[0-9]$/', $field['Default']) ?
$field['Default'] : $this->db->quote((string)
$field['Default'])
					: null;

		/* nextval() as default value means that type field is serial */
		if (strpos($fDefault, 'nextval') !== false)
		{
			$query = $this->db->quoteName($fName) . ' SERIAL';
		}
		else
		{
			$query = $this->db->quoteName($fName) . ' ' . $fType;

			if ($fNull == 'NO')
			{
				if (in_array($fType, $blobs) || $fDefault === null)
				{
					$query .= ' NOT NULL';
				}
				else
				{
					$query .= ' NOT NULL DEFAULT ' . $fDefault;
				}
			}
			else
			{
				if ($fDefault !== null)
				{
					$query .= ' DEFAULT ' . $fDefault;
				}
			}
		}

		return $query;
	}

	/**
	 * Get the SQL syntax to drop an index.
	 *
	 * @param   string  $name  The name of the key to drop.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getDropIndexSql($name)
	{
		return 'DROP INDEX ' . $this->db->quoteName($name);
	}

	/**
	 * Get the SQL syntax to drop a key.
	 *
	 * @param   string  $table  The table name.
	 * @param   string  $name   The constraint name.
	 *
	 * @return  string
	 *
	 * @since   3.0.0
	 */
	protected function getDropPrimaryKeySql($table, $name)
	{
		return 'ALTER TABLE ONLY ' . $this->db->quoteName($table)
. ' DROP CONSTRAINT ' . $this->db->quoteName($name);
	}

	/**
	 * Get the details list of keys for a table.
	 *
	 * @param   array  $keys  An array of objects that comprise the keys for
the table.
	 *
	 * @return  array  The lookup array. array({key name} => array(object,
...))
	 *
	 * @since   3.0.0
	 * @throws  Exception
	 */
	protected function getIdxLookup($keys)
	{
		// First pass, create a lookup of the keys.
		$lookup = array();

		foreach ($keys as $key)
		{
			if ($key instanceof SimpleXMLElement)
			{
				$kName = (string) $key['Index'];
			}
			else
			{
				$kName = $key->Index;
			}

			if (empty($lookup[$kName]))
			{
				$lookup[$kName] = array();
			}

			$lookup[$kName][] = $key;
		}

		return $lookup;
	}

	/**
	 * Get the details list of sequences for a table.
	 *
	 * @param   array  $sequences  An array of objects that comprise the
sequences for the table.
	 *
	 * @return  array  The lookup array. array({key name} => array(object,
...))
	 *
	 * @since   3.0.0
	 * @throws  Exception
	 */
	protected function getSeqLookup($sequences)
	{
		// First pass, create a lookup of the keys.
		$lookup = array();

		foreach ($sequences as $seq)
		{
			if ($seq instanceof SimpleXMLElement)
			{
				$sName = (string) $seq['Name'];
			}
			else
			{
				$sName = $seq->Name;
			}

			if (empty($lookup[$sName]))
			{
				$lookup[$sName] = array();
			}

			$lookup[$sName][] = $seq;
		}

		return $lookup;
	}
}
PK��[Gu�SSimporter.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Importer Class
 *
 * @since  3.0.0
 */
abstract class JDatabaseImporter
{
	/**
	 * @var    array  An array of cached data.
	 * @since  3.2.0
	 */
	protected $cache = array();

	/**
	 * The database connector to use for exporting structure and/or data.
	 *
	 * @var    JDatabaseDriver
	 * @since  3.2.0
	 */
	protected $db = null;

	/**
	 * The input source.
	 *
	 * @var    mixed
	 * @since  3.2.0
	 */
	protected $from = array();

	/**
	 * The type of input format (XML).
	 *
	 * @var    string
	 * @since  3.2.0
	 */
	protected $asFormat = 'xml';

	/**
	 * An array of options for the exporter.
	 *
	 * @var    object
	 * @since  3.2.0
	 */
	protected $options = null;

	/**
	 * Constructor.
	 *
	 * Sets up the default options for the exporter.
	 *
	 * @since   3.2.0
	 */
	public function __construct()
	{
		$this->options = new stdClass;

		$this->cache = array('columns' => array(),
'keys' => array());

		// Set up the class defaults:

		// Import with only structure
		$this->withStructure();

		// Export as XML.
		$this->asXml();

		// Default destination is a string using $output = (string) $exporter;
	}

	/**
	 * Set the output option for the exporter to XML format.
	 *
	 * @return  JDatabaseImporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function asXml()
	{
		$this->asFormat = 'xml';

		return $this;
	}

	/**
	 * Checks if all data and options are in order prior to exporting.
	 *
	 * @return  JDatabaseImporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 * @throws  Exception if an error is encountered.
	 */
	abstract public function check();

	/**
	 * Specifies the data source to import.
	 *
	 * @param   mixed  $from  The data source to import.
	 *
	 * @return  JDatabaseImporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function from($from)
	{
		$this->from = $from;

		return $this;
	}

	/**
	 * Get the SQL syntax to drop a column.
	 *
	 * @param   string  $table  The table name.
	 * @param   string  $name   The name of the field to drop.
	 *
	 * @return  string
	 *
	 * @since   3.2.0
	 */
	protected function getDropColumnSql($table, $name)
	{
		return 'ALTER TABLE ' . $this->db->quoteName($table) .
' DROP COLUMN ' . $this->db->quoteName($name);
	}

	/**
	 * Get the real name of the table, converting the prefix wildcard string
if present.
	 *
	 * @param   string  $table  The name of the table.
	 *
	 * @return  string	The real name of the table.
	 *
	 * @since   3.2.0
	 */
	protected function getRealTableName($table)
	{
		$prefix = $this->db->getPrefix();

		// Replace the magic prefix if found.
		$table = preg_replace('|^#__|', $prefix, $table);

		return $table;
	}

	/**
	 * Merges the incoming structure definition with the existing structure.
	 *
	 * @return  void
	 *
	 * @note    Currently only supports XML format.
	 * @since   3.2.0
	 * @throws  RuntimeException on error.
	 */
	public function mergeStructure()
	{
		$prefix = $this->db->getPrefix();
		$tables = $this->db->getTableList();

		if ($this->from instanceof SimpleXMLElement)
		{
			$xml = $this->from;
		}
		else
		{
			$xml = new SimpleXMLElement($this->from);
		}

		// Get all the table definitions.
		$xmlTables = $xml->xpath('database/table_structure');

		foreach ($xmlTables as $table)
		{
			// Convert the magic prefix into the real table name.
			$tableName = (string) $table['name'];
			$tableName = preg_replace('|^#__|', $prefix, $tableName);

			if (in_array($tableName, $tables))
			{
				// The table already exists. Now check if there is any difference.
				if ($queries = $this->getAlterTableSql($table))
				{
					// Run the queries to upgrade the data structure.
					foreach ($queries as $query)
					{
						$this->db->setQuery((string) $query);
						$this->db->execute();
					}
				}
			}
			else
			{
				// This is a new table.
				$sql = $this->xmlToCreate($table);

				$this->db->setQuery((string) $sql);
				$this->db->execute();
			}
		}
	}

	/**
	 * Sets the database connector to use for exporting structure and/or data.
	 *
	 * @param   JDatabaseDriver  $db  The database connector.
	 *
	 * @return  JDatabaseImporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function setDbo(JDatabaseDriver $db)
	{
		$this->db = $db;

		return $this;
	}

	/**
	 * Sets an internal option to merge the structure based on the input data.
	 *
	 * @param   boolean  $setting  True to export the structure, false to not.
	 *
	 * @return  JDatabaseImporter  Method supports chaining.
	 *
	 * @since   3.2.0
	 */
	public function withStructure($setting = true)
	{
		$this->options->withStructure = (boolean) $setting;

		return $this;
	}
}
PK��[���''
interface.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Interface
 *
 * @since  1.7.0
*/
interface JDatabaseInterface
{
	/**
	 * Test to see if the connector is available.
	 *
	 * @return  boolean  True on success, false otherwise.
	 *
	 * @since   1.7.0
	 */
	public static function isSupported();
}
PK��[6 0��iterator/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL database iterator.
 *
 * @link        http://dev.mysql.com/doc/
 * @since       3.0.0
 * @deprecated  4.0  Use MySQLi or PDO MySQL instead
 */
class JDatabaseIteratorMysql extends JDatabaseIterator
{
	/**
	 * Get the number of rows in the result set for the executed SQL given by
the cursor.
	 *
	 * @return  integer  The number of rows in the result set.
	 *
	 * @since   3.0.0
	 * @see     Countable::count()
	 */
	public function count()
	{
		return mysql_num_rows($this->cursor);
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	protected function fetchObject()
	{
		return mysql_fetch_object($this->cursor, $this->class);
	}

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	protected function freeResult()
	{
		mysql_free_result($this->cursor);
	}
}
PK��[l]����iterator/mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQLi database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorMysqli extends JDatabaseIterator
{
	/**
	 * Get the number of rows in the result set for the executed SQL given by
the cursor.
	 *
	 * @return  integer  The number of rows in the result set.
	 *
	 * @since   3.0.0
	 * @see     Countable::count()
	 */
	public function count()
	{
		return mysqli_num_rows($this->cursor);
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	protected function fetchObject()
	{
		return mysqli_fetch_object($this->cursor, $this->class);
	}

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	protected function freeResult()
	{
		mysqli_free_result($this->cursor);
	}
}
PK��[�V����iterator/oracle.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Oracle database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorOracle extends JDatabaseIteratorPdo
{
}
PK��[c�t��iterator/pdo.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PDO database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorPdo extends JDatabaseIterator
{
	/**
	 * Get the number of rows in the result set for the executed SQL given by
the cursor.
	 *
	 * @return  integer  The number of rows in the result set.
	 *
	 * @since   3.0.0
	 * @see     Countable::count()
	 */
	public function count()
	{
		if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
		{
			return $this->cursor->rowCount();
		}
		else
		{
			return 0;
		}
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	protected function fetchObject()
	{
		if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
		{
			return $this->cursor->fetchObject($this->class);
		}
		else
		{
			return false;
		}
	}

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	protected function freeResult()
	{
		if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
		{
			$this->cursor->closeCursor();
		}
	}
}
PK��[g�Ziterator/pdomysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * MySQL database iterator for the PDO based MySQL database driver.
 *
 * @package     Joomla.Platform
 * @subpackage  Database
 * @link        https://dev.mysql.com/doc/
 * @since       3.4
 */
class JDatabaseIteratorPdomysql extends JDatabaseIteratorPdo
{
}
PK��["y]U��iterator/pgsql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL database iterator for the PDO based PostgreSQL database
driver.
 *
 * @since  3.9.0
 */
class JDatabaseIteratorPgsql extends JDatabaseIteratorPdo
{
}
PK��[�����iterator/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PostgreSQL database iterator.
 *
 * @since       3.2.0
 * @deprecated  4.0  Use PDO PostgreSQL instead
 */
class JDatabaseIteratorPostgresql extends JDatabaseIterator
{
	/**
	 * Get the number of rows in the result set for the executed SQL given by
the cursor.
	 *
	 * @return  integer  The number of rows in the result set.
	 *
	 * @since   3.2.0
	 * @see     Countable::count()
	 */
	public function count()
	{
		return pg_num_rows($this->cursor);
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.2.0
	 */
	protected function fetchObject()
	{
		return pg_fetch_object($this->cursor, null, $this->class);
	}

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.2.0
	 */
	protected function freeResult()
	{
		pg_free_result($this->cursor);
	}
}
PK��[t�{��iterator/sqlazure.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQL azure database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorSqlazure extends JDatabaseIteratorSqlsrv
{
}
PK��[Zn��iterator/sqlite.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQLite database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorSqlite extends JDatabaseIteratorPdo
{
}
PK��[-����iterator/sqlsrv.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQL server database iterator.
 *
 * @since  3.0.0
 */
class JDatabaseIteratorSqlsrv extends JDatabaseIterator
{
	/**
	 * Get the number of rows in the result set for the executed SQL given by
the cursor.
	 *
	 * @return  integer  The number of rows in the result set.
	 *
	 * @since   3.0.0
	 * @see     Countable::count()
	 */
	public function count()
	{
		return sqlsrv_num_rows($this->cursor);
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed   Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	protected function fetchObject()
	{
		return sqlsrv_fetch_object($this->cursor, $this->class);
	}

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	protected function freeResult()
	{
		sqlsrv_free_stmt($this->cursor);
	}
}
PK��[h�Jh��iterator.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Platform Database Driver Class
 *
 * @since  3.0.0
 */
abstract class JDatabaseIterator implements Countable, Iterator
{
	/**
	 * The database cursor.
	 *
	 * @var    mixed
	 * @since  3.0.0
	 */
	protected $cursor;

	/**
	 * The class of object to create.
	 *
	 * @var    string
	 * @since  3.0.0
	 */
	protected $class;

	/**
	 * The name of the column to use for the key of the database record.
	 *
	 * @var    mixed
	 * @since  3.0.0
	 */
	private $_column;

	/**
	 * The current database record.
	 *
	 * @var    mixed
	 * @since  3.0.0
	 */
	private $_current;

	/**
	 * A numeric or string key for the current database record.
	 *
	 * @var    int|string
	 * @since  3.0.0
	 */
	private $_key;

	/**
	 * The number of fetched records.
	 *
	 * @var    integer
	 * @since  3.0.0
	 */
	private $_fetched = 0;

	/**
	 * Database iterator constructor.
	 *
	 * @param   mixed   $cursor  The database cursor.
	 * @param   string  $column  An option column to use as the iterator key.
	 * @param   string  $class   The class of object that is returned.
	 *
	 * @throws  InvalidArgumentException
	 */
	public function __construct($cursor, $column = null, $class =
'stdClass')
	{
		if (!class_exists($class))
		{
			throw new InvalidArgumentException(sprintf('new %s(*%s*,
cursor)', get_class($this), gettype($class)));
		}

		$this->cursor = $cursor;
		$this->class = $class;
		$this->_column = $column;
		$this->_fetched = 0;
		$this->next();
	}

	/**
	 * Database iterator destructor.
	 *
	 * @since   3.0.0
	 */
	public function __destruct()
	{
		if ($this->cursor)
		{
			$this->freeResult($this->cursor);
		}
	}

	/**
	 * The current element in the iterator.
	 *
	 * @return  object
	 *
	 * @see     Iterator::current()
	 * @since   3.0.0
	 */
	public function current()
	{
		return $this->_current;
	}

	/**
	 * The key of the current element in the iterator.
	 *
	 * @return  int|string
	 *
	 * @see     Iterator::key()
	 * @since   3.0.0
	 */
	public function key()
	{
		return $this->_key;
	}

	/**
	 * Moves forward to the next result from the SQL query.
	 *
	 * @return  void
	 *
	 * @see     Iterator::next()
	 * @since   3.0.0
	 */
	public function next()
	{
		// Set the default key as being the number of fetched object
		$this->_key = $this->_fetched;

		// Try to get an object
		$this->_current = $this->fetchObject();

		// If an object has been found
		if ($this->_current)
		{
			// Set the key as being the indexed column (if it exists)
			if (isset($this->_current->{$this->_column}))
			{
				$this->_key = $this->_current->{$this->_column};
			}

			// Update the number of fetched object
			$this->_fetched++;
		}
	}

	/**
	 * Rewinds the iterator.
	 *
	 * This iterator cannot be rewound.
	 *
	 * @return  void
	 *
	 * @see     Iterator::rewind()
	 * @since   3.0.0
	 */
	public function rewind()
	{
	}

	/**
	 * Checks if the current position of the iterator is valid.
	 *
	 * @return  boolean
	 *
	 * @see     Iterator::valid()
	 * @since   3.0.0
	 */
	public function valid()
	{
		return (boolean) $this->_current;
	}

	/**
	 * Method to fetch a row from the result set cursor as an object.
	 *
	 * @return  mixed  Either the next row from the result set or false if
there are no more rows.
	 *
	 * @since   3.0.0
	 */
	abstract protected function fetchObject();

	/**
	 * Method to free up the memory used for the result set.
	 *
	 * @return  void
	 *
	 * @since   3.0.0
	 */
	abstract protected function freeResult();
}
PK��[�D�
�
query/element.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Element Class.
 *
 * @property-read    string  $name      The name of the element.
 * @property-read    array   $elements  An array of elements.
 * @property-read    string  $glue      Glue piece.
 *
 * @since  1.7.0
 */
class JDatabaseQueryElement
{
	/**
	 * @var    string  The name of the element.
	 * @since  1.7.0
	 */
	protected $name = null;

	/**
	 * @var    array  An array of elements.
	 * @since  1.7.0
	 */
	protected $elements = null;

	/**
	 * @var    string  Glue piece.
	 * @since  1.7.0
	 */
	protected $glue = null;

	/**
	 * Constructor.
	 *
	 * @param   string  $name      The name of the element.
	 * @param   mixed   $elements  String or array.
	 * @param   string  $glue      The glue for elements.
	 *
	 * @since   1.7.0
	 */
	public function __construct($name, $elements, $glue = ',')
	{
		$this->elements = array();
		$this->name = $name;
		$this->glue = $glue;

		$this->append($elements);
	}

	/**
	 * Magic function to convert the query element to a string.
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	public function __toString()
	{
		if (substr($this->name, -2) == '()')
		{
			return PHP_EOL . substr($this->name, 0, -2) . '(' .
implode($this->glue, $this->elements) . ')';
		}
		else
		{
			return PHP_EOL . $this->name . ' ' .
implode($this->glue, $this->elements);
		}
	}

	/**
	 * Appends element parts to the internal list.
	 *
	 * @param   mixed  $elements  String or array.
	 *
	 * @return  void
	 *
	 * @since   1.7.0
	 */
	public function append($elements)
	{
		if (is_array($elements))
		{
			$this->elements = array_merge($this->elements, $elements);
		}
		else
		{
			$this->elements[] = $elements;
		}
	}

	/**
	 * Gets the elements of this element.
	 *
	 * @return  array
	 *
	 * @since   1.7.0
	 */
	public function getElements()
	{
		return $this->elements;
	}

	/**
	 * Sets the name of this element.
	 *
	 * @param   string  $name  Name of the element.
	 *
	 * @return  JDatabaseQueryElement  Returns this object to allow chaining.
	 *
	 * @since   3.6
	 */
	public function setName($name)
	{
		$this->name = $name;

		return $this;
	}

	/**
	 * Method to provide deep copy support to nested objects and arrays
	 * when cloning.
	 *
	 * @return  void
	 *
	 * @since   1.7.3
	 */
	public function __clone()
	{
		foreach ($this as $k => $v)
		{
			if (is_object($v) || is_array($v))
			{
				$this->{$k} = unserialize(serialize($v));
			}
		}
	}
}
PK��[?}P>��query/limitable.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Database Query Limitable Interface.
 * Adds bind/unbind methods as well as a getBounded() method
 * to retrieve the stored bounded variables on demand prior to
 * query execution.
 *
 * @since  3.0.0
 */
interface JDatabaseQueryLimitable
{
	/**
	 * 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. This method is used
	 * automatically by the __toString() method if it detects that the
	 * query implements the JDatabaseQueryLimitable interface.
	 *
	 * @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   3.0.0
	 */
	public function processLimit($query, $limit, $offset = 0);

	/**
	 * 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  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setLimit($limit = 0, $offset = 0);
}
PK��[��q��query/mysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since       1.7.0
 * @deprecated  4.0  Use MySQLi or PDO MySQL instead
 */
class JDatabaseQueryMysql extends JDatabaseQueryMysqli
{
}
PK��[��
��query/mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since  1.7.0
 */
class JDatabaseQueryMysqli extends JDatabaseQuery implements
JDatabaseQueryLimitable
{
	/**
	 * @var    integer  The offset for the result set.
	 * @since  3.0.0
	 */
	protected $offset;

	/**
	 * @var    integer  The limit for the result set.
	 * @since  3.0.0
	 */
	protected $limit;

	/**
	 * Magic function to convert the query to a string.
	 *
	 * @return  string  The completed query.
	 *
	 * @since   1.7.0
	 */
	public function __toString()
	{
		switch ($this->type)
		{
			case 'select':
				if ($this->selectRowNumber)
				{
					$orderBy      = $this->selectRowNumber['orderBy'];
					$tmpOffset    = $this->offset;
					$tmpLimit     = $this->limit;
					$this->offset = 0;
					$this->limit  = 0;
					$tmpOrder     = $this->order;
					$this->order  = null;
					$query        = parent::__toString();
					$this->order  = $tmpOrder;
					$this->offset = $tmpOffset;
					$this->limit  = $tmpLimit;

					// Add support for second order by, offset and limit
					$query = PHP_EOL . 'SELECT * FROM (' . $query . PHP_EOL .
"ORDER BY $orderBy" . PHP_EOL . ') w';

					if ($this->order)
					{
						$query .= (string) $this->order;
					}

					return $this->processLimit($query, $this->limit,
$this->offset);
				}
		}

		return parent::__toString();
	}

	/**
	 * 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 3.0.0
	 */
	public function processLimit($query, $limit, $offset = 0)
	{
		if ($limit > 0 && $offset > 0)
		{
			$query .= ' LIMIT ' . $offset . ', ' . $limit;
		}
		elseif ($limit > 0)
		{
			$query .= ' LIMIT ' . $limit;
		}

		return $query;
	}

	/**
	 * Concatenates an array of column names or values.
	 *
	 * @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   1.7.0
	 */
	public function concatenate($values, $separator = null)
	{
		if ($separator)
		{
			$concat_string = 'CONCAT_WS(' . $this->quote($separator);

			foreach ($values as $value)
			{
				$concat_string .= ', ' . $value;
			}

			return $concat_string . ')';
		}
		else
		{
			return 'CONCAT(' . implode(',', $values) .
')';
		}
	}

	/**
	 * 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  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setLimit($limit = 0, $offset = 0)
	{
		$this->limit  = (int) $limit;
		$this->offset = (int) $offset;

		return $this;
	}

	/**
	 * Return correct regexp operator for mysqli.
	 *
	 * Ensure that the regexp operator is mysqli compatible.
	 *
	 * Usage:
	 * $query->where('field ' . $query->regexp($search));
	 *
	 * @param   string  $value  The regex pattern.
	 *
	 * @return  string  Returns the regex operator.
	 *
	 * @since   1.7.3
	 */
	public function regexp($value)
	{
		return ' REGEXP ' . $value;
	}

	/**
	 * Return correct rand() function for Mysql.
	 *
	 * Ensure that the rand() function is Mysql compatible.
	 * 
	 * Usage:
	 * $query->Rand();
	 * 
	 * @return  string  The correct rand function.
	 *
	 * @since   3.5
	 */
	public function Rand()
	{
		return ' RAND() ';
	}

	/**
	 * Return the number of the current row.
	 *
	 * @param   string  $orderBy           An expression of ordering for
window function.
	 * @param   string  $orderColumnAlias  An alias for new ordering column.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.7.0
	 * @throws  RuntimeException
	 */
	public function selectRowNumber($orderBy, $orderColumnAlias)
	{
		$this->validateRowNumber($orderBy, $orderColumnAlias);
		$this->select("(SELECT @rownum := @rownum + 1 FROM (SELECT
@rownum := 0) AS r) AS $orderColumnAlias");

		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'));
	 * $query->select($query->castAsChar('a', 40));
	 *
	 * @param   string  $value  The value to cast as a char.
	 *
	 * @param   string  $len    The length of the char.
	 *
	 * @return  string  Returns the cast value.
	 *
	 * @since   3.7.0
	 */
	public function castAsChar($value, $len = null)
	{
		if (!$len)
		{
			return $value;
		}
		else
		{
			return ' CAST(' . $value . ' AS CHAR(' . $len .
'))';
		}
	}
}
PK��[�w<��query/oracle.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Oracle Query Building Class.
 *
 * @since  3.0.0
 */
class JDatabaseQueryOracle extends JDatabaseQueryPdo implements
JDatabaseQueryPreparable, JDatabaseQueryLimitable
{
	/**
	 * @var    integer  The offset for the result set.
	 * @since  3.0.0
	 */
	protected $offset;

	/**
	 * @var    integer  The limit for the result set.
	 * @since  3.0.0
	 */
	protected $limit;

	/**
	 * @var    array  Bounded object array
	 * @since  3.0.0
	 */
	protected $bounded = array();

	/**
	 * Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
	 * removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
	 *
	 * @param   string|integer  $key            The key that will be used in
your SQL query to reference the value. Usually of
	 *                                          the form ':key', but
can also be an integer.
	 * @param   mixed           &$value         The value that will be
bound. The value is passed by reference to support output
	 *                                          parameters such as those
possible with stored procedures.
	 * @param   integer         $dataType       Constant corresponding to a
SQL datatype.
	 * @param   integer         $length         The length of the variable.
Usually required for OUTPUT parameters.
	 * @param   array           $driverOptions  Optional driver options to be
used.
	 *
	 * @return  JDatabaseQueryOracle
	 *
	 * @since   3.0.0
	 */
	public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array())
	{
		// Case 1: Empty Key (reset $bounded array)
		if (empty($key))
		{
			$this->bounded = array();

			return $this;
		}

		// Case 2: Key Provided, null value (unset key from $bounded array)
		if (is_null($value))
		{
			if (isset($this->bounded[$key]))
			{
				unset($this->bounded[$key]);
			}

			return $this;
		}

		$obj = new stdClass;

		$obj->value = &$value;
		$obj->dataType = $dataType;
		$obj->length = $length;
		$obj->driverOptions = $driverOptions;

		// Case 3: Simply add the Key/Value into the bounded array
		$this->bounded[$key] = $obj;

		return $this;
	}

	/**
	 * Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
	 * returned.
	 *
	 * @param   mixed  $key  The bounded variable key to retrieve.
	 *
	 * @return  mixed
	 *
	 * @since   3.0.0
	 */
	public function &getBounded($key = null)
	{
		if (empty($key))
		{
			return $this->bounded;
		}
		else
		{
			if (isset($this->bounded[$key]))
			{
				return $this->bounded[$key];
			}
		}
	}

	/**
	 * 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  JDatabaseQueryOracle  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function clear($clause = null)
	{
		switch ($clause)
		{
			case null:
				$this->bounded = array();
				break;
		}

		parent::clear($clause);

		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. This method is used
	 * automatically by the __toString() method if it detects that the
	 * query implements the JDatabaseQueryLimitable interface.
	 *
	 * @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   3.0.0
	 */
	public function processLimit($query, $limit, $offset = 0)
	{
		// Check if we need to mangle the query.
		if ($limit || $offset)
		{
			$query = 'SELECT joomla2.*
		              FROM (
		                  SELECT joomla1.*, ROWNUM AS joomla_db_rownum
		                  FROM (
		                      ' . $query . '
		                  ) joomla1
		              ) joomla2';

			// Check if the limit value is greater than zero.
			if ($limit > 0)
			{
				$query .= ' WHERE joomla2.joomla_db_rownum BETWEEN ' .
($offset + 1) . ' AND ' . ($offset + $limit);
			}
			else
			{
				// Check if there is an offset and then use this.
				if ($offset)
				{
					$query .= ' WHERE joomla2.joomla_db_rownum > ' . ($offset
+ 1);
				}
			}
		}

		return $query;
	}

	/**
	 * 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  JDatabaseQueryOracle  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setLimit($limit = 0, $offset = 0)
	{
		$this->limit = (int) $limit;
		$this->offset = (int) $offset;

		return $this;
	}
}
PK��[�����
query/pdo.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PDO Query Building Class.
 *
 * @since  3.0.0
 */
class JDatabaseQueryPdo extends JDatabaseQuery
{
	/**
	 * Casts a value to a char.
	 *
	 * Ensure that the value is properly quoted before passing to the method.
	 *
	 * Usage:
	 * $query->select($query->castAsChar('a'));
	 * $query->select($query->castAsChar('a', 40));
	 *
	 * @param   string  $value  The value to cast as a char.
	 *
	 * @param   string  $len    The length of the char.
	 *
	 * @return  string  Returns the cast value.
	 *
	 * @since   1.7.0
	 */
	public function castAsChar($value, $len = null)
	{
		if (!$len)
		{
			return $value;
		}
		else
		{
			return ' CAST(' . $value . ' AS CHAR(' . $len .
'))';
		}
	}
}
PK��[h.���query/pdomysql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @package     Joomla.Platform
 * @subpackage  Database
 * @since       3.4
 */
class JDatabaseQueryPdomysql extends JDatabaseQueryMysqli
{
}
PK��[���P}}query/pgsql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * PDO PostgreSQL Query Building Class.
 *
 * @since  3.9.0
 */
class JDatabaseQueryPgsql extends JDatabaseQueryPostgresql implements
JDatabaseQueryPreparable
{
	/**
	 * Holds key / value pair of bound objects.
	 *
	 * @var    mixed
	 * @since  3.9.0
	 */
	protected $bounded = array();

	/**
	 * Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
	 * removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
	 *
	 * @param   string|integer  $key            The key that will be used in
your SQL query to reference the value. Usually of
	 *                                          the form ':key', but
can also be an integer.
	 * @param   mixed           &$value         The value that will be
bound. The value is passed by reference to support output
	 *                                          parameters such as those
possible with stored procedures.
	 * @param   integer         $dataType       Constant corresponding to a
SQL datatype.
	 * @param   integer         $length         The length of the variable.
Usually required for OUTPUT parameters.
	 * @param   array           $driverOptions  Optional driver options to be
used.
	 *
	 * @return  JDatabaseQueryPgsql
	 *
	 * @since   3.9.0
	 */
	public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array())
	{
		// Case 1: Empty Key (reset $bounded array)
		if (empty($key))
		{
			$this->bounded = array();

			return $this;
		}

		// Case 2: Key Provided, null value (unset key from $bounded array)
		if (is_null($value))
		{
			if (isset($this->bounded[$key]))
			{
				unset($this->bounded[$key]);
			}

			return $this;
		}

		$obj = new stdClass;

		$obj->value = &$value;
		$obj->dataType = $dataType;
		$obj->length = $length;
		$obj->driverOptions = $driverOptions;

		// Case 3: Simply add the Key/Value into the bounded array
		$this->bounded[$key] = $obj;

		return $this;
	}

	/**
	 * Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
	 * returned.
	 *
	 * @param   mixed  $key  The bounded variable key to retrieve.
	 *
	 * @return  mixed
	 *
	 * @since   3.9.0
	 */
	public function &getBounded($key = null)
	{
		if (empty($key))
		{
			return $this->bounded;
		}

		if (isset($this->bounded[$key]))
		{
			return $this->bounded[$key];
		}
	}

	/**
	 * 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  JDatabaseQueryPgsql  Returns this object to allow chaining.
	 *
	 * @since   3.9.0
	 */
	public function clear($clause = null)
	{
		switch ($clause)
		{
			case null:
				$this->bounded = array();
				break;
		}

		return parent::clear($clause);
	}
}
PK��[�Za�BBquery/postgresql.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since       1.7.3
 * @deprecated  4.0  Use PDO PostgreSQL instead
 */
class JDatabaseQueryPostgresql extends JDatabaseQuery implements
JDatabaseQueryLimitable
{
	/**
	 * @var    object  The FOR UPDATE element used in "FOR UPDATE" 
lock
	 * @since  1.7.3
	 */
	protected $forUpdate = null;

	/**
	 * @var    object  The FOR SHARE element used in "FOR SHARE" 
lock
	 * @since  1.7.3
	 */
	protected $forShare = null;

	/**
	 * @var    object  The NOWAIT element used in "FOR SHARE" and
"FOR UPDATE" lock
	 * @since  1.7.3
	 */
	protected $noWait = null;

	/**
	 * @var    object  The LIMIT element
	 * @since  1.7.3
	 */
	protected $limit = null;

	/**
	 * @var    object  The OFFSET element
	 * @since  1.7.3
	 */
	protected $offset = null;

	/**
	 * @var    object  The RETURNING element of INSERT INTO
	 * @since  1.7.3
	 */
	protected $returning = null;

	/**
	 * Magic function to convert the query to a string, only for postgresql
specific query
	 *
	 * @return  string	The completed query.
	 *
	 * @since   1.7.3
	 */
	public function __toString()
	{
		$query = '';

		switch ($this->type)
		{
			case 'select':
				if ($this->selectRowNumber &&
$this->selectRowNumber['native'] === false)
				{
					// Workaround for postgresql version less than 8.4.0
					try
					{
						$this->db->setQuery('CREATE TEMP SEQUENCE
ROW_NUMBER');
						$this->db->execute();
					}
					catch (JDatabaseExceptionExecuting $e)
					{
						// Do nothing, sequence exists
					}

					$orderBy          = $this->selectRowNumber['orderBy'];
					$orderColumnAlias =
$this->selectRowNumber['orderColumnAlias'];

					$columns = "nextval('ROW_NUMBER') - 1 AS
$orderColumnAlias";

					if ($this->select === null)
					{
						$query = PHP_EOL . "SELECT 1"
							. (string) $this->from
							. (string) $this->where;
					}
					else
					{
						$tmpOffset    = $this->offset;
						$tmpLimit     = $this->limit;
						$this->offset = 0;
						$this->limit  = 0;
						$tmpOrder     = $this->order;
						$this->order  = null;
						$query        = parent::__toString();
						$columns      = "w.*, $columns";
						$this->order  = $tmpOrder;
						$this->offset = $tmpOffset;
						$this->limit  = $tmpLimit;
					}

					// Add support for second order by, offset and limit
					$query = PHP_EOL . "SELECT $columns FROM (" . $query .
PHP_EOL . "ORDER BY $orderBy"
						. PHP_EOL . ") w,(SELECT setval('ROW_NUMBER', 1)) AS
r";

					if ($this->order)
					{
						$query .= (string) $this->order;
					}

					break;
				}

				$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->selectRowNumber)
				{
					if ($this->order)
					{
						$query .= (string) $this->order;
					}

					break;
				}

				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)
				{
					$tmpFrom     = $this->from;
					$tmpWhere    = $this->where ? clone $this->where : null;
					$this->from  = null;

					// Workaround for special case of JOIN with UPDATE
					foreach ($this->join as $join)
					{
						$joinElem = $join->getElements();

						$joinArray = preg_split('/\sON\s/i', $joinElem[0]);

						if (count($joinArray) > 2)
						{
							$condition = array_pop($joinArray);
							$joinArray = array(implode(' ON ', $joinArray),
$condition);
						}

						$this->from($joinArray[0]);

						if (isset($joinArray[1]))
						{
							$this->where($joinArray[1]);
						}
					}

					$query .= (string) $this->from;

					if ($this->where)
					{
						$query .= (string) $this->where;
					}

					$this->from  = $tmpFrom;
					$this->where = $tmpWhere;
				}
				elseif ($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 JDatabaseQueryLimitable)
		{
			$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  JDatabaseQueryPostgresql  Returns this object to allow
chaining.
	 *
	 * @since   1.7.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'));
	 * $query->select($query->castAsChar('a', 40));
	 *
	 * @param   string  $value  The value to cast as a char.
	 *
	 * @param   string  $len    The length of the char.
	 *
	 * @return  string  Returns the cast value.
	 *
	 * @since   1.7.3
	 */
	public function castAsChar($value, $len = null)
	{
		if (!$len)
		{
			return $value . '::text';
		}
		else
		{
			return ' CAST(' . $value . ' AS CHAR(' . $len .
'))';
		}
	}

	/**
	 * 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   1.7.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   1.7.3
	 */
	public function currentTimestamp()
	{
		return 'NOW()';
	}

	/**
	 * Sets the FOR UPDATE lock on select's output row
	 *
	 * @param   string  $tableName  The table to lock
	 * @param   string  $glue       The glue by which to join the conditions.
Defaults to ',' .
	 *
	 * @return  JDatabaseQueryPostgresql  FOR UPDATE query element
	 *
	 * @since   1.7.3
	 */
	public function forUpdate($tableName, $glue = ',')
	{
		$this->type = 'forUpdate';

		if (is_null($this->forUpdate))
		{
			$glue            = strtoupper($glue);
			$this->forUpdate = new JDatabaseQueryElement('FOR UPDATE',
'OF ' . $tableName, "$glue ");
		}
		else
		{
			$this->forUpdate->append($tableName);
		}

		return $this;
	}

	/**
	 * Sets the FOR SHARE lock on select's output row
	 *
	 * @param   string  $tableName  The table to lock
	 * @param   string  $glue       The glue by which to join the conditions.
Defaults to ',' .
	 *
	 * @return  JDatabaseQueryPostgresql  FOR SHARE query element
	 *
	 * @since   1.7.3
	 */
	public function forShare($tableName, $glue = ',')
	{
		$this->type = 'forShare';

		if (is_null($this->forShare))
		{
			$glue           = strtoupper($glue);
			$this->forShare = new JDatabaseQueryElement('FOR SHARE',
'OF ' . $tableName, "$glue ");
		}
		else
		{
			$this->forShare->append($tableName);
		}

		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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	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   3.0.0
	 */
	public function second($date)
	{
		return 'EXTRACT (SECOND FROM ' . $date . ')';
	}

	/**
	 * Sets the NOWAIT lock on select's output row
	 *
	 * @return  JDatabaseQueryPostgresql  NO WAIT query element
	 *
	 * @since   1.7.3
	 */
	public function noWait ()
	{
		$this->type = 'noWait';

		if (is_null($this->noWait))
		{
			$this->noWait = new JDatabaseQueryElement('NOWAIT', null);
		}

		return $this;
	}

	/**
	 * Set the LIMIT clause to the query
	 *
	 * @param   integer  $limit  An int of how many row will be returned
	 *
	 * @return  JDatabaseQueryPostgresql  Returns this object to allow
chaining.
	 *
	 * @since   1.7.3
	 */
	public function limit($limit = 0)
	{
		if (is_null($this->limit))
		{
			$this->limit = new JDatabaseQueryElement('LIMIT', (int)
$limit);
		}

		return $this;
	}

	/**
	 * Set the OFFSET clause to the query
	 *
	 * @param   integer  $offset  An int for skipping row
	 *
	 * @return  JDatabaseQueryPostgresql  Returns this object to allow
chaining.
	 *
	 * @since   1.7.3
	 */
	public function offset($offset = 0)
	{
		if (is_null($this->offset))
		{
			$this->offset = new JDatabaseQueryElement('OFFSET', (int)
$offset);
		}

		return $this;
	}

	/**
	 * Add the RETURNING element to INSERT INTO statement.
	 *
	 * @param   mixed  $pkCol  The name of the primary key column.
	 *
	 * @return  JDatabaseQueryPostgresql  Returns this object to allow
chaining.
	 *
	 * @since   1.7.3
	 */
	public function returning($pkCol)
	{
		if (is_null($this->returning))
		{
			$this->returning = new JDatabaseQueryElement('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  JDatabaseQueryPostgresql  Returns this object to allow
chaining.
	 *
	 * @since   3.0.0
	 */
	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   3.0.0
	 */
	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   string  $date      The db quoted string representation of 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   3.2.0
	 * @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   1.7.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() ';
	}

	/**
	 * Return the number of the current row.
	 *
	 * @param   string  $orderBy           An expression of ordering for
window function.
	 * @param   string  $orderColumnAlias  An alias for new ordering column.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.7.0
	 * @throws  RuntimeException
	 */
	public function selectRowNumber($orderBy, $orderColumnAlias)
	{
		$this->validateRowNumber($orderBy, $orderColumnAlias);

		if (version_compare($this->db->getVersion(), '8.4.0')
>= 0)
		{
			$this->selectRowNumber['native'] = true;
			$this->select("ROW_NUMBER() OVER (ORDER BY $orderBy) AS
$orderColumnAlias");
		}
		else
		{
			$this->selectRowNumber['native'] = false;
		}

		return $this;
	}
}
PK��[	T����query/preparable.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Joomla Database Query Preparable Interface.
 * Adds bind/unbind methods as well as a getBounded() method
 * to retrieve the stored bounded variables on demand prior to
 * query execution.
 *
 * @since  3.0.0
 */
interface JDatabaseQueryPreparable
{
	/**
	 * Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
	 * removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
	 *
	 * @param   string|integer  $key            The key that will be used in
your SQL query to reference the value. Usually of
	 *                                          the form ':key', but
can also be an integer.
	 * @param   mixed           &$value         The value that will be
bound. The value is passed by reference to support output
	 *                                          parameters such as those
possible with stored procedures.
	 * @param   integer         $dataType       Constant corresponding to a
SQL datatype.
	 * @param   integer         $length         The length of the variable.
Usually required for OUTPUT parameters.
	 * @param   array           $driverOptions  Optional driver options to be
used.
	 *
	 * @return  JDatabaseQuery
	 *
	 * @since   3.0.0
	 */
	public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array());

	/**
	 * Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
	 * returned.
	 *
	 * @param   mixed  $key  The bounded variable key to retrieve.
	 *
	 * @return  mixed
	 *
	 * @since   3.0.0
	 */
	public function &getBounded($key = null);
}
PK��[L�;�88query/sqlazure.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since  1.7.0
 */
class JDatabaseQuerySqlazure extends JDatabaseQuerySqlsrv
{
	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc.  The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 *
	 * @since  1.7.0
	 */
	protected $name_quotes = '';
}
PK��[�	�(�(query/sqlite.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * SQLite Query Building Class.
 *
 * @since  3.0.0
 */
class JDatabaseQuerySqlite extends JDatabaseQueryPdo implements
JDatabaseQueryPreparable, JDatabaseQueryLimitable
{
	/**
	 * @var    integer  The offset for the result set.
	 * @since  3.0.0
	 */
	protected $offset;

	/**
	 * @var    integer  The limit for the result set.
	 * @since  3.0.0
	 */
	protected $limit;

	/**
	 * @var    array  Bounded object array
	 * @since  3.0.0
	 */
	protected $bounded = array();

	/**
	 * Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
	 * removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
	 *
	 * @param   string|integer  $key            The key that will be used in
your SQL query to reference the value. Usually of
	 *                                          the form ':key', but
can also be an integer.
	 * @param   mixed           &$value         The value that will be
bound. The value is passed by reference to support output
	 *                                          parameters such as those
possible with stored procedures.
	 * @param   integer         $dataType       Constant corresponding to a
SQL datatype.
	 * @param   integer         $length         The length of the variable.
Usually required for OUTPUT parameters.
	 * @param   array           $driverOptions  Optional driver options to be
used.
	 *
	 * @return  JDatabaseQuerySqlite
	 *
	 * @since   3.0.0
	 */
	public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array())
	{
		// Case 1: Empty Key (reset $bounded array)
		if (empty($key))
		{
			$this->bounded = array();

			return $this;
		}

		// Case 2: Key Provided, null value (unset key from $bounded array)
		if (is_null($value))
		{
			if (isset($this->bounded[$key]))
			{
				unset($this->bounded[$key]);
			}

			return $this;
		}

		$obj = new stdClass;

		$obj->value = &$value;
		$obj->dataType = $dataType;
		$obj->length = $length;
		$obj->driverOptions = $driverOptions;

		// Case 3: Simply add the Key/Value into the bounded array
		$this->bounded[$key] = $obj;

		return $this;
	}

	/**
	 * Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
	 * returned.
	 *
	 * @param   mixed  $key  The bounded variable key to retrieve.
	 *
	 * @return  mixed
	 *
	 * @since   3.0.0
	 */
	public function &getBounded($key = null)
	{
		if (empty($key))
		{
			return $this->bounded;
		}
		else
		{
			if (isset($this->bounded[$key]))
			{
				return $this->bounded[$key];
			}
		}
	}

	/**
	 * Gets the number of characters in a string.
	 *
	 * Note, use 'length' to find the number of bytes in a string.
	 *
	 * Usage:
	 * $query->select($query->charLength('a'));
	 *
	 * @param   string  $field      A value.
	 * @param   string  $operator   Comparison operator between charLength
integer value and $condition
	 * @param   string  $condition  Integer value to compare charLength with.
	 *
	 * @return  string  The required char length call.
	 *
	 * @since   3.2.0
	 */
	public function charLength($field, $operator = null, $condition = null)
	{
		return 'length(' . $field . ')' . (isset($operator)
&& isset($condition) ? ' ' . $operator . ' ' .
$condition : '');
	}

	/**
	 * 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  JDatabaseQuerySqlite  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function clear($clause = null)
	{
		switch ($clause)
		{
			case null:
				$this->bounded = array();
				break;
		}

		parent::clear($clause);

		return $this;
	}

	/**
	 * 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   1.7.0
	 */
	public function concatenate($values, $separator = null)
	{
		if ($separator)
		{
			return implode(' || ' . $this->quote($separator) . '
|| ', $values);
		}
		else
		{
			return implode(' || ', $values);
		}
	}

	/**
	 * 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. This method is used
	 * automatically by the __toString() method if it detects that the
	 * query implements the JDatabaseQueryLimitable interface.
	 *
	 * @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   3.0.0
	 */
	public function processLimit($query, $limit, $offset = 0)
	{
		if ($limit > 0 || $offset > 0)
		{
			$query .= ' LIMIT ' . $offset . ', ' . $limit;
		}

		return $query;
	}

	/**
	 * 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  JDatabaseQuerySqlite  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setLimit($limit = 0, $offset = 0)
	{
		$this->limit = (int) $limit;
		$this->offset = (int) $offset;

		return $this;
	}

	/**
	 * Add to the current date and time.
	 * Usage:
	 * $query->select($query->dateAdd());
	 * Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
	 *
	 * @param   datetime  $date      The date or datetime 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   3.2.0
	 * @link    http://www.sqlite.org/lang_datefunc.html
	 */
	public function dateAdd($date, $interval, $datePart)
	{
		// SQLite does not support microseconds as a separate unit. Convert the
interval to seconds
		if (strcasecmp($datePart, 'microseconds') == 0)
		{
			// Force the dot as a decimal point
			$interval = str_replace(',', '.', .001 * $interval);

			$datePart = 'seconds';
		}

		if (substr($interval, 0, 1) != '-')
		{
			return "datetime('" . $date . "', '+"
. $interval . " " . $datePart . "')";
		}
		else
		{
			return "datetime('" . $date . "', '"
. $interval . " " . $datePart . "')";
		}
	}

	/**
	 * Gets the current date and time.
	 *
	 * Usage:
	 * $query->where('published_up <
'.$query->currentTimestamp());
	 *
	 * @return  string
	 *
	 * @since   3.4
	 */
	public function currentTimestamp()
	{
		return 'CURRENT_TIMESTAMP';
	}

	/**
	 * Magic function to convert the query to a string.
	 *
	 * @return  string  The completed query.
	 *
	 * @since   1.7.0
	 */
	public function __toString()
	{
		switch ($this->type)
		{
			case 'select':
				if ($this->selectRowNumber)
				{
					$orderBy          = $this->selectRowNumber['orderBy'];
					$orderColumnAlias =
$this->selectRowNumber['orderColumnAlias'];

					$column = "ROW_NUMBER() AS $orderColumnAlias";

					if ($this->select === null)
					{
						$query = PHP_EOL . "SELECT 1"
							. (string) $this->from
							. (string) $this->where;
					}
					else
					{
						$tmpOffset    = $this->offset;
						$tmpLimit     = $this->limit;
						$this->offset = 0;
						$this->limit  = 0;
						$tmpOrder    = $this->order;
						$this->order = null;
						$query       = parent::__toString();
						$column      = "w.*, $column";
						$this->order = $tmpOrder;
						$this->offset = $tmpOffset;
						$this->limit  = $tmpLimit;
					}

					// Special sqlite query to count ROW_NUMBER
					$query = PHP_EOL . "SELECT $column"
						. PHP_EOL . "FROM ($query" . PHP_EOL . "ORDER BY
$orderBy"
						. PHP_EOL . ") AS w,(SELECT ROW_NUMBER(0)) AS r"
						// Forbid to flatten subqueries.
						. ((string) $this->order ?: PHP_EOL . 'ORDER BY NULL');

					return $this->processLimit($query, $this->limit,
$this->offset);
				}

				break;

			case 'update':
				if ($this->join)
				{
					$table = $this->update->getElements();
					$table = $table[0];

					$tableName = explode(' ', $table);
					$tableName = $tableName[0];

					if ($this->columns === null)
					{
						$fields = $this->db->getTableColumns($tableName);

						foreach ($fields as $key => $value)
						{
							$fields[$key] = $key;
						}

						$this->columns = new JDatabaseQueryElement('()',
$fields);
					}

					$fields   = $this->columns->getElements();
					$elements = $this->set->getElements();

					foreach ($elements as $nameValue)
					{
						$setArray = explode(' = ', $nameValue, 2);

						if ($setArray[0][0] === '`')
						{
							// Unquote column name
							$setArray[0] = substr($setArray[0], 1, -1);
						}

						$fields[$setArray[0]] = $setArray[1];
					}

					$select = new JDatabaseQuerySqlite($this->db);
					$select->select(array_values($fields))
						->from($table);

					$select->join  = $this->join;
					$select->where = $this->where;

					return 'INSERT OR REPLACE INTO ' . $tableName
						. ' (' . implode(',', array_keys($fields)) .
')'
						. (string) $select;
				}
		}

		return parent::__toString();
	}

	/**
	 * Return the number of the current row.
	 *
	 * @param   string  $orderBy           An expression of ordering for
window function.
	 * @param   string  $orderColumnAlias  An alias for new ordering column.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.7.0
	 * @throws  RuntimeException
	 */
	public function selectRowNumber($orderBy, $orderColumnAlias)
	{
		$this->validateRowNumber($orderBy, $orderColumnAlias);

		return $this;
	}
}
PK��[h��{�]�]query/sqlsrv.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since  1.7.0
 */
class JDatabaseQuerySqlsrv extends JDatabaseQuery implements
JDatabaseQueryLimitable
{
	/**
	 * The character(s) used to quote SQL statement names such as table names
or field names,
	 * etc.  The child classes should define this as necessary.  If a single
character string the
	 * same character is used for both sides of the quoted name, else the
first character will be
	 * used for the opening quote and the second for the closing quote.
	 *
	 * @var    string
	 * @since  1.7.0
	 */
	protected $name_quotes = '`';

	/**
	 * The null or zero representation of a timestamp for the database driver.
 This should be
	 * defined in child classes to hold the appropriate value for the engine.
	 *
	 * @var    string
	 * @since  1.7.0
	 */
	protected $null_date = '1900-01-01 00:00:00';

	/**
	 * @var    integer  The affected row limit for the current SQL statement.
	 * @since  3.2
	 */
	protected $limit = 0;

	/**
	 * @var    integer  The affected row offset to apply for the current SQL
statement.
	 * @since  3.2
	 */
	protected $offset = 0;

	/**
	 * Magic function to convert the query to a string.
	 *
	 * @return  string	The completed query.
	 *
	 * @since   1.7.0
	 */
	public function __toString()
	{
		$query = '';

		switch ($this->type)
		{
			case 'select':
				// Add required aliases for offset or fixGroupColumns method
				$columns = $this->fixSelectAliases();

				$query = (string) $this->select;

				if ($this->group)
				{
					$this->fixGroupColumns($columns);
				}

				$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->selectRowNumber === null)
				{
					if ($this->group)
					{
						$query .= (string) $this->group;
					}

					if ($this->having)
					{
						$query .= (string) $this->having;
					}
				}

				if ($this->order)
				{
					$query .= (string) $this->order;
				}

				if ($this instanceof JDatabaseQueryLimitable &&
($this->limit > 0 || $this->offset > 0))
				{
					$query = $this->processLimit($query, $this->limit,
$this->offset);
				}

				break;

			case 'insert':
				$query .= (string) $this->insert;

				// Set method
				if ($this->set)
				{
					$query .= (string) $this->set;
				}
				// Columns-Values method
				elseif ($this->values)
				{
					if ($this->columns)
					{
						$query .= (string) $this->columns;
					}

					$elements = $this->insert->getElements();
					$tableName = array_shift($elements);

					$query .= 'VALUES ';
					$query .= (string) $this->values;

					if ($this->autoIncrementField)
					{
						$query = 'SET IDENTITY_INSERT ' . $tableName . '
ON;' . $query . 'SET IDENTITY_INSERT ' . $tableName . '
OFF;';
					}

					if ($this->where)
					{
						$query .= (string) $this->where;
					}
				}

				break;

			case 'delete':
				$query .= (string) $this->delete;
				$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->order)
				{
					$query .= (string) $this->order;
				}

				break;

			case 'update':
				if ($this->join)
				{
					$tmpUpdate    = $this->update;
					$tmpFrom      = $this->from;
					$this->update = null;
					$this->from   = null;

					$updateElem  = $tmpUpdate->getElements();
					$updateArray = explode(' ', $updateElem[0]);

					// Use table alias if exists
					$this->update(end($updateArray));
					$this->from($updateElem[0]);

					$query .= (string) $this->update;
					$query .= (string) $this->set;
					$query .= (string) $this->from;

					$this->update = $tmpUpdate;
					$this->from   = $tmpFrom;

					// Special case for joins
					foreach ($this->join as $join)
					{
						$query .= (string) $join;
					}
				}
				else
				{
					$query .= (string) $this->update;
					$query .= (string) $this->set;
				}

				if ($this->where)
				{
					$query .= (string) $this->where;
				}

				if ($this->order)
				{
					$query .= (string) $this->order;
				}

				break;

			default:
				$query = parent::__toString();

				break;
		}

		return $query;
	}

	/**
	 * Casts a value to a char.
	 *
	 * Ensure that the value is properly quoted before passing to the method.
	 *
	 * @param   string  $value  The value to cast as a char.
	 *
	 * @param   string  $len    The length of the char.
	 *
	 * @return  string  Returns the cast value.
	 *
	 * @since   1.7.0
	 */
	public function castAsChar($value, $len = null)
	{
		if (!$len)
		{
			return 'CAST(' . $value . ' as NVARCHAR(30))';
		}
		else
		{
			return 'CAST(' . $value . ' as NVARCHAR(' . $len .
'))';
		}
	}

	/**
	 * Gets the function to determine the length of a character string.
	 *
	 * @param   string  $field      A value.
	 * @param   string  $operator   Comparison operator between charLength
integer value and $condition
	 * @param   string  $condition  Integer value to compare charLength with.
	 *
	 * @return  string  The required char length call.
	 *
	 * @since   1.7.0
	 */
	public function charLength($field, $operator = null, $condition = null)
	{
		return 'DATALENGTH(' . $field . ')' .
(isset($operator) && isset($condition) ? ' ' . $operator
. ' ' . $condition : '');
	}

	/**
	 * Concatenates an array of column names or values.
	 *
	 * @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   1.7.0
	 */
	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
	 *
	 * @since   1.7.0
	 */
	public function currentTimestamp()
	{
		return 'GETDATE()';
	}

	/**
	 * Get the length of a string in bytes.
	 *
	 * @param   string  $value  The string to measure.
	 *
	 * @return  integer
	 *
	 * @since   1.7.0
	 */
	public function length($value)
	{
		return 'LEN(' . $value . ')';
	}

	/**
	 * Add to the current date and time.
	 * 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; type may be time or
datetime.
	 * @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   3.2.0
	 * @note    Not all drivers support all units.
	 * @link    http://msdn.microsoft.com/en-us/library/ms186819.aspx for more
information
	 */
	public function dateAdd($date, $interval, $datePart)
	{
		return 'DATEADD(' . $datePart . ', ' . $interval .
', ' . $date . ')';
	}

	/**
	 * 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   3.0.0
	 */
	public function processLimit($query, $limit, $offset = 0)
	{
		if ($limit)
		{
			$total = $offset + $limit;

			$position = stripos($query, 'SELECT');
			$distinct = stripos($query, 'SELECT DISTINCT');

			if ($position === $distinct)
			{
				$query = substr_replace($query, 'SELECT DISTINCT TOP ' .
(int) $total, $position, 15);
			}
			else
			{
				$query = substr_replace($query, 'SELECT TOP ' . (int) $total,
$position, 6);
			}
		}

		if (!$offset)
		{
			return $query;
		}

		return PHP_EOL
			. 'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0))
AS RowNumber FROM ('
			. $query
			. PHP_EOL . ') AS A) AS A WHERE RowNumber > ' . (int)
$offset;
	}

	/**
	 * 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  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setLimit($limit = 0, $offset = 0)
	{
		$this->limit  = (int) $limit;
		$this->offset = (int) $offset;

		return $this;
	}

	/**
	 * Split a string of sql expression into an array of individual columns.
	 * Single line or line end comments and multi line comments are stripped
off.
	 * Always return at least one column.
	 *
	 * @param   string  $string  Input string of sql expression like select
expression.
	 *
	 * @return  array[]  The columns from the input string separated into an
array.
	 *
	 * @since   3.7.0
	 */
	protected function splitSqlExpression($string)
	{
		// Append whitespace as equivalent to the last comma
		$string .= ' ';

		$colIdx    = 0;
		$start     = 0;
		$open      = false;
		$openC     = 0;
		$comment   = false;
		$endString = '';
		$length    = strlen($string);
		$columns   = array();
		$column    = array();
		$current   = '';
		$previous  = null;
		$operators = array(
			'+' => '',
			'-' => '',
			'*' => '',
			'/' => '',
			'%' => '',
			'&' => '',
			'|' => '',
			'~' => '',
			'^' => '',
		);

		$addBlock = function ($block) use (&$column, &$colIdx)
		{
			if (isset($column[$colIdx]))
			{
				$column[$colIdx] .= $block;
			}
			else
			{
				$column[$colIdx] = $block;
			}
		};

		for ($i = 0; $i < $length; $i++)
		{
			$current      = substr($string, $i, 1);
			$current2     = substr($string, $i, 2);
			$current3     = substr($string, $i, 3);
			$lenEndString = strlen($endString);
			$testEnd      = substr($string, $i, $lenEndString);

			if ($current == '[' || $current == '"' ||
$current == "'" || $current2 == '--'
				|| ($current2 == '/*') || ($current == '#'
&& $current3 != '#__')
				|| ($lenEndString && $testEnd == $endString))
			{
				if ($open)
				{
					if ($testEnd === $endString)
					{
						if ($comment)
						{
							if ($lenEndString > 1)
							{
								$i += ($lenEndString - 1);
							}

							// Move cursor after close tag of comment
							$start = $i + 1;
							$comment = false;
						}
						elseif ($current == "'" || $current == ']'
|| $current == '"')
						{
							// Check for escaped quote like '', ]] or ""
							$n = 1;

							while ($i + $n < $length && $string[$i + $n] == $current)
							{
								$n++;
							}

							// Jump to the last quote
							$i += $n - 1;

							if ($n % 2 === 0)
							{
								// There is only escaped quote
								continue;
							}
							elseif ($n > 2)
							{
								// The last right close quote is not escaped
								$current = $string[$i];
							}
						}

						$open = false;
						$endString = '';
					}
				}
				else
				{
					$open = true;

					if ($current == '#' || $current2 == '--')
					{
						$endString = "\n";
						$comment = true;
					}
					elseif ($current2 == '/*')
					{
						$endString = '*/';
						$comment = true;
					}
					elseif ($current == '[')
					{
						$endString = ']';
					}
					else
					{
						$endString = $current;
					}

					if ($comment && $start < $i)
					{
						// Add string exists before comment
						$addBlock(substr($string, $start, $i - $start));
						$previous = $string[$i - 1];
						$start = $i;
					}
				}
			}
			elseif (!$open)
			{
				if ($current == '(')
				{
					$openC++;
					$previous = $current;
				}
				elseif ($current == ')')
				{
					$openC--;
					$previous = $current;
				}
				elseif ($current == '.')
				{
					if ($i === $start && $colIdx > 0 &&
!isset($column[$colIdx]))
					{
						// Remove whitepace placed before dot
						$colIdx--;
					}

					$previous = $current;
				}
				elseif ($openC === 0)
				{
					if (ctype_space($current))
					{
						// Normalize whitepace
						$string[$i] = ' ';

						if ($start < $i)
						{
							// Add text placed before whitespace
							$addBlock(substr($string, $start, $i - $start));
							$colIdx++;
							$previous = $string[$i - 1];
						}
						elseif (isset($column[$colIdx]))
						{
							if ($colIdx > 1 || !isset($operators[$previous]))
							{
								// There was whitespace after comment
								$colIdx++;
							}
						}

						// Move cursor forward
						$start = $i + 1;
					}
					elseif (isset($operators[$current]) && ($current !==
'*' || $previous !== '.'))
					{
						if ($start < $i)
						{
							// Add text before operator
							$addBlock(substr($string, $start, $i - $start));
							$colIdx++;
						}
						elseif (!isset($column[$colIdx]) &&
isset($operators[$previous]))
						{
							// Do not create whitespace between operators
							$colIdx--;
						}

						// Add operator
						$addBlock($current);
						$previous = $current;
						$colIdx++;

						// Move cursor forward
						$start = $i + 1;
					}
					else
					{
						$previous = $current;
					}
				}
			}

			if (($current == ',' && !$open && $openC == 0)
|| $i == $length - 1)
			{
				if ($start < $i && !$comment)
				{
					// Save remaining text
					$addBlock(substr($string, $start, $i - $start));
				}

				$columns[] = $column;

				// Reset values
				$column   = array();
				$colIdx   = 0;
				$previous = null;

				// Column saved, move cursor forward after comma
				$start = $i + 1;
			}
		}

		return $columns;
	}

	/**
	 * Add required aliases to columns for select statement in subquery.
	 *
	 * @return  array[]  Array of columns with added missing aliases.
	 *
	 * @since   3.7.0
	 */
	protected function fixSelectAliases()
	{
		$operators = array(
			'+' => '',
			'-' => '',
			'*' => '',
			'/' => '',
			'%' => '',
			'&' => '',
			'|' => '',
			'~' => '',
			'^' => '',
		);

		// Split into array and remove comments
		$columns = $this->splitSqlExpression(implode(',',
$this->select->getElements()));

		foreach ($columns as $i => $column)
		{
			$size = count($column);

			if ($size == 0)
			{
				continue;
			}

			if ($size > 2 && strcasecmp($column[$size - 2],
'AS') === 0)
			{
				// Alias exists, replace it to uppercase
				$columns[$i][$size - 2] = 'AS';
				continue;
			}

			if ($i == 0 && stripos(' DISTINCT ALL ', "
$column[0] ") !== false)
			{
				// This words are reserved, they are not column names
				array_shift($column);
				$size--;
			}

			$lastWord = strtoupper($column[$size - 1]);
			$length   = strlen($lastWord);
			$lastChar = $lastWord[$length - 1];

			if ($lastChar == '*')
			{
				// Skip on wildcard
				continue;
			}

			if ($lastChar == ')'
				|| ($size == 1 && $lastChar == "'")
				|| $lastWord[0] == '@'
				|| $lastWord == 'NULL'
				|| $lastWord == 'END'
				|| is_numeric($lastWord))
			{
				/* Ends with:
				 * - SQL function
				 * - single static value like 'only '+'string'
				 * - @@var
				 * - NULL
				 * - CASE ... END
				 * - Numeric
				 */
				$columns[$i][] = 'AS';
				$columns[$i][] = $this->quoteName('columnAlias' . $i);
				continue;
			}

			if ($size == 1)
			{
				continue;
			}

			$lastChar2 = substr($column[$size - 2], -1);

			// Check if column ends with  '- a.x' or '- a. x'
			if (isset($operators[$lastChar2])
				|| ($size > 2 && $lastChar2 === '.' &&
isset($operators[substr($column[$size - 3], -1)])))
			{
				// Ignore plus signs if column start with them
				if ($size != 2 || ltrim($column[0], '+') !== '' ||
$column[1][0] === "'")
				{
					// If operator exists before last word then alias is required for
subquery
					$columns[$i][] = 'AS';
					$columns[$i][] = $this->quoteName('columnAlias' . $i);
					continue;
				}
			}
			elseif ($column[$size - 1][0] !== '.' && $lastChar2
!== '.')
			{
				// If columns is like name name2 then second word is alias.
				// Add missing AS before the alias, exception for 'a. x' and
'a .x'
				array_splice($columns[$i], -1, 0, 'AS');
			}
		}

		$selectColumns = array();

		foreach ($columns as $i => $column)
		{
			$selectColumns[$i] = implode(' ', $column);
		}

		$this->select = new JDatabaseQueryElement('SELECT',
$selectColumns);

		return $columns;
	}

	/**
	 * Add missing columns names to GROUP BY clause.
	 *
	 * @param   array[]  $selectColumns  Array of columns from
splitSqlExpression method.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.7.0
	 */
	protected function fixGroupColumns($selectColumns)
	{
		// Cache tables columns
		static $cacheCols = array();

		// Known columns of all included tables
		$knownColumnsByAlias = array();

		$iquotes  = array('"' => '', '['
=> '', "'" => '');
		$nquotes = array('"', '[', ']');

		// Aggregate functions
		$aFuncs = array(
			'AVG(',
			'CHECKSUM_AGG(',
			'COUNT(',
			'COUNT_BIG(',
			'GROUPING(',
			'GROUPING_ID(',
			'MIN(',
			'MAX(',
			'SUM(',
			'STDEV(',
			'STDEVP(',
			'VAR(',
			'VARP(',
		);

		// Aggregated columns
		$filteredColumns = array();

		// Aliases found in SELECT statement
		$knownAliases = array();
		$wildcardTables = array();

		foreach ($selectColumns as $i => $column)
		{
			$size = count($column);

			if ($size === 0)
			{
				continue;
			}

			if ($i == 0 && stripos(' DISTINCT ALL ', "
$column[0] ") !== false)
			{
				// These words are reserved, they are not column names
				array_shift($selectColumns[0]);
				array_shift($column);
				$size--;
			}

			if ($size > 2 && $column[$size - 2] === 'AS')
			{
				// Save and remove AS alias
				$alias = $column[$size - 1];

				if (isset($iquotes[$alias[0]]))
				{
					$alias = substr($alias, 1, -1);
				}

				// Remove alias
				$selectColumns[$i] = $column = array_slice($column, 0, -2);

				if ($size === 3 || ($size === 4 &&
strpos('+-*/%&|~^', $column[0][0]) !== false))
				{
					$lastWord = $column[$size - 3];

					if ($lastWord[0] === "'" || $lastWord ===
'NULL' || is_numeric($lastWord))
					{
						unset($selectColumns[$i]);

						continue;
					}
				}

				// Remember pair alias => column expression
				$knownAliases[$alias] = implode(' ', $column);
			}

			$aggregated = false;

			foreach ($column as $j => $block)
			{
				if (substr($block, -2) === '.*')
				{
					// Found column ends with .*
					if (isset($iquotes[$block[0]]))
					{
						// Quoted table
						$wildcardTables[] = substr($block, 1, -3);
					}
					else
					{
						$wildcardTables[] = substr($block, 0, -2);
					}
				}
				elseif (str_ireplace($aFuncs, '', $block) != $block)
				{
					$aggregated = true;
				}

				if ($block[0] === "'")
				{
					// Shrink static strings which could contain column name
					$column[$j] = "''";
				}
			}

			if (!$aggregated)
			{
				// Without aggregated columns and aliases
				$filteredColumns[] = implode(' ', $selectColumns[$i]);
			}

			// Without aliases and static strings
			$selectColumns[$i] = implode(' ', $column);
		}

		// If select statement use table.* expression
		if ($wildcardTables)
		{
			// Split FROM statement into list of tables
			$tables = $this->splitSqlExpression(implode(',',
$this->from->getElements()));

			foreach ($tables as $i => $table)
			{
				$table = implode(' ', $table);

				// Exclude subquery from the FROM clause
				if (strpos($table, '(') === false)
				{
					// Unquote
					$table = str_replace($nquotes, '', $table);
					$table = str_replace('#__', $this->db->getPrefix(),
$table);
					$table = explode(' ', $table);
					$alias = end($table);
					$table = $table[0];

					// Chek if exists a wildcard with current alias table?
					if (in_array($alias, $wildcardTables, true))
					{
						if (!isset($cacheCols[$table]))
						{
							$cacheCols[$table] = $this->db->getTableColumns($table);
						}

						if ($this->join || $table != $alias)
						{
							foreach ($cacheCols[$table] as $name => $type)
							{
								$knownColumnsByAlias[$alias][] = $alias . '.' . $name;
							}
						}
						else
						{
							foreach ($cacheCols[$table] as $name => $type)
							{
								$knownColumnsByAlias[$alias][] = $name;
							}
						}
					}
				}
			}

			// Now we need to get all tables from any joins
			// Go through all joins and add them to the tables array
			if ($this->join)
			{
				foreach ($this->join as $join)
				{
					// Unquote and replace prefix
					$joinTbl = str_replace($nquotes, '', (string) $join);
					$joinTbl = str_replace("#__", $this->db->getPrefix(),
$joinTbl);

					// Exclude subquery
					if (preg_match('/JOIN\s+(\w+)(?:\s+AS)?(?:\s+(\w+))?/i',
$joinTbl, $matches))
					{
						$table = $matches[1];
						$alias = isset($matches[2]) ? $matches[2] : $table;

						// Chek if exists a wildcard with current alias table?
						if (in_array($alias, $wildcardTables, true))
						{
							if (!isset($cacheCols[$table]))
							{
								$cacheCols[$table] = $this->db->getTableColumns($table);
							}

							foreach ($cacheCols[$table] as $name => $type)
							{
								$knownColumnsByAlias[$alias][] = $alias . '.' . $name;
							}
						}
					}
				}
			}
		}

		$selectExpression = implode(',', $selectColumns);

		// Split into the right columns
		$groupColumns = $this->splitSqlExpression(implode(',',
$this->group->getElements()));

		// Remove column aliases from GROUP statement - SQLSRV does not support
it
		foreach ($groupColumns as $i => $column)
		{
			$groupColumns[$i] = implode(' ', $column);
			$column = str_replace($nquotes, '', $groupColumns[$i]);

			if (isset($knownAliases[$column]))
			{
				// Be sure that this is not a valid column name
				if (!preg_match('/\b' . preg_quote($column, '/') .
'\b/', $selectExpression))
				{
					// Replace column alias by column expression
					$groupColumns[$i] = $knownAliases[$column];
				}
			}
		}

		// Find all alias.* and fill with proper table column names
		foreach ($filteredColumns as $i => $column)
		{
			if (substr($column, -2) === '.*')
			{
				unset($filteredColumns[$i]);

				// Extract alias.* columns into GROUP BY statement
				$groupColumns = array_merge($groupColumns,
$knownColumnsByAlias[substr($column, 0, -2)]);
			}
		}

		$groupColumns = array_merge($groupColumns, $filteredColumns);

		if ($this->order)
		{
			// Remove direction suffixes
			$dir = array(" DESC\v", " ASC\v");

			$orderColumns = $this->splitSqlExpression(implode(',',
$this->order->getElements()));

			foreach ($orderColumns as $i => $column)
			{
				$column = implode(' ', $column);
				$orderColumns[$i] = $column = trim(str_ireplace($dir, '',
"$column\v"), "\v");

				if (isset($knownAliases[str_replace($nquotes, '', $column)]))
				{
					unset($orderColumns[$i]);
				}

				if (str_ireplace($aFuncs, '', $column) != $column)
				{
					// Do not add aggregate expression
					unset($orderColumns[$i]);
				}
			}

			$groupColumns = array_merge($groupColumns, $orderColumns);
		}

		// Get a unique string of all column names that need to be included in
the group statement
		$this->group = new JDatabaseQueryElement('GROUP BY',
array_unique($groupColumns));

		return $this;
	}

	/**
	 * Return correct rand() function for MSSQL.
	 *
	 * Ensure that the rand() function is MSSQL compatible.
	 *
	 * Usage:
	 * $query->Rand();
	 *
	 * @return  string  The correct rand function.
	 *
	 * @since   3.5
	 */
	public function Rand()
	{
		return ' NEWID() ';
	}
}
PK��[�_�����	query.phpnu�[���<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Query Building Class.
 *
 * @since  1.7.0
 *
 * @method      string  q()   q($text, $escape = true)  Alias for quote
method
 * @method      string  qn()  qn($name, $as = null)     Alias for quoteName
method
 * @method      string  e()   e($text, $extra = false)  Alias for escape
method
 * @property-read   JDatabaseQueryElement  $type
 * @property-read   JDatabaseQueryElement  $select
 * @property-read   JDatabaseQueryElement  $group
 * @property-read   JDatabaseQueryElement  $having
 */
abstract class JDatabaseQuery
{
	/**
	 * @var    JDatabaseDriver  The database driver.
	 * @since  1.7.0
	 */
	protected $db = null;

	/**
	 * @var    string  The SQL query (if a direct query string was provided).
	 * @since  3.0.0
	 */
	protected $sql = null;

	/**
	 * @var    string  The query type.
	 * @since  1.7.0
	 */
	protected $type = '';

	/**
	 * @var    JDatabaseQueryElement  The query element for a generic query
(type = null).
	 * @since  1.7.0
	 */
	protected $element = null;

	/**
	 * @var    JDatabaseQueryElement  The select element.
	 * @since  1.7.0
	 */
	protected $select = null;

	/**
	 * @var    JDatabaseQueryElement  The delete element.
	 * @since  1.7.0
	 */
	protected $delete = null;

	/**
	 * @var    JDatabaseQueryElement  The update element.
	 * @since  1.7.0
	 */
	protected $update = null;

	/**
	 * @var    JDatabaseQueryElement  The insert element.
	 * @since  1.7.0
	 */
	protected $insert = null;

	/**
	 * @var    JDatabaseQueryElement  The from element.
	 * @since  1.7.0
	 */
	protected $from = null;

	/**
	 * @var    JDatabaseQueryElement  The join element.
	 * @since  1.7.0
	 */
	protected $join = null;

	/**
	 * @var    JDatabaseQueryElement  The set element.
	 * @since  1.7.0
	 */
	protected $set = null;

	/**
	 * @var    JDatabaseQueryElement  The where element.
	 * @since  1.7.0
	 */
	protected $where = null;

	/**
	 * @var    JDatabaseQueryElement  The group by element.
	 * @since  1.7.0
	 */
	protected $group = null;

	/**
	 * @var    JDatabaseQueryElement  The having element.
	 * @since  1.7.0
	 */
	protected $having = null;

	/**
	 * @var    JDatabaseQueryElement  The column list for an INSERT statement.
	 * @since  1.7.0
	 */
	protected $columns = null;

	/**
	 * @var    JDatabaseQueryElement  The values list for an INSERT statement.
	 * @since  1.7.0
	 */
	protected $values = null;

	/**
	 * @var    JDatabaseQueryElement  The order element.
	 * @since  1.7.0
	 */
	protected $order = null;

	/**
	 * @var   object  The auto increment insert field element.
	 * @since 1.7.0
	 */
	protected $autoIncrementField = null;

	/**
	 * @var    JDatabaseQueryElement  The call element.
	 * @since  3.0.0
	 */
	protected $call = null;

	/**
	 * @var    JDatabaseQueryElement  The exec element.
	 * @since  3.0.0
	 */
	protected $exec = null;

	/**
	 * @var    JDatabaseQueryElement  The union element.
	 * @since  3.0.0
	 * @deprecated  4.0  Will be transformed and moved to $merge variable.
	 */
	protected $union = null;

	/**
	 * @var    JDatabaseQueryElement  The unionAll element.
	 * @since  3.2.0
	 * @deprecated  4.0  Will be transformed and moved to $merge variable.
	 */
	protected $unionAll = null;

	/**
	 * @var    array  Details of window function.
	 * @since  3.7.0
	 */
	protected $selectRowNumber = null;

	/**
	 * Magic method to provide method alias support for quote() and
quoteName().
	 *
	 * @param   string  $method  The called method.
	 * @param   array   $args    The array of arguments passed to the method.
	 *
	 * @return  string  The aliased method's return value or null.
	 *
	 * @since   1.7.0
	 */
	public function __call($method, $args)
	{
		if (empty($args))
		{
			return;
		}

		switch ($method)
		{
			case 'q':
				return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
				break;

			case 'qn':
				return $this->quoteName($args[0], isset($args[1]) ? $args[1] :
null);
				break;

			case 'e':
				return $this->escape($args[0], isset($args[1]) ? $args[1] : false);
				break;
		}
	}

	/**
	 * Class constructor.
	 *
	 * @param   JDatabaseDriver  $db  The database driver.
	 *
	 * @since   1.7.0
	 */
	public function __construct(JDatabaseDriver $db = null)
	{
		$this->db = $db;
	}

	/**
	 * Magic function to convert the query to a string.
	 *
	 * @return  string	The completed query.
	 *
	 * @since   1.7.0
	 */
	public function __toString()
	{
		$query = '';

		if ($this->sql)
		{
			return $this->sql;
		}

		switch ($this->type)
		{
			case 'element':
				$query .= (string) $this->element;
				break;

			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->selectRowNumber === null)
				{
					if ($this->group)
					{
						$query .= (string) $this->group;
					}

					if ($this->having)
					{
						$query .= (string) $this->having;
					}

					if ($this->union)
					{
						$query .= (string) $this->union;
					}

					if ($this->unionAll)
					{
						$query .= (string) $this->unionAll;
					}
				}

				if ($this->order)
				{
					$query .= (string) $this->order;
				}

				break;

			case 'delete':
				$query .= (string) $this->delete;
				$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->order)
				{
					$query .= (string) $this->order;
				}

				break;

			case 'update':
				$query .= (string) $this->update;

				if ($this->join)
				{
					// Special case for joins
					foreach ($this->join as $join)
					{
						$query .= (string) $join;
					}
				}

				$query .= (string) $this->set;

				if ($this->where)
				{
					$query .= (string) $this->where;
				}

				if ($this->order)
				{
					$query .= (string) $this->order;
				}

				break;

			case 'insert':
				$query .= (string) $this->insert;

				// Set method
				if ($this->set)
				{
					$query .= (string) $this->set;
				}
				// Columns-Values method
				elseif ($this->values)
				{
					if ($this->columns)
					{
						$query .= (string) $this->columns;
					}

					$elements = $this->values->getElements();

					if (!($elements[0] instanceof $this))
					{
						$query .= ' VALUES ';
					}

					$query .= (string) $this->values;
				}

				break;

			case 'call':
				$query .= (string) $this->call;
				break;

			case 'exec':
				$query .= (string) $this->exec;
				break;
		}

		if ($this instanceof JDatabaseQueryLimitable)
		{
			$query = $this->processLimit($query, $this->limit,
$this->offset);
		}

		return $query;
	}

	/**
	 * Magic function to get protected variable value
	 *
	 * @param   string  $name  The name of the variable.
	 *
	 * @return  mixed
	 *
	 * @since   1.7.0
	 */
	public function __get($name)
	{
		return isset($this->$name) ? $this->$name : null;
	}

	/**
	 * Add a single column, or array of columns to the CALL clause of the
query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 * The call method can, however, be called multiple times in the same
query.
	 *
	 * Usage:
	 * $query->call('a.*')->call('b.id');
	 * $query->call(array('a.*', 'b.id'));
	 *
	 * @param   mixed  $columns  A string or an array of field names.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function call($columns)
	{
		$this->type = 'call';

		if (is_null($this->call))
		{
			$this->call = new JDatabaseQueryElement('CALL', $columns);
		}
		else
		{
			$this->call->append($columns);
		}

		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   1.7.0
	 */
	public function castAsChar($value)
	{
		return $value;
	}

	/**
	 * Gets the number of characters in a string.
	 *
	 * Note, use 'length' to find the number of bytes in a string.
	 *
	 * Usage:
	 * $query->select($query->charLength('a'));
	 *
	 * @param   string  $field      A value.
	 * @param   string  $operator   Comparison operator between charLength
integer value and $condition
	 * @param   string  $condition  Integer value to compare charLength with.
	 *
	 * @return  string  The required char length call.
	 *
	 * @since   1.7.0
	 */
	public function charLength($field, $operator = null, $condition = null)
	{
		return 'CHAR_LENGTH(' . $field . ')' .
(isset($operator) && isset($condition) ? ' ' . $operator
. ' ' . $condition : '');
	}

	/**
	 * 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  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function clear($clause = null)
	{
		$this->sql = null;

		switch ($clause)
		{
			case 'select':
				$this->select = null;
				$this->type = null;
				$this->selectRowNumber = null;
				break;

			case 'delete':
				$this->delete = null;
				$this->type = null;
				break;

			case 'update':
				$this->update = null;
				$this->type = null;
				break;

			case 'insert':
				$this->insert = null;
				$this->type = null;
				$this->autoIncrementField = null;
				break;

			case 'from':
				$this->from = null;
				break;

			case 'join':
				$this->join = null;
				break;

			case 'set':
				$this->set = null;
				break;

			case 'where':
				$this->where = null;
				break;

			case 'group':
				$this->group = null;
				break;

			case 'having':
				$this->having = null;
				break;

			case 'order':
				$this->order = null;
				break;

			case 'columns':
				$this->columns = null;
				break;

			case 'values':
				$this->values = null;
				break;

			case 'exec':
				$this->exec = null;
				$this->type = null;
				break;

			case 'call':
				$this->call = null;
				$this->type = null;
				break;

			case 'limit':
				$this->offset = 0;
				$this->limit = 0;
				break;

			case 'offset':
				$this->offset = 0;
				break;

			case 'union':
				$this->union = null;
				break;

			case 'unionAll':
				$this->unionAll = null;
				break;

			default:
				$this->type = null;
				$this->select = null;
				$this->selectRowNumber = null;
				$this->delete = null;
				$this->update = null;
				$this->insert = null;
				$this->from = null;
				$this->join = null;
				$this->set = null;
				$this->where = null;
				$this->group = null;
				$this->having = null;
				$this->order = null;
				$this->columns = null;
				$this->values = null;
				$this->autoIncrementField = null;
				$this->exec = null;
				$this->call = null;
				$this->union = null;
				$this->unionAll = null;
				$this->offset = 0;
				$this->limit = 0;
				break;
		}

		return $this;
	}

	/**
	 * Adds a column, or array of column names that would be used for an
INSERT INTO statement.
	 *
	 * @param   mixed  $columns  A column name, or array of column names.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function columns($columns)
	{
		if (is_null($this->columns))
		{
			$this->columns = new JDatabaseQueryElement('()', $columns);
		}
		else
		{
			$this->columns->append($columns);
		}

		return $this;
	}

	/**
	 * 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   1.7.0
	 */
	public function concatenate($values, $separator = null)
	{
		if ($separator)
		{
			return 'CONCATENATE(' . implode(' || ' .
$this->quote($separator) . ' || ', $values) . ')';
		}
		else
		{
			return 'CONCATENATE(' . implode(' || ', $values) .
')';
		}
	}

	/**
	 * Gets the current date and time.
	 *
	 * Usage:
	 * $query->where('published_up <
'.$query->currentTimestamp());
	 *
	 * @return  string
	 *
	 * @since   1.7.0
	 */
	public function currentTimestamp()
	{
		return 'CURRENT_TIMESTAMP()';
	}

	/**
	 * Returns a PHP date() function compliant date format for the database
driver.
	 *
	 * This method is provided for use where the query object is passed to a
function for modification.
	 * If you have direct access to the database object, it is recommended you
use the getDateFormat method directly.
	 *
	 * @return  string  The format string.
	 *
	 * @since   1.7.0
	 */
	public function dateFormat()
	{
		if (!($this->db instanceof JDatabaseDriver))
		{
			throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
		}

		return $this->db->getDateFormat();
	}

	/**
	 * Creates a formatted dump of the query for debugging purposes.
	 *
	 * Usage:
	 * echo $query->dump();
	 *
	 * @return  string
	 *
	 * @since   1.7.3
	 */
	public function dump()
	{
		return '<pre class="jdatabasequery">' .
str_replace('#__', $this->db->getPrefix(), $this) .
'</pre>';
	}

	/**
	 * Add a table name to the DELETE clause of the query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 *
	 * Usage:
	 * $query->delete('#__a')->where('id = 1');
	 *
	 * @param   string  $table  The name of the table to delete from.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function delete($table = null)
	{
		$this->type = 'delete';
		$this->delete = new JDatabaseQueryElement('DELETE', null);

		if (!empty($table))
		{
			$this->from($table);
		}

		return $this;
	}

	/**
	 * Method to escape a string for usage in an SQL statement.
	 *
	 * This method is provided for use where the query object is passed to a
function for modification.
	 * If you have direct access to the database object, it is recommended you
use the escape method directly.
	 *
	 * Note that 'e' is an alias for this method as it is in
JDatabaseDriver.
	 *
	 * @param   string   $text   The string to be escaped.
	 * @param   boolean  $extra  Optional parameter to provide extra escaping.
	 *
	 * @return  string  The escaped string.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException if the internal db property is not a valid
object.
	 */
	public function escape($text, $extra = false)
	{
		if (!($this->db instanceof JDatabaseDriver))
		{
			throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
		}

		return $this->db->escape($text, $extra);
	}

	/**
	 * Add a single column, or array of columns to the EXEC clause of the
query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 * The exec method can, however, be called multiple times in the same
query.
	 *
	 * Usage:
	 * $query->exec('a.*')->exec('b.id');
	 * $query->exec(array('a.*', 'b.id'));
	 *
	 * @param   mixed  $columns  A string or an array of field names.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function exec($columns)
	{
		$this->type = 'exec';

		if (is_null($this->exec))
		{
			$this->exec = new JDatabaseQueryElement('EXEC', $columns);
		}
		else
		{
			$this->exec->append($columns);
		}

		return $this;
	}

	/**
	 * Add a table to the FROM clause of the query.
	 *
	 * Note that while an array of tables can be provided, it is recommended
you use explicit joins.
	 *
	 * Usage:
	 * $query->select('*')->from('#__a');
	 *
	 * @param   mixed   $tables         A string or array of table names.
	 *                                  This can be a JDatabaseQuery object
(or a child of it) when used
	 *                                  as a subquery in FROM clause along
with a value for $subQueryAlias.
	 * @param   string  $subQueryAlias  Alias used when $tables is a
JDatabaseQuery.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @throws  RuntimeException
	 *
	 * @since   1.7.0
	 */
	public function from($tables, $subQueryAlias = null)
	{
		if (is_null($this->from))
		{
			if ($tables instanceof $this)
			{
				if (is_null($subQueryAlias))
				{
					throw new
RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS');
				}

				$tables = '( ' . (string) $tables . ' ) AS ' .
$this->quoteName($subQueryAlias);
			}

			$this->from = new JDatabaseQueryElement('FROM', $tables);
		}
		else
		{
			$this->from->append($tables);
		}

		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   3.0.0
	 */
	public function year($date)
	{
		return 'YEAR(' . $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   3.0.0
	 */
	public function month($date)
	{
		return 'MONTH(' . $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   3.0.0
	 */
	public function day($date)
	{
		return 'DAY(' . $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   3.0.0
	 */
	public function hour($date)
	{
		return 'HOUR(' . $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   3.0.0
	 */
	public function minute($date)
	{
		return 'MINUTE(' . $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   3.0.0
	 */
	public function second($date)
	{
		return 'SECOND(' . $date . ')';
	}

	/**
	 * Add a grouping column to the GROUP clause of the query.
	 *
	 * Usage:
	 * $query->group('id');
	 *
	 * @param   mixed  $columns  A string or array of ordering columns.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function group($columns)
	{
		if (is_null($this->group))
		{
			$this->group = new JDatabaseQueryElement('GROUP BY',
$columns);
		}
		else
		{
			$this->group->append($columns);
		}

		return $this;
	}

	/**
	 * A conditions to the HAVING clause of the query.
	 *
	 * Usage:
	 * $query->group('id')->having('COUNT(id) >
5');
	 *
	 * @param   mixed   $conditions  A string or array of columns.
	 * @param   string  $glue        The glue by which to join the conditions.
Defaults to AND.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function having($conditions, $glue = 'AND')
	{
		if (is_null($this->having))
		{
			$glue = strtoupper($glue);
			$this->having = new JDatabaseQueryElement('HAVING',
$conditions, " $glue ");
		}
		else
		{
			$this->having->append($conditions);
		}

		return $this;
	}

	/**
	 * Add an INNER JOIN clause to the query.
	 *
	 * Usage:
	 * $query->innerJoin('b ON b.id =
a.id')->innerJoin('c ON c.id = b.id');
	 *
	 * @param   string  $condition  The join condition.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function innerJoin($condition)
	{
		$this->join('INNER', $condition);

		return $this;
	}

	/**
	 * Add a table name to the INSERT clause of the query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 *
	 * Usage:
	 * $query->insert('#__a')->set('id = 1');
	 * $query->insert('#__a')->columns('id,
title')->values('1,2')->values('3,4');
	 * $query->insert('#__a')->columns('id,
title')->values(array('1,2', '3,4'));
	 *
	 * @param   mixed    $table           The name of the table to insert data
into.
	 * @param   boolean  $incrementField  The name of the field to auto
increment.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function insert($table, $incrementField=false)
	{
		$this->type = 'insert';
		$this->insert = new JDatabaseQueryElement('INSERT INTO',
$table);
		$this->autoIncrementField = $incrementField;

		return $this;
	}

	/**
	 * Add a JOIN clause to the query.
	 *
	 * Usage:
	 * $query->join('INNER', 'b ON b.id = a.id);
	 *
	 * @param   string  $type        The type of join. This string is
prepended to the JOIN keyword.
	 * @param   string  $conditions  A string or array of conditions.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function join($type, $conditions)
	{
		if (is_null($this->join))
		{
			$this->join = array();
		}

		$this->join[] = new JDatabaseQueryElement(strtoupper($type) . '
JOIN', $conditions);

		return $this;
	}

	/**
	 * Add a LEFT JOIN clause to the query.
	 *
	 * Usage:
	 * $query->leftJoin('b ON b.id = a.id')->leftJoin('c
ON c.id = b.id');
	 *
	 * @param   string  $condition  The join condition.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function leftJoin($condition)
	{
		$this->join('LEFT', $condition);

		return $this;
	}

	/**
	 * Get the length of a string in bytes.
	 *
	 * Note, use 'charLength' to find the number of characters in a
string.
	 *
	 * Usage:
	 * query->where($query->length('a').' > 3');
	 *
	 * @param   string  $value  The string to measure.
	 *
	 * @return  int
	 *
	 * @since   1.7.0
	 */
	public function length($value)
	{
		return 'LENGTH(' . $value . ')';
	}

	/**
	 * Get the null or zero representation of a timestamp for the database
driver.
	 *
	 * This method is provided for use where the query object is passed to a
function for modification.
	 * If you have direct access to the database object, it is recommended you
use the nullDate method directly.
	 *
	 * Usage:
	 * $query->where('modified_date <>
'.$query->nullDate());
	 *
	 * @param   boolean  $quoted  Optionally wraps the null date in database
quotes (true by default).
	 *
	 * @return  string  Null or zero representation of a timestamp.
	 *
	 * @since   1.7.0
	 */
	public function nullDate($quoted = true)
	{
		if (!($this->db instanceof JDatabaseDriver))
		{
			throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
		}

		$result = $this->db->getNullDate($quoted);

		if ($quoted)
		{
			return $this->db->quote($result);
		}

		return $result;
	}

	/**
	 * Add an ordering column to the ORDER clause of the query.
	 *
	 * Usage:
	 * $query->order('foo')->order('bar');
	 * $query->order(array('foo','bar'));
	 *
	 * @param   mixed  $columns  A string or array of ordering columns.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function order($columns)
	{
		if (is_null($this->order))
		{
			$this->order = new JDatabaseQueryElement('ORDER BY',
$columns);
		}
		else
		{
			$this->order->append($columns);
		}

		return $this;
	}

	/**
	 * Add an OUTER JOIN clause to the query.
	 *
	 * Usage:
	 * $query->outerJoin('b ON b.id =
a.id')->outerJoin('c ON c.id = b.id');
	 *
	 * @param   string  $condition  The join condition.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function outerJoin($condition)
	{
		$this->join('OUTER', $condition);

		return $this;
	}

	/**
	 * Method to quote and optionally escape a string to database requirements
for insertion into the database.
	 *
	 * This method is provided for use where the query object is passed to a
function for modification.
	 * If you have direct access to the database object, it is recommended you
use the quote method directly.
	 *
	 * Note that 'q' is an alias for this method as it is in
JDatabaseDriver.
	 *
	 * Usage:
	 * $query->quote('fulltext');
	 * $query->q('fulltext');
	 * $query->q(array('option', 'fulltext'));
	 *
	 * @param   mixed    $text    A string or an array of strings to quote.
	 * @param   boolean  $escape  True to escape the string, false to leave it
unchanged.
	 *
	 * @return  string  The quoted input string.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException if the internal db property is not a valid
object.
	 */
	public function quote($text, $escape = true)
	{
		if (!($this->db instanceof JDatabaseDriver))
		{
			throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
		}

		return $this->db->quote($text, $escape);
	}

	/**
	 * Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
	 * risks and reserved word conflicts.
	 *
	 * This method is provided for use where the query object is passed to a
function for modification.
	 * If you have direct access to the database object, it is recommended you
use the quoteName method directly.
	 *
	 * Note that 'qn' is an alias for this method as it is in
JDatabaseDriver.
	 *
	 * Usage:
	 * $query->quoteName('#__a');
	 * $query->qn('#__a');
	 *
	 * @param   mixed  $name  The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
	 *                        Each type supports dot-notation name.
	 * @param   mixed  $as    The AS query part associated to $name. It can be
string or array, in latter case it has to be
	 *                        same length of $name; if is null there will not
be any AS part for string or array element.
	 *
	 * @return  mixed  The quote wrapped name, same type of $name.
	 *
	 * @since   1.7.0
	 * @throws  RuntimeException if the internal db property is not a valid
object.
	 */
	public function quoteName($name, $as = null)
	{
		if (!($this->db instanceof JDatabaseDriver))
		{
			throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
		}

		return $this->db->quoteName($name, $as);
	}

	/**
	 * Add a RIGHT JOIN clause to the query.
	 *
	 * Usage:
	 * $query->rightJoin('b ON b.id =
a.id')->rightJoin('c ON c.id = b.id');
	 *
	 * @param   string  $condition  The join condition.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function rightJoin($condition)
	{
		$this->join('RIGHT', $condition);

		return $this;
	}

	/**
	 * Add a single column, or array of columns to the SELECT clause of the
query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 * The select method can, however, be called multiple times in the same
query.
	 *
	 * Usage:
	 * $query->select('a.*')->select('b.id');
	 * $query->select(array('a.*', 'b.id'));
	 *
	 * @param   mixed  $columns  A string or an array of field names.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function select($columns)
	{
		$this->type = 'select';

		if (is_null($this->select))
		{
			$this->select = new JDatabaseQueryElement('SELECT',
$columns);
		}
		else
		{
			$this->select->append($columns);
		}

		return $this;
	}

	/**
	 * Add a single condition string, or an array of strings to the SET clause
of the query.
	 *
	 * Usage:
	 * $query->set('a = 1')->set('b = 2');
	 * $query->set(array('a = 1', 'b = 2');
	 *
	 * @param   mixed   $conditions  A string or array of string conditions.
	 * @param   string  $glue        The glue by which to join the condition
strings. Defaults to ,.
	 *                               Note that the glue is set on first use
and cannot be changed.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function set($conditions, $glue = ',')
	{
		if (is_null($this->set))
		{
			$glue = strtoupper($glue);
			$this->set = new JDatabaseQueryElement('SET', $conditions,
"\n\t$glue ");
		}
		else
		{
			$this->set->append($conditions);
		}

		return $this;
	}

	/**
	 * Allows a direct query to be provided to the database
	 * driver's setQuery() method, but still allow queries
	 * to have bounded variables.
	 *
	 * Usage:
	 * $query->setQuery('select * from #__users');
	 *
	 * @param   mixed  $sql  An SQL Query
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.0.0
	 */
	public function setQuery($sql)
	{
		$this->sql = $sql;

		return $this;
	}

	/**
	 * Add a table name to the UPDATE clause of the query.
	 *
	 * Note that you must not mix insert, update, delete and select method
calls when building a query.
	 *
	 * Usage:
	 * $query->update('#__foo')->set(...);
	 *
	 * @param   string  $table  A table to update.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function update($table)
	{
		$this->type = 'update';
		$this->update = new JDatabaseQueryElement('UPDATE', $table);

		return $this;
	}

	/**
	 * Adds a tuple, or array of tuples that would be used as values for an
INSERT INTO statement.
	 *
	 * Usage:
	 * $query->values('1,2,3')->values('4,5,6');
	 * $query->values(array('1,2,3', '4,5,6'));
	 *
	 * @param   string  $values  A single tuple, or array of tuples.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function values($values)
	{
		if (is_null($this->values))
		{
			$this->values = new JDatabaseQueryElement('()', $values,
'),(');
		}
		else
		{
			$this->values->append($values);
		}

		return $this;
	}

	/**
	 * Add a single condition, or an array of conditions to the WHERE clause
of the query.
	 *
	 * Usage:
	 * $query->where('a = 1')->where('b = 2');
	 * $query->where(array('a = 1', 'b = 2'));
	 *
	 * @param   mixed   $conditions  A string or array of where conditions.
	 * @param   string  $glue        The glue by which to join the conditions.
Defaults to AND.
	 *                               Note that the glue is set on first use
and cannot be changed.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   1.7.0
	 */
	public function where($conditions, $glue = 'AND')
	{
		if (is_null($this->where))
		{
			$glue = strtoupper($glue);
			$this->where = new JDatabaseQueryElement('WHERE',
$conditions, " $glue ");
		}
		else
		{
			$this->where->append($conditions);
		}

		return $this;
	}

	/**
	 * Extend the WHERE clause with a single condition or an array of
conditions, with a potentially
	 * different logical operator from the one in the current WHERE clause.
	 *
	 * Usage:
	 * $query->where(array('a = 1', 'b =
2'))->extendWhere('XOR', array('c = 3', 'd
= 4'));
	 * will produce: WHERE ((a = 1 AND b = 2) XOR (c = 3 AND d = 4)
	 *
	 * @param   string  $outerGlue   The glue by which to join the conditions
to the current WHERE conditions.
	 * @param   mixed   $conditions  A string or array of WHERE conditions.
	 * @param   string  $innerGlue   The glue by which to join the conditions.
Defaults to AND.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.6
	 */
	public function extendWhere($outerGlue, $conditions, $innerGlue =
'AND')
	{
		// Replace the current WHERE with a new one which has the old one as an
unnamed child.
		$this->where = new JDatabaseQueryElement('WHERE',
$this->where->setName('()'), " $outerGlue ");

		// Append the new conditions as a new unnamed child.
		$this->where->append(new JDatabaseQueryElement('()',
$conditions, " $innerGlue "));

		return $this;
	}

	/**
	 * Extend the WHERE clause with an OR and a single condition or an array
of conditions.
	 *
	 * Usage:
	 * $query->where(array('a = 1', 'b =
2'))->orWhere(array('c = 3', 'd = 4'));
	 * will produce: WHERE ((a = 1 AND b = 2) OR (c = 3 AND d = 4)
	 *
	 * @param   mixed   $conditions  A string or array of WHERE conditions.
	 * @param   string  $glue        The glue by which to join the conditions.
Defaults to AND.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.6
	 */
	public function orWhere($conditions, $glue = 'AND')
	{
		return $this->extendWhere('OR', $conditions, $glue);
	}

	/**
	 * Extend the WHERE clause with an AND and a single condition or an array
of conditions.
	 *
	 * Usage:
	 * $query->where(array('a = 1', 'b =
2'))->andWhere(array('c = 3', 'd = 4'));
	 * will produce: WHERE ((a = 1 AND b = 2) AND (c = 3 OR d = 4)
	 *
	 * @param   mixed   $conditions  A string or array of WHERE conditions.
	 * @param   string  $glue        The glue by which to join the conditions.
Defaults to OR.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.6
	 */
	public function andWhere($conditions, $glue = 'OR')
	{
		return $this->extendWhere('AND', $conditions, $glue);
	}

	/**
	 * Method to provide deep copy support to nested objects and
	 * arrays when cloning.
	 *
	 * @return  void
	 *
	 * @since   1.7.3
	 */
	public function __clone()
	{
		foreach ($this as $k => $v)
		{
			if ($k === 'db')
			{
				continue;
			}

			if (is_object($v) || is_array($v))
			{
				$this->{$k} = unserialize(serialize($v));
			}
		}
	}

	/**
	 * Add a query to UNION with the current query.
	 * Multiple unions each require separate statements and create an array of
unions.
	 *
	 * Usage (the $query base query MUST be a select query):
	 * $query->union('SELECT name FROM  #__foo')
	 * $query->union('SELECT name FROM  #__foo', true)
	 * $query->union($query2)->union($query3)
	 *
	 * The $query attribute as an array is deprecated and will not be
supported in 4.0.
	 *
	 * $query->union(array('SELECT name FROM 
#__foo','SELECT name FROM  #__bar'))
	 * $query->union(array($query2, $query3))
	 *
	 * @param   mixed    $query     The JDatabaseQuery object or string to
union.
	 * @param   boolean  $distinct  True to only return distinct rows from the
union.
	 * @param   string   $glue      The glue by which to join the conditions.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @link http://dev.mysql.com/doc/refman/5.0/en/union.html
	 *
	 * @since   3.0.0
	 */
	public function union($query, $distinct = false, $glue = '')
	{
		// Set up the DISTINCT flag, the name with parentheses, and the glue.
		if ($distinct)
		{
			$name = 'UNION DISTINCT ()';
			$glue = ')' . PHP_EOL . 'UNION DISTINCT (';
		}
		else
		{
			$glue = ')' . PHP_EOL . 'UNION (';
			$name = 'UNION ()';
		}

		if (is_array($query))
		{
			JLog::add('Query attribute as an array is deprecated.',
JLog::WARNING, 'deprecated');
		}

		// Get the JDatabaseQueryElement if it does not exist
		if (is_null($this->union))
		{
			$this->union = new JDatabaseQueryElement($name, $query,
"$glue");
		}
		// Otherwise append the second UNION.
		else
		{
			$this->union->append($query);
		}

		return $this;
	}

	/**
	 * Add a query to UNION DISTINCT with the current query. Simply a proxy to
union with the DISTINCT keyword.
	 *
	 * Usage:
	 * $query->unionDistinct('SELECT name FROM  #__foo')
	 *
	 * @param   mixed   $query  The JDatabaseQuery object or string to union.
	 * @param   string  $glue   The glue by which to join the conditions.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @see     union
	 *
	 * @since   3.0.0
	 * @deprecated  4.0  Use union() instead.
	 */
	public function unionDistinct($query, $glue = '')
	{
		$distinct = true;

		// Apply the distinct flag to the union.
		return $this->union($query, $distinct, $glue);
	}

	/**
	 * Find and replace sprintf-like tokens in a format string.
	 * Each token takes one of the following forms:
	 *     %%       - A literal percent character.
	 *     %[t]     - Where [t] is a type specifier.
	 *     %[n]$[x] - Where [n] is an argument specifier and [t] is a type
specifier.
	 *
	 * Types:
	 * a - Numeric: Replacement text is coerced to a numeric type but not
quoted or escaped.
	 * e - Escape: Replacement text is passed to $this->escape().
	 * E - Escape (extra): Replacement text is passed to $this->escape()
with true as the second argument.
	 * n - Name Quote: Replacement text is passed to $this->quoteName().
	 * q - Quote: Replacement text is passed to $this->quote().
	 * Q - Quote (no escape): Replacement text is passed to $this->quote()
with false as the second argument.
	 * r - Raw: Replacement text is used as-is. (Be careful)
	 *
	 * Date Types:
	 * - Replacement text automatically quoted (use uppercase for Name Quote).
	 * - Replacement text should be a string in date format or name of a date
column.
	 * y/Y - Year
	 * m/M - Month
	 * d/D - Day
	 * h/H - Hour
	 * i/I - Minute
	 * s/S - Second
	 *
	 * Invariable Types:
	 * - Takes no argument.
	 * - Argument index not incremented.
	 * t - Replacement text is the result of $this->currentTimestamp().
	 * z - Replacement text is the result of $this->nullDate(false).
	 * Z - Replacement text is the result of $this->nullDate(true).
	 *
	 * Usage:
	 * $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a',
'foo', '#__foo', 'bar', 1);
	 * Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1
	 *
	 * Notes:
	 * The argument specifier is optional but recommended for clarity.
	 * The argument index used for unspecified tokens is incremented only when
used.
	 *
	 * @param   string  $format  The formatting string.
	 *
	 * @return  string  Returns a string produced according to the formatting
string.
	 *
	 * @since   3.1.4
	 */
	public function format($format)
	{
		$query = $this;
		$args = array_slice(func_get_args(), 1);
		array_unshift($args, null);

		$i = 1;
		$func = function ($match) use ($query, $args, &$i)
		{
			if (isset($match[6]) && $match[6] == '%')
			{
				return '%';
			}

			// No argument required, do not increment the argument index.
			switch ($match[5])
			{
				case 't':
					return $query->currentTimestamp();
					break;

				case 'z':
					return $query->nullDate(false);
					break;

				case 'Z':
					return $query->nullDate(true);
					break;
			}

			// Increment the argument index only if argument specifier not provided.
			$index = is_numeric($match[4]) ? (int) $match[4] : $i++;

			if (!$index || !isset($args[$index]))
			{
				// TODO - What to do? sprintf() throws a Warning in these cases.
				$replacement = '';
			}
			else
			{
				$replacement = $args[$index];
			}

			switch ($match[5])
			{
				case 'a':
					return 0 + $replacement;
					break;

				case 'e':
					return $query->escape($replacement);
					break;

				case 'E':
					return $query->escape($replacement, true);
					break;

				case 'n':
					return $query->quoteName($replacement);
					break;

				case 'q':
					return $query->quote($replacement);
					break;

				case 'Q':
					return $query->quote($replacement, false);
					break;

				case 'r':
					return $replacement;
					break;

				// Dates
				case 'y':
					return $query->year($query->quote($replacement));
					break;

				case 'Y':
					return $query->year($query->quoteName($replacement));
					break;

				case 'm':
					return $query->month($query->quote($replacement));
					break;

				case 'M':
					return $query->month($query->quoteName($replacement));
					break;

				case 'd':
					return $query->day($query->quote($replacement));
					break;

				case 'D':
					return $query->day($query->quoteName($replacement));
					break;

				case 'h':
					return $query->hour($query->quote($replacement));
					break;

				case 'H':
					return $query->hour($query->quoteName($replacement));
					break;

				case 'i':
					return $query->minute($query->quote($replacement));
					break;

				case 'I':
					return $query->minute($query->quoteName($replacement));
					break;

				case 's':
					return $query->second($query->quote($replacement));
					break;

				case 'S':
					return $query->second($query->quoteName($replacement));
					break;
			}

			return '';
		};

		/**
		 * Regexp to find and replace all tokens.
		 * Matched fields:
		 * 0: Full token
		 * 1: Everything following '%'
		 * 2: Everything following '%' unless '%'
		 * 3: Argument specifier and '$'
		 * 4: Argument specifier
		 * 5: Type specifier
		 * 6: '%' if full token is '%%'
		 */
		return
preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#',
$func, $format);
	}

	/**
	 * Add to the current date and time.
	 * Usage:
	 * $query->select($query->dateAdd());
	 * Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
	 * Note: Not all drivers support all units.
	 *
	 * @param   string  $date      The db quoted string representation of the
date to add to. May be date or datetime
	 * @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
	 *
	 * @link   
http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add
	 * @since   3.2.0
	 */
	public function dateAdd($date, $interval, $datePart)
	{
		return 'DATE_ADD(' . $date . ', INTERVAL ' .
$interval . ' ' . $datePart . ')';
	}

	/**
	 * Add a query to UNION ALL with the current query.
	 * Multiple unions each require separate statements and create an array of
unions.
	 *
	 * Usage:
	 * $query->union('SELECT name FROM  #__foo')
	 *
	 * The $query attribute as an array is deprecated and will not be
supported in 4.0.
	 *
	 * $query->union(array('SELECT name FROM 
#__foo','SELECT name FROM  #__bar'))
	 *
	 * @param   mixed    $query     The JDatabaseQuery object or string to
union.
	 * @param   boolean  $distinct  Not used - ignored.
	 * @param   string   $glue      Not used - ignored.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @see     union
	 *
	 * @since   3.2.0
	 */
	public function unionAll($query, $distinct = false, $glue = '')
	{
		$glue = ')' . PHP_EOL . 'UNION ALL (';
		$name = 'UNION ALL ()';

		if (is_array($query))
		{
			JLog::add('Query attribute as an array is deprecated.',
JLog::WARNING, 'deprecated');
		}

		// Get the JDatabaseQueryElement if it does not exist
		if (is_null($this->unionAll))
		{
			$this->unionAll = new JDatabaseQueryElement($name, $query,
"$glue");
		}

		// Otherwise append the second UNION.
		else
		{
			$this->unionAll->append($query);
		}

		return $this;
	}

	/**
	 * Validate arguments which are passed to selectRowNumber method and set
up common variables.
	 *
	 * @param   string  $orderBy           An expression of ordering for
window function.
	 * @param   string  $orderColumnAlias  An alias for new ordering column.
	 *
	 * @return  void
	 *
	 * @since   3.7.0
	 * @throws  RuntimeException
	 */
	protected function validateRowNumber($orderBy, $orderColumnAlias)
	{
		if ($this->selectRowNumber)
		{
			throw new RuntimeException("Method 'selectRowNumber' can
be called only once per instance.");
		}

		$this->type = 'select';

		$this->selectRowNumber = array(
			'orderBy' => $orderBy,
			'orderColumnAlias' => $orderColumnAlias,
		);
	}

	/**
	 * Return the number of the current row.
	 *
	 * Usage:
	 * $query->select('id');
	 * $query->selectRowNumber('ordering,publish_up DESC',
'new_ordering');
	 * $query->from('#__content');
	 *
	 * @param   string  $orderBy           An expression of ordering for
window function.
	 * @param   string  $orderColumnAlias  An alias for new ordering column.
	 *
	 * @return  JDatabaseQuery  Returns this object to allow chaining.
	 *
	 * @since   3.7.0
	 * @throws  RuntimeException
	 */
	public function selectRowNumber($orderBy, $orderColumnAlias)
	{
		$this->validateRowNumber($orderBy, $orderColumnAlias);
		$this->select("ROW_NUMBER() OVER (ORDER BY $orderBy) AS
$orderColumnAlias");

		return $this;
	}
}
PK�]�[�诅$$
exception.phpnu�[���<?php
/**
 * @package     Joomla.Legacy
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JLog::add('JDatabaseException is deprecated, use SPL Exceptions
instead.', JLog::WARNING, 'deprecated');

/**
 * Exception class definition for the Database subpackage.
 *
 * @since       1.7
 * @deprecated  2.5.5
 */
class JDatabaseException extends RuntimeException
{
}
PK�]�[>c��NN	mysql.phpnu�[���<?php
/**
 * @package     Joomla.Legacy
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JLog::add('JDatabaseMysql is deprecated, use JDatabaseDriverMysql
instead.', JLog::WARNING, 'deprecated');

/**
 * MySQL database driver
 *
 * @link        http://dev.mysql.com/doc/
 * @since       1.5
 * @deprecated  3.0 Use JDatabaseDriverMysql instead.
 */
class JDatabaseMysql extends JDatabaseDriverMysql
{
}
PK�]�[6Q�hh
mysqli.phpnu�[���<?php
/**
 * @package     Joomla.Legacy
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JLog::add('JDatabaseMysqli is deprecated, use JDatabaseDriverMysqli
instead.', JLog::WARNING, 'deprecated');

/**
 * MySQLi database driver
 *
 * @link        https://www.php.net/manual/en/book.mysqli.php
 * @since       1.5
 * @deprecated  3.0 Use JDatabaseDriverMysqli instead.
 */
class JDatabaseMysqli extends JDatabaseDriverMysqli
{
}
PK�]�[�}{��sqlazure.phpnu�[���<?php
/**
 * @package     Joomla.Legacy
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JLog::add('JDatabaseSqlazure is deprecated, use
JDatabaseDriverSqlazure instead.', JLog::WARNING,
'deprecated');

/**
 * SQL Server database driver
 *
 * @link       
https://azure.microsoft.com/en-us/documentation/services/sql-database/
 * @since       1.7
 * @deprecated  3.0 Use JDatabaseDriverSqlazure instead.
 */
class JDatabaseSqlazure extends JDatabaseDriverSqlazure
{
}
PK�]�[Q��?}}
sqlsrv.phpnu�[���<?php
/**
 * @package     Joomla.Legacy
 * @subpackage  Database
 *
 * @copyright   Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('JPATH_PLATFORM') or die;

JLog::add('JDatabaseSqlsrv is deprecated, use JDatabaseDriverSqlsrv
instead.', JLog::WARNING, 'deprecated');

/**
 * SQL Server database driver
 *
 * @link       
https://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx
 * @since       1.7
 * @deprecated  3.0 Use JDatabaseDriverSqlsrv instead.
 */
class JDatabaseSqlsrv extends JDatabaseDriverSqlsrv
{
}
PK���[�Q==driver/joomla.phpnu�[���<?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;

/**
 * This crazy three line bit is required to convince Joomla! to load
JDatabaseInterface which is on the same file as the
 * abstract JDatabaseDriver class for reasons that beat me. It makes no
sense. Furthermore, jimport on Joomla! 3.4
 * doesn't seem to actually load the file, merely registering the
association in the autoloader. Hence the class_exists
 * in here.
 */
jimport('joomla.database.driver');
jimport('joomla.database.driver.mysqli');
class_exists('JDatabaseDriver', true);

/**
 * Joomla! pass-through database driver.
 */
class FOFDatabaseDriverJoomla extends FOFDatabase implements
FOFDatabaseInterface
{
	/** @var FOFDatabase The real database connection object */
	private $dbo;

	/**
	 * @var    string  The character(s) used to quote SQL statement names such
as table names or field names,
	 *                 etc.  The child classes should define this as
necessary.  If a single character string the
	 *                 same character is used for both sides of the quoted
name, else the first character will be
	 *                 used for the opening quote and the second for the
closing quote.
	 * @since  11.1
	 */
	protected $nameQuote = '';

	/**
	 * Is this driver supported
	 *
	 * @since  11.2
	 */
	public static function isSupported()
	{
		return true;
	}

	/**
	 * Database object constructor
	 *
	 * @param    array $options List of options used to configure the
connection
	 */
	public function __construct($options = array())
	{
		// Get best matching Akeeba Backup driver instance
		$this->dbo = JFactory::getDbo();

		$reflection = new ReflectionClass($this->dbo);

		try
		{
			$refProp = $reflection->getProperty('nameQuote');
			$refProp->setAccessible(true);
			$this->nameQuote = $refProp->getValue($this->dbo);
		}
		catch (Exception $e)
		{
			$this->nameQuote = '`';
		}
	}

	public function close()
	{
		if (method_exists($this->dbo, 'close'))
		{
			$this->dbo->close();
		}
		elseif (method_exists($this->dbo, 'disconnect'))
		{
			$this->dbo->disconnect();
		}
	}

	public function disconnect()
	{
		$this->close();
	}

	public function open()
	{
		if (method_exists($this->dbo, 'open'))
		{
			$this->dbo->open();
		}
		elseif (method_exists($this->dbo, 'connect'))
		{
			$this->dbo->connect();
		}
	}

	public function connect()
	{
		$this->open();
	}

	public function connected()
	{
		if (method_exists($this->dbo, 'connected'))
		{
			return $this->dbo->connected();
		}

		return true;
	}

	public function escape($text, $extra = false)
	{
		return $this->dbo->escape($text, $extra);
	}

	public function execute()
	{
		if (method_exists($this->dbo, 'execute'))
		{
			return $this->dbo->execute();
		}

		return $this->dbo->query();
	}

	public function getAffectedRows()
	{
		if (method_exists($this->dbo, 'getAffectedRows'))
		{
			return $this->dbo->getAffectedRows();
		}

		return 0;
	}

	public function getCollation()
	{
		if (method_exists($this->dbo, 'getCollation'))
		{
			return $this->dbo->getCollation();
		}

		return 'utf8_general_ci';
	}

	public function getConnection()
	{
		if (method_exists($this->dbo, 'getConnection'))
		{
			return $this->dbo->getConnection();
		}

		return null;
	}

	public function getCount()
	{
		if (method_exists($this->dbo, 'getCount'))
		{
			return $this->dbo->getCount();
		}

		return 0;
	}

	public function getDateFormat()
	{
		if (method_exists($this->dbo, 'getDateFormat'))
		{
			return $this->dbo->getDateFormat();
		}

		return 'Y-m-d H:i:s';;
	}

	public function getMinimum()
	{
		if (method_exists($this->dbo, 'getMinimum'))
		{
			return $this->dbo->getMinimum();
		}

		return '5.0.40';
	}

	public function getNullDate()
	{
		if (method_exists($this->dbo, 'getNullDate'))
		{
			return $this->dbo->getNullDate();
		}

		return '0000-00-00 00:00:00';
	}

	public function getNumRows($cursor = null)
	{
		if (method_exists($this->dbo, 'getNumRows'))
		{
			return $this->dbo->getNumRows($cursor);
		}

		return 0;
	}

	public function getQuery($new = false)
	{
		if (method_exists($this->dbo, 'getQuery'))
		{
			return $this->dbo->getQuery($new);
		}

		return null;
	}

	public function getTableColumns($table, $typeOnly = true)
	{
		if (method_exists($this->dbo, 'getTableColumns'))
		{
			return $this->dbo->getTableColumns($table, $typeOnly);
		}

		$result = $this->dbo->getTableFields(array($table), $typeOnly);

		return $result[$table];
	}

	public function getTableKeys($tables)
	{
		if (method_exists($this->dbo, 'getTableKeys'))
		{
			return $this->dbo->getTableKeys($tables);
		}

		return array();
	}

	public function getTableList()
	{
		if (method_exists($this->dbo, 'getTableList'))
		{
			return $this->dbo->getTableList();
		}

		return array();
	}

	public function getVersion()
	{
		if (method_exists($this->dbo, 'getVersion'))
		{
			return $this->dbo->getVersion();
		}

		return '5.0.40';
	}

	public function insertid()
	{
		if (method_exists($this->dbo, 'insertid'))
		{
			return $this->dbo->insertid();
		}

		return null;
	}

	public function insertObject($table, &$object, $key = null)
	{
		if (method_exists($this->dbo, 'insertObject'))
		{
			return $this->dbo->insertObject($table, $object, $key);
		}

		return null;
	}

	public function loadAssoc()
	{
		if (method_exists($this->dbo, 'loadAssoc'))
		{
			return $this->dbo->loadAssoc();
		}

		return null;
	}

	public function loadAssocList($key = null, $column = null)
	{
		if (method_exists($this->dbo, 'loadAssocList'))
		{
			return $this->dbo->loadAssocList($key, $column);
		}

		return null;
	}

	public function loadObject($class = 'stdClass')
	{
		if (method_exists($this->dbo, 'loadObject'))
		{
			return $this->dbo->loadObject($class);
		}

		return null;
	}

	public function loadObjectList($key = '', $class =
'stdClass')
	{
		if (method_exists($this->dbo, 'loadObjectList'))
		{
			return $this->dbo->loadObjectList($key, $class);
		}

		return null;
	}

	public function loadResult()
	{
		if (method_exists($this->dbo, 'loadResult'))
		{
			return $this->dbo->loadResult();
		}

		return null;
	}

	public function loadRow()
	{
		if (method_exists($this->dbo, 'loadRow'))
		{
			return $this->dbo->loadRow();
		}

		return null;
	}

	public function loadRowList($key = null)
	{
		if (method_exists($this->dbo, 'loadRowList'))
		{
			return $this->dbo->loadRowList($key);
		}

		return null;
	}

	public function lockTable($tableName)
	{
		if (method_exists($this->dbo, 'lockTable'))
		{
			return $this->dbo->lockTable($this);
		}

		return $this;
	}

	public function quote($text, $escape = true)
	{
		if (method_exists($this->dbo, 'quote'))
		{
			return $this->dbo->quote($text, $escape);
		}

		return $text;
	}

	public function select($database)
	{
		if (method_exists($this->dbo, 'select'))
		{
			return $this->dbo->select($database);
		}

		return false;
	}

	public function setQuery($query, $offset = 0, $limit = 0)
	{
		if (method_exists($this->dbo, 'setQuery'))
		{
			return $this->dbo->setQuery($query, $offset, $limit);
		}

		return false;
	}

	public function transactionCommit($toSavepoint = false)
	{
		if (method_exists($this->dbo, 'transactionCommit'))
		{
			$this->dbo->transactionCommit($toSavepoint);
		}
	}

	public function transactionRollback($toSavepoint = false)
	{
		if (method_exists($this->dbo, 'transactionRollback'))
		{
			$this->dbo->transactionRollback($toSavepoint);
		}
	}

	public function transactionStart($asSavepoint = false)
	{
		if (method_exists($this->dbo, 'transactionStart'))
		{
			$this->dbo->transactionStart($asSavepoint);
		}
	}

	public function unlockTables()
	{
		if (method_exists($this->dbo, 'unlockTables'))
		{
			return $this->dbo->unlockTables();
		}

		return $this;
	}

	public function updateObject($table, &$object, $key, $nulls = false)
	{
		if (method_exists($this->dbo, 'updateObject'))
		{
			return $this->dbo->updateObject($table, $object, $key, $nulls);
		}

		return false;
	}

	public function getLog()
	{
		if (method_exists($this->dbo, 'getLog'))
		{
			return $this->dbo->getLog();
		}

		return array();
	}

	public function dropTable($table, $ifExists = true)
	{
		if (method_exists($this->dbo, 'dropTable'))
		{
			return $this->dbo->dropTable($table, $ifExists);
		}

		return $this;
	}

	public function getTableCreate($tables)
	{
		if (method_exists($this->dbo, 'getTableCreate'))
		{
			return $this->dbo->getTableCreate($tables);
		}

		return array();
	}

	public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
	{
		if (method_exists($this->dbo, 'renameTable'))
		{
			return $this->dbo->renameTable($oldTable, $newTable, $backup,
$prefix);
		}

		return $this;
	}

	public function setUtf()
	{
		if (method_exists($this->dbo, 'setUtf'))
		{
			return $this->dbo->setUtf();
		}

		return false;
	}


	protected function freeResult($cursor = null)
	{
		return false;
	}

	/**
	 * Method to get an array of values from the
<var>$offset</var> field in each row of the result set from
	 * the database query.
	 *
	 * @param   integer  $offset  The row offset to use to build the result
array.
	 *
	 * @return  mixed    The return value or null if the query failed.
	 *
	 * @since   11.1
	 * @throws  RuntimeException
	 */
	public function loadColumn($offset = 0)
	{
		if (method_exists($this->dbo, 'loadColumn'))
		{
			return $this->dbo->loadColumn($offset);
		}

		return $this->dbo->loadResultArray($offset);
	}

	/**
	 * Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
	 * risks and reserved word conflicts.
	 *
	 * @param   mixed  $name  The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
	 *                        Each type supports dot-notation name.
	 * @param   mixed  $as    The AS query part associated to $name. It can be
string or array, in latter case it has to be
	 *                        same length of $name; if is null there will not
be any AS part for string or array element.
	 *
	 * @return  mixed  The quote wrapped name, same type of $name.
	 *
	 * @since   11.1
	 */
	public function quoteName($name, $as = null)
	{
		if (is_string($name))
		{
			$quotedName = $this->quoteNameStr(explode('.', $name));

			$quotedAs = '';

			if (!is_null($as))
			{
				settype($as, 'array');
				$quotedAs .= ' AS ' . $this->quoteNameStr($as);
			}

			return $quotedName . $quotedAs;
		}
		else
		{
			$fin = array();

			if (is_null($as))
			{
				foreach ($name as $str)
				{
					$fin[] = $this->quoteName($str);
				}
			}
			elseif (is_array($name) && (count($name) == count($as)))
			{
				$count = count($name);

				for ($i = 0; $i < $count; $i++)
				{
					$fin[] = $this->quoteName($name[$i], $as[$i]);
				}
			}

			return $fin;
		}
	}

	/**
	 * Quote strings coming from quoteName call.
	 *
	 * @param   array  $strArr  Array of strings coming from quoteName
dot-explosion.
	 *
	 * @return  string  Dot-imploded string of quoted parts.
	 *
	 * @since 11.3
	 */
	protected function quoteNameStr($strArr)
	{
		$parts = array();
		$q = $this->nameQuote;

		foreach ($strArr as $part)
		{
			if (is_null($part))
			{
				continue;
			}

			if (strlen($q) == 1)
			{
				$parts[] = $q . $part . $q;
			}
			else
			{
				$parts[] = $q[0] . $part . $q[1];
			}
		}

		return implode('.', $parts);
	}

	/**
	 * Gets the error message from the database connection.
	 *
	 * @param   boolean  $escaped  True to escape the message string for use
in JavaScript.
	 *
	 * @return  string  The error message for the most recent query.
	 *
	 * @since   11.1
	 */
	public function getErrorMsg($escaped = false)
	{
		if (method_exists($this->dbo, 'getErrorMsg'))
		{
			$errorMessage = $this->dbo->getErrorMsg();
		}
		else
		{
			$errorMessage = $this->errorMsg;
		}

		if ($escaped)
		{
			return addslashes($errorMessage);
		}

		return $errorMessage;
	}

	/**
	 * Gets the error number from the database connection.
	 *
	 * @return      integer  The error number for the most recent query.
	 *
	 * @since       11.1
	 * @deprecated  13.3 (Platform) & 4.0 (CMS)
	 */
	public function getErrorNum()
	{
		if (method_exists($this->dbo, 'getErrorNum'))
		{
			$errorNum = $this->dbo->getErrorNum();
		}
		else
		{
			$errorNum = $this->getErrorNum;
		}

		return $errorNum;
	}

	/**
	 * Return the most recent error message for the database connector.
	 *
	 * @param   boolean  $showSQL  True to display the SQL statement sent to
the database as well as the error.
	 *
	 * @return  string  The error message for the most recent query.
	 */
	public function stderr($showSQL = false)
	{
		if (method_exists($this->dbo, 'stderr'))
		{
			return $this->dbo->stderr($showSQL);
		}

		return parent::stderr($showSQL);
	}

	/**
	 * Magic method to proxy all calls to the loaded database driver object
	 */
	public function __call($name, array $arguments)
	{
		if (is_null($this->dbo))
		{
			throw new Exception('FOF database driver is not loaded');
		}

		if (method_exists($this->dbo, $name) || in_array($name,
array('q', 'nq', 'qn', 'query')))
		{
			switch ($name)
			{
				case 'execute':
					$name = 'query';
					break;

				case 'q':
					$name = 'quote';
					break;

				case 'qn':
				case 'nq':
					switch (count($arguments))
					{
						case 0 :
							$result = $this->quoteName();
							break;
						case 1 :
							$result = $this->quoteName($arguments[0]);
							break;
						case 2:
						default:
							$result = $this->quoteName($arguments[0], $arguments[1]);
							break;
					}
					return $result;

					break;
			}

			switch (count($arguments))
			{
				case 0 :
					$result = $this->dbo->$name();
					break;
				case 1 :
					$result = $this->dbo->$name($arguments[0]);
					break;
				case 2:
					$result = $this->dbo->$name($arguments[0], $arguments[1]);
					break;
				case 3:
					$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2]);
					break;
				case 4:
					$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2], $arguments[3]);
					break;
				case 5:
					$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2], $arguments[3], $arguments[4]);
					break;
				default:
					// Resort to using call_user_func_array for many segments
					$result = call_user_func_array(array($this->dbo, $name),
$arguments);
			}

			if (class_exists('JDatabase') && is_object($result)
&& ($result instanceof JDatabase))
			{
				return $this;
			}

			return $result;
		}
		else
		{
			throw new \Exception('Method ' . $name . ' not found in
FOFDatabase');
		}
	}

	public function __get($name)
	{
		if (isset($this->dbo->$name) || property_exists($this->dbo,
$name))
		{
			return $this->dbo->$name;
		}
		else
		{
			$this->dbo->$name = null;
			user_error('Database driver does not support property ' .
$name);
		}
	}

	public function __set($name, $value)
	{
		if (isset($this->dbo->name) || property_exists($this->dbo,
$name))
		{
			$this->dbo->$name = $value;
		}
		else
		{
			$this->dbo->$name = null;
			user_error('Database driver not support property ' . $name);
		}
	}
}
PK���[��GD�[�[
installer.phpnu�[���<?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
 */

class FOFDatabaseInstaller
{
	/** @var  FOFDatabase  The database connector object */
	private $db = null;

	/**
	 * @var   FOFInput  Input variables
	 */
	protected $input = array();

	/** @var  string  The directory where the XML schema files are stored */
	private $xmlDirectory = null;

	/** @var  array  A list of the base names of the XML schema files */
	public $xmlFiles = array('mysql', 'mysqli',
'pdomysql', 'postgresql', 'sqlsrv',
'mssql');

	/** @var array Internal cache for table list  */
	protected static $allTables = array();

	/**
	 * Public constructor
	 *
	 * @param   array  $config  The configuration array
	 */
	public function __construct($config = array())
	{
		// Make sure $config is an array
		if (is_object($config))
		{
			$config = (array) $config;
		}
		elseif (!is_array($config))
		{
			$config = array();
		}

		// Get the input
		if (array_key_exists('input', $config))
		{
			if ($config['input'] instanceof FOFInput)
			{
				$this->input = $config['input'];
			}
			else
			{
				$this->input = new FOFInput($config['input']);
			}
		}
		else
		{
			$this->input = new FOFInput;
		}

		// Set the database object
		if (array_key_exists('dbo', $config))
		{
			$this->db = $config['dbo'];
		}
		else
		{
			$this->db = FOFPlatform::getInstance()->getDbo();
		}

		// Set the $name/$_name variable
		$component = $this->input->getCmd('option',
'com_foobar');

		if (array_key_exists('option', $config))
		{
			$component = $config['option'];
		}

		// Figure out where the XML schema files are stored
		if (array_key_exists('dbinstaller_directory', $config))
		{
			$this->xmlDirectory = $config['dbinstaller_directory'];
		}
		else
		{
			// Nothing is defined, assume the files are stored in the sql/xml
directory inside the component's administrator section
			$directories =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
			$this->setXmlDirectory($directories['admin'] .
'/sql/xml');
		}

		// Do we have a set of XML files to look for?
		if (array_key_exists('dbinstaller_files', $config))
		{
			$files = $config['dbinstaller_files'];

			if (!is_array($files))
			{
				$files = explode(',', $files);
			}

			$this->xmlFiles = $files;
		}

	}

	/**
	 * Sets the directory where XML schema files are stored
	 *
	 * @param   string  $xmlDirectory
	 */
	public function setXmlDirectory($xmlDirectory)
	{
		$this->xmlDirectory = $xmlDirectory;
	}

	/**
	 * Returns the directory where XML schema files are stored
	 *
	 * @return  string
	 */
	public function getXmlDirectory()
	{
		return $this->xmlDirectory;
	}

	/**
	 * Creates or updates the database schema
	 *
	 * @return  void
	 *
	 * @throws  Exception  When a database query fails and it doesn't
have the canfail flag
	 */
	public function updateSchema()
	{
		// Get the schema XML file
		$xml = $this->findSchemaXml();

		if (empty($xml))
		{
			return;
		}

		// Make sure there are SQL commands in this file
		if (!$xml->sql)
		{
			return;
		}

		// Walk the sql > action tags to find all tables
		/** @var SimpleXMLElement $actions */
		$actions = $xml->sql->children();

		/**
		 * The meta/autocollation node defines if I should automatically apply
the correct collation (utf8 or utf8mb4)
		 * to the database tables managed by the schema updater. When enabled
(default) the queries are automatically
		 * converted to the correct collation (utf8mb4_unicode_ci or
utf8_general_ci) depending on whether your Joomla!
		 * and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if
UTF8MB4 is supported, all CREATE TABLE
		 * queries are analyzed and the tables referenced in them are
auto-converted to the proper utf8mb4 collation.
		 */
		$autoCollationConversion = true;

		if ($xml->meta->autocollation)
		{
			$value = (string) $xml->meta->autocollation;
			$value = trim($value);
			$value = strtolower($value);

			$autoCollationConversion = in_array($value, array('true',
'1', 'on', 'yes'));
		}

		try
		{
			$hasUtf8mb4Support = $this->db->hasUTF8mb4Support();
		}
		catch (\Exception $e)
		{
			$hasUtf8mb4Support = false;
		}

		$tablesToConvert = array();

		/** @var SimpleXMLElement $action */
		foreach ($actions as $action)
		{
			// Get the attributes
			$attributes = $action->attributes();

			// Get the table / view name
			$table = $attributes->table ? (string)$attributes->table :
'';

			if (empty($table))
			{
				continue;
			}

			// Am I allowed to let this action fail?
			$canFailAction = $attributes->canfail ? $attributes->canfail : 0;

			// Evaluate conditions
			$shouldExecute = true;

			/** @var SimpleXMLElement $node */
			foreach ($action->children() as $node)
			{
				if ($node->getName() == 'condition')
				{
					// Get the operator
					$operator = $node->attributes()->operator ?
(string)$node->attributes()->operator : 'and';
					$operator = empty($operator) ? 'and' : $operator;

					$condition = $this->conditionMet($table, $node);

					switch ($operator)
					{
						case 'not':
							$shouldExecute = $shouldExecute && !$condition;
							break;

						case 'or':
							$shouldExecute = $shouldExecute || $condition;
							break;

						case 'nor':
							$shouldExecute = !$shouldExecute && !$condition;
							break;

						case 'xor':
							$shouldExecute = ($shouldExecute xor $condition);
							break;

						case 'maybe':
							$shouldExecute = $condition ? true : $shouldExecute;
							break;

						default:
							$shouldExecute = $shouldExecute && $condition;
							break;
					}
				}

				// DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION!
				// if (!$shouldExecute) break;
			}

			// Do I have to only collect the tables from CREATE TABLE queries?
			$onlyCollectTables = !$shouldExecute && $autoCollationConversion
&& $hasUtf8mb4Support;

			// Make sure all conditions are met OR I have to collect tables from
CREATE TABLE queries.
			if (!$shouldExecute && !$onlyCollectTables)
			{
				continue;
			}

			// Execute queries
			foreach ($action->children() as $node)
			{
				if ($node->getName() == 'query')
				{
					$query = (string) $node;

					if ($autoCollationConversion && $hasUtf8mb4Support)
					{
						$this->extractTablesToConvert($query, $tablesToConvert);
					}

					// If we're only collecting tables do not run the queries
					if ($onlyCollectTables)
					{
						continue;
					}

					$canFail = $node->attributes->canfail ?
(string)$node->attributes->canfail : $canFailAction;

					if (is_string($canFail))
					{
						$canFail = strtoupper($canFail);
					}

					$canFail = (in_array($canFail, array(true, 1, 'YES',
'TRUE')));

					// Do I need to automatically convert the collation of all CREATE /
ALTER queries?
					if ($autoCollationConversion)
					{
						if ($hasUtf8mb4Support)
						{
							// We have UTF8MB4 support. Convert all queries to UTF8MB4.
							$query = $this->convertUtf8QueryToUtf8mb4($query);
						}
						else
						{
							// We do not have UTF8MB4 support. Convert all queries to plain old
UTF8.
							$query = $this->convertUtf8mb4QueryToUtf8($query);
						}
					}

					$this->db->setQuery($query);

					try
					{
						$this->db->execute();
					}
					catch (Exception $e)
					{
						// If we are not allowed to fail, throw back the exception we caught
						if (!$canFail)
						{
							throw $e;
						}
					}
				}
			}
		}

		// Auto-convert the collation of tables if we are told to do so, have
utf8mb4 support and a list of tables.
		if ($autoCollationConversion && $hasUtf8mb4Support &&
!empty($tablesToConvert))
		{
			$this->convertTablesToUtf8mb4($tablesToConvert);
		}
	}

	/**
	 * Uninstalls the database schema
	 *
	 * @return  void
	 */
	public function removeSchema()
	{
		// Get the schema XML file
		$xml = $this->findSchemaXml();

		if (empty($xml))
		{
			return;
		}

		// Make sure there are SQL commands in this file
		if (!$xml->sql)
		{
			return;
		}

		// Walk the sql > action tags to find all tables
		$tables = array();
		/** @var SimpleXMLElement $actions */
		$actions = $xml->sql->children();

		/** @var SimpleXMLElement $action */
		foreach ($actions as $action)
		{
			$attributes = $action->attributes();
			$tables[] = (string)$attributes->table;
		}

		// Simplify the tables list
		$tables = array_unique($tables);

		// Start dropping tables
		foreach ($tables as $table)
		{
			try
			{
				$this->db->dropTable($table);
			}
			catch (Exception $e)
			{
				// Do not fail if I can't drop the table
			}
		}
	}

	/**
	 * Find an suitable schema XML file for this database type and return the
SimpleXMLElement holding its information
	 *
	 * @return  null|SimpleXMLElement  Null if no suitable schema XML file is
found
	 */
	protected function findSchemaXml()
	{
		$driverType = $this->db->name;
		$xml = null;

		// And now look for the file
		foreach ($this->xmlFiles as $baseName)
		{
			// Remove any accidental whitespace
			$baseName = trim($baseName);

			// Get the full path to the file
			$fileName = $this->xmlDirectory . '/' . $baseName .
'.xml';

			// Make sure the file exists
			if (!@file_exists($fileName))
			{
				continue;
			}

			// Make sure the file is a valid XML document
			try
			{
				$xml = new SimpleXMLElement($fileName, LIBXML_NONET, true);
			}
			catch (Exception $e)
			{
				$xml = null;
				continue;
			}

			// Make sure the file is an XML schema file
			if ($xml->getName() != 'schema')
			{
				$xml = null;
				continue;
			}

			if (!$xml->meta)
			{
				$xml = null;
				continue;
			}

			if (!$xml->meta->drivers)
			{
				$xml = null;
				continue;
			}

			/** @var SimpleXMLElement $drivers */
			$drivers = $xml->meta->drivers;

			// Strict driver name match
			foreach ($drivers->children() as $driverTypeTag)
			{
				$thisDriverType = (string)$driverTypeTag;

				if ($thisDriverType == $driverType)
				{
					return $xml;
				}
			}

			// Some custom database drivers use a non-standard $name variable. Let
try a relaxed match.
			foreach ($drivers->children() as $driverTypeTag)
			{
				$thisDriverType = (string)$driverTypeTag;

				if (
					// e.g. $driverType = 'mysqlistupid', $thisDriverType =
'mysqli' => driver matched
					strpos($driverType, $thisDriverType) === 0
					// e.g. $driverType = 'stupidmysqli', $thisDriverType =
'mysqli' => driver matched
					|| (substr($driverType, -strlen($thisDriverType)) == $thisDriverType)
				)
				{
					return $xml;
				}
			}

			$xml = null;
		}

		return $xml;
	}

	/**
	 * Checks if a condition is met
	 *
	 * @param   string            $table  The table we're operating on
	 * @param   SimpleXMLElement  $node   The condition definition node
	 *
	 * @return  bool
	 */
	protected function conditionMet($table, SimpleXMLElement $node)
	{
		if (empty(static::$allTables))
		{
			static::$allTables = $this->db->getTableList();
		}

		// Does the table exist?
		$tableNormal = $this->db->replacePrefix($table);
		$tableExists = in_array($tableNormal, static::$allTables);

		// Initialise
		$condition = false;

		// Get the condition's attributes
		$attributes = $node->attributes();
		$type = $attributes->type ? $attributes->type : null;
		$value = $attributes->value ? (string) $attributes->value : null;

		switch ($type)
		{
			// Check if a table or column is missing
			case 'missing':
				$fieldName = (string)$value;

				if (empty($fieldName))
				{
					$condition = !$tableExists;
				}
				else
				{
					try
					{
						$tableColumns = $this->db->getTableColumns($tableNormal, true);
					}
					catch (\Exception $e)
					{
						$tableColumns = array();
					}

					$condition = !array_key_exists($fieldName, $tableColumns);
				}

				break;

			// Check if a column type matches the "coltype" attribute
			case 'type':
				try
				{
					$tableColumns = $this->db->getTableColumns($tableNormal, false);
				}
				catch (\Exception $e)
				{
					$tableColumns = array();
				}

				$condition = false;

				if (array_key_exists($value, $tableColumns))
				{
					$coltype = $attributes->coltype ? $attributes->coltype : null;

					if (!empty($coltype))
					{
						$coltype = strtolower($coltype);
						$currentType = strtolower($tableColumns[$value]->Type);

						$condition = ($coltype == $currentType);
					}
				}

				break;

			// Check if a (named) index exists on the table. Currently only
supported on MySQL.
			case 'index':
				$indexName = (string) $value;
				$condition = true;

				if (!empty($indexName))
				{
					$indexName = str_replace('#__',
$this->db->getPrefix(), $indexName);
					$condition = $this->hasIndex($tableNormal, $indexName);
				}

				break;

			// Check if a table or column needs to be upgraded to utf8mb4
			case 'utf8mb4upgrade':
				$condition = false;

				// Check if the driver and the database connection have UTF8MB4 support
				try
				{
					$hasUtf8mb4Support = $this->db->hasUTF8mb4Support();
				}
				catch (\Exception $e)
				{
					$hasUtf8mb4Support = false;
				}

				if ($hasUtf8mb4Support)
				{
					$fieldName = (string)$value;

					if (empty($fieldName))
					{
						$collation = $this->getTableCollation($tableNormal);
					}
					else
					{
						$collation = $this->getColumnCollation($tableNormal, $fieldName);
					}

					$parts    = explode('_', $collation, 3);
					$encoding = empty($parts[0]) ? '' : strtolower($parts[0]);

					$condition = $encoding != 'utf8mb4';
				}

				break;

			// Check if the result of a query matches our expectation
			case 'equals':
				$query = (string)$node;
				$this->db->setQuery($query);

				try
				{
					$result = $this->db->loadResult();
					$condition = ($result == $value);
				}
				catch (Exception $e)
				{
					return false;
				}

				break;

			// Always returns true
			case 'true':
				return true;
				break;

			default:
				return false;
				break;
		}

		return $condition;
	}

	/**
	 * Get the collation of a table. Uses an internal cache for efficiency.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string  The collation, e.g. "utf8_general_ci"
	 */
	private function getTableCollation($tableName)
	{
		static $cache = array();

		$tableName = $this->db->replacePrefix($tableName);

		if (!isset($cache[$tableName]))
		{
			$cache[$tableName] = $this->realGetTableCollation($tableName);
		}

		return $cache[$tableName];
	}

	/**
	 * Get the collation of a table. This is the internal method used by
getTableCollation.
	 *
	 * @param   string  $tableName  The name of the table
	 *
	 * @return  string  The collation, e.g. "utf8_general_ci"
	 */
	private function realGetTableCollation($tableName)
	{
		try
		{
			$utf8Support = $this->db->hasUTFSupport();
		}
		catch (\Exception $e)
		{
			$utf8Support = false;
		}

		try
		{
			$utf8mb4Support = $utf8Support &&
$this->db->hasUTF8mb4Support();
		}
		catch (\Exception $e)
		{
			$utf8mb4Support = false;
		}

		$collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' :
($utf8Support ? 'utf_general_ci' :
'latin1_swedish_ci');

		$query = 'SHOW TABLE STATUS LIKE ' .
$this->db->q($tableName);

		try
		{
			$row = $this->db->setQuery($query)->loadAssoc();
		}
		catch (\Exception $e)
		{
			return $collation;
		}

		if (empty($row))
		{
			return $collation;
		}

		if (!isset($row['Collation']))
		{
			return $collation;
		}

		if (empty($row['Collation']))
		{
			return $collation;
		}

		return $row['Collation'];
	}

	/**
	 * Get the collation of a column. Uses an internal cache for efficiency.
	 *
	 * @param   string  $tableName   The name of the table
	 * @param   string  $columnName  The name of the column
	 *
	 * @return  string  The collation, e.g. "utf8_general_ci"
	 */
	private function getColumnCollation($tableName, $columnName)
	{
		static $cache = array();

		$tableName = $this->db->replacePrefix($tableName);
		$columnName = $this->db->replacePrefix($columnName);

		if (!isset($cache[$tableName]))
		{
			$cache[$tableName] = array();
		}

		if (!isset($cache[$tableName][$columnName]))
		{
			$cache[$tableName][$columnName] =
$this->realGetColumnCollation($tableName, $columnName);
		}

		return $cache[$tableName][$columnName];
	}

	/**
	 * Get the collation of a column. This is the internal method used by
getColumnCollation.
	 *
	 * @param   string  $tableName   The name of the table
	 * @param   string  $columnName  The name of the column
	 *
	 * @return  string  The collation, e.g. "utf8_general_ci"
	 */
	private function realGetColumnCollation($tableName, $columnName)
	{
		$collation = $this->getTableCollation($tableName);

		$query = 'SHOW FULL COLUMNS FROM ' .
$this->db->qn($tableName) . ' LIKE ' .
$this->db->q($columnName);

		try
		{
			$row = $this->db->setQuery($query)->loadAssoc();
		}
		catch (\Exception $e)
		{
			return $collation;
		}

		if (empty($row))
		{
			return $collation;
		}

		if (!isset($row['Collation']))
		{
			return $collation;
		}

		if (empty($row['Collation']))
		{
			return $collation;
		}

		return $row['Collation'];
	}

	/**
	 * Automatically downgrade a CREATE TABLE or ALTER TABLE query from
utf8mb4 (UTF-8 Multibyte) to plain utf8.
	 *
	 * We use our own method so we can be site it works even on Joomla! 3.4 or
earlier, where UTF8MB4 support is not
	 * implemented.
	 *
	 * @param   string  $query  The query to convert
	 *
	 * @return  string  The converted query
	 */
	private function convertUtf8mb4QueryToUtf8($query)
	{
		// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
		$beginningOfQuery = substr($query, 0, 12);
		$beginningOfQuery = strtoupper($beginningOfQuery);

		if (!in_array($beginningOfQuery, array('ALTER TABLE ',
'CREATE TABLE')))
		{
			return $query;
		}

		// Replace utf8mb4 with utf8
		$from = array(
			'utf8mb4_unicode_ci',
			'utf8mb4_',
			'utf8mb4',
		);

		$to = array(
			'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to
utf8_general_ci per Joomla!'s conventions
			'utf8_',
			'utf8',
		);

		return str_replace($from, $to, $query);
	}

	/**
	 * Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain
utf8 to utf8mb4 (UTF-8 Multibyte).
	 *
	 * @param   string  $query  The query to convert
	 *
	 * @return  string  The converted query
	 */
	private function convertUtf8QueryToUtf8mb4($query)
	{
		// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
		$beginningOfQuery = substr($query, 0, 12);
		$beginningOfQuery = strtoupper($beginningOfQuery);

		if (!in_array($beginningOfQuery, array('ALTER TABLE ',
'CREATE TABLE')))
		{
			return $query;
		}

		// Replace utf8 with utf8mb4
		$from = array(
			'utf8_general_ci',
			'utf8_',
			'utf8',
		);

		$to = array(
			'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to
utf8mb4_unicode_ci per Joomla!'s conventions
			'utf8mb4_',
			'utf8mb4',
		);

		return str_replace($from, $to, $query);
	}

	/**
	 * Analyzes a query. If it's a CREATE TABLE query the table is added
to the $tables array.
	 *
	 * @param   string  $query   The query to analyze
	 * @param   string  $tables  The array where the name of the detected
table is added
	 *
	 * @return  void
	 */
	private function extractTablesToConvert($query, &$tables)
	{
		// Normalize the whitespace of the query
		$query = trim($query);
		$query = str_replace(array("\r\n", "\r",
"\n"), ' ', $query);

		while (strstr($query, '  ') !== false)
		{
			$query = str_replace('  ', ' ', $query);
		}

		// Is it a create table query?
		$queryStart = substr($query, 0, 12);
		$queryStart = strtoupper($queryStart);

		if ($queryStart != 'CREATE TABLE')
		{
			return;
		}

		// Remove the CREATE TABLE keyword. Also, If there's an IF NOT
EXISTS clause remove it.
		$query = substr($query, 12);
		$query = str_ireplace('IF NOT EXISTS', '', $query);
		$query = trim($query);

		// Make sure there is a space between the table name and its definition,
denoted by an open parenthesis
		$query = str_replace('(', ' (', $query);

		// Now we should have the name of the table, a space and the rest of the
query. Extract the table name.
		$parts = explode(' ', $query, 2);
		$tableName = $parts[0];

		/**
		 * The table name may be quoted. Since UTF8MB4 is only supported in
MySQL, the table name can only be
		 * quoted with surrounding backticks. Therefore we can trim backquotes
from the table name to unquote it!
		 **/
		$tableName = trim($tableName, '`');

		// Finally, add the table name to $tables if it doesn't already
exist.
		if (!in_array($tableName, $tables))
		{
			$tables[] = $tableName;
		}
	}

	/**
	 * Converts the collation of tables listed in $tablesToConvert to
utf8mb4_unicode_ci
	 *
	 * @param   array  $tablesToConvert  The list of tables to convert
	 *
	 * @return  void
	 */
	private function convertTablesToUtf8mb4($tablesToConvert)
	{
		try
		{
			$utf8mb4Support = $this->db->hasUTF8mb4Support();
		}
		catch (\Exception $e)
		{
			$utf8mb4Support = false;
		}

		// Make sure the database driver REALLY has support for converting
character sets
		if (!$utf8mb4Support)
		{
			return;
		}

		asort($tablesToConvert);

		foreach ($tablesToConvert as $tableName)
		{
			$collation = $this->getTableCollation($tableName);

			$parts    = explode('_', $collation, 3);
			$encoding = empty($parts[0]) ? '' : strtolower($parts[0]);

			if ($encoding != 'utf8mb4')
			{
				$queries = $this->db->getAlterTableCharacterSet($tableName);

				try
				{
					foreach ($queries as $query)
					{
						$this->db->setQuery($query)->execute();
					}
				}
				catch (\Exception $e)
				{
					// We ignore failed conversions. Remember, you MUST change your
indices MANUALLY.
				}
			}
		}
	}

	/**
	 * Returns true if table $tableName has an index named $indexName or if
it's impossible to retrieve index names for
	 * the table (not enough privileges, not a MySQL database, ...)
	 *
	 * @param   string  $tableName  The name of the table
	 * @param   string  $indexName  The name of the index
	 *
	 * @return  bool
	 */
	private function hasIndex($tableName, $indexName)
	{
		static $isMySQL = null;
		static $cache = array();

		if (is_null($isMySQL))
		{
			$driverType = $this->db->name;
			$driverType = strtolower($driverType);
			$isMySQL = true;

			if (
				!strpos($driverType, 'mysql') === 0
				&& !(substr($driverType, -5) == 'mysql')
				&& !(substr($driverType, -6) == 'mysqli')
			)
			{
				$isMySQL = false;
			}
		}

		// Not MySQL? Lie and return true.
		if (!$isMySQL)
		{
			return true;
		}

		if (!isset($cache[$tableName]))
		{
			$cache[$tableName] = array();
		}

		if (!isset($cache[$tableName][$indexName]))
		{
			$cache[$tableName][$indexName] = true;

			try
			{
				$indices          = array();
				$query            = 'SHOW INDEXES FROM ' .
$this->db->qn($tableName);
				$indexDefinitions =
$this->db->setQuery($query)->loadAssocList();

				if (!empty($indexDefinitions) && is_array($indexDefinitions))
				{
					foreach ($indexDefinitions as $def)
					{
						$indices[] = $def['Key_name'];
					}

					$indices = array_unique($indices);
				}

				$cache[$tableName][$indexName] = in_array($indexName, $indices);
			}
			catch (\Exception $e)
			{
				// Ignore errors
			}
		}

		return $cache[$tableName][$indexName];
	}
}
PK���[�
�JQQiterator/azure.phpnu�[���<?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;

/**
 * SQL azure database iterator.
 */
class FOFDatabaseIteratorAzure extends FOFDatabaseIteratorSqlsrv
{
}
PK��[��database.phpnu�[���PK��[�kp0�7�7=driver/mysql.phpnu�[���PK��[�'�HaHagLdriver/mysqli.phpnu�[���PK��[����=�=�driver/oracle.phpnu�[���PK��[:�֍�g�g��driver/pdo.phpnu�[���PK��[	�CQ5Q5�Sdriver/pdomysql.phpnu�[���PK��[��ggx�driver/pgsql.phpnu�[���PK��[�t�֠֠��driver/postgresql.phpnu�[���PK��[�KKXRRّdriver/sqlazure.phpnu�[���PK��[�s��//n�driver/sqlite.phpnu�[���PK��[�)�$l$l��driver/sqlsrv.phpnu�[���PK��[��UD�D�
00driver.phpnu�[���PK��[�ۄ����exception/connecting.phpnu�[���PK��[>������exception/executing.phpnu�[���PK��[n��u���exception/unsupported.phpnu�[���PK��[+��%�exporter/mysql.phpnu�[���PK��[�{c���
exporter/mysqli.phpnu�[���PK��[�U���'exporter/pdomysql.phpnu�[���PK��[THF{��4exporter/pgsql.phpnu�[���PK��[��v<8exporter/postgresql.phpnu�[���PK��[r�(���Fexporter.phpnu�[���PK��[::2jjuXfactory.phpnu�[���PK��[�ڙnimporter/mysql.phpnu�[���PK��[&��;�-�-jrimporter/mysqli.phpnu�[���PK��[;c%�
,
,v�importer/pdomysql.phpnu�[���PK��[�
-�����importer/pgsql.phpnu�[���PK��[Z��38989��importer/postgresql.phpnu�[���PK��[Gu�SSl
importer.phpnu�[���PK��[���''
�interface.phpnu�[���PK��[6
0��_
iterator/mysql.phpnu�[���PK��[l]�����%iterator/mysqli.phpnu�[���PK��[�V����g*iterator/oracle.phpnu�[���PK��[c�t��4,iterator/pdo.phpnu�[���PK��[g�Z2iterator/pdomysql.phpnu�[���PK��["y]U��v4iterator/pgsql.phpnu�[���PK��[�����r6iterator/postgresql.phpnu�[���PK��[t�{���;iterator/sqlazure.phpnu�[���PK��[Zn��Y=iterator/sqlite.phpnu�[���PK��[-����&?iterator/sqlsrv.phpnu�[���PK��[h�Jh���Citerator.phpnu�[���PK��[�D�
�
�Rquery/element.phpnu�[���PK��[?}P>���]query/limitable.phpnu�[���PK��[��q��wdquery/mysql.phpnu�[���PK��[��
��rfquery/mysqli.phpnu�[���PK��[�w<��{query/oracle.phpnu�[���PK��[�����
��query/pdo.phpnu�[���PK��[h.�����query/pdomysql.phpnu�[���PK��[���P}}��query/pgsql.phpnu�[���PK��[�Za�BBx�query/postgresql.phpnu�[���PK��[	T������query/preparable.phpnu�[���PK��[L�;�88��query/sqlazure.phpnu�[���PK��[�	�(�(W�query/sqlite.phpnu�[���PK��[h��{�]�]zquery/sqlsrv.phpnu�[���PK��[�_�����	�xquery.phpnu�[���PK�]�[�诅$$
p)exception.phpnu�[���PK�]�[>c��NN	�+mysql.phpnu�[���PK�]�[6Q�hh
X.mysqli.phpnu�[���PK�]�[�}{���0sqlazure.phpnu�[���PK�]�[Q��?}}
�3sqlsrv.phpnu�[���PK���[�Q==|6driver/joomla.phpnu�[���PK���[��GD�[�[
�sinstaller.phpnu�[���PK���[�
�JQQ��iterator/azure.phpnu�[���PK>>�S�