Spade

Mini Shell

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

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

PK�}�[��9��1�1ChangeItem/MysqlChangeItem.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Schema\ChangeItem;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Schema\ChangeItem;

/**
 * Checks the database schema against one MySQL DDL query to see if it has
been run.
 *
 * @since  2.5
 */
class MysqlChangeItem extends ChangeItem
{
	/**
	 * Checks a DDL query to see if it is a known type
	 * If yes, build a check query to see if the DDL has been run on the
database.
	 * If successful, the $msgElements, $queryType, $checkStatus and
$checkQuery fields are populated.
	 * The $msgElements contains the text to create the user message.
	 * The $checkQuery contains the SQL query to check whether the schema
change has
	 * been run against the current database. The $queryType contains the type
of
	 * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN,
CHANGE_COLUMN_TYPE, ADD_INDEX).
	 * The $checkStatus field is set to zero if the query is created
	 *
	 * If not successful, $checkQuery is empty and , and $checkStatus is -1.
	 * For example, this will happen if the current line is a non-DDL
statement.
	 *
	 * @return void
	 *
	 * @since  2.5
	 */
	protected function buildCheckQuery()
	{
		// Initialize fields in case we can't create a check query
		$this->checkStatus = -1; // change status to skipped
		$result = null;

		// Remove any newlines
		$this->updateQuery = str_replace("\n", '',
$this->updateQuery);

		// Fix up extra spaces around () and in general
		$find = array('#((\s*)\(\s*([^)\s]+)\s*)(\))#',
'#(\s)(\s*)#');
		$replace = array('($3)', '$1');
		$updateQuery = preg_replace($find, $replace, $this->updateQuery);
		$wordArray =
preg_split("~'[^']*'(*SKIP)(*F)|\s+~u",
trim($updateQuery, "; \t\n\r\0\x0B"));

		// First, make sure we have an array of at least 6 elements
		// if not, we can't make a check query for this one
		if (count($wordArray) < 6)
		{
			// Done with method
			return;
		}

		// We can only make check queries for alter table and create table
queries
		$command = strtoupper($wordArray[0] . ' ' . $wordArray[1]);

		// Check for special update statement to reset utf8mb4 conversion status
		if (($command === 'UPDATE `#__UTF8_CONVERSION`'
			|| $command === 'UPDATE #__UTF8_CONVERSION')
			&& strtoupper($wordArray[2]) === 'SET'
			&& strtolower(substr(str_replace('`', '',
$wordArray[3]), 0, 9)) === 'converted')
		{
			// Statement is special statement to reset conversion status
			$this->queryType = 'UTF8CNV';

			// Done with method
			return;
		}

		if ($command === 'ALTER TABLE')
		{
			$alterCommand = strtoupper($wordArray[3] . ' ' .
$wordArray[4]);

			if ($alterCommand === 'ADD COLUMN')
			{
				$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE
field = ' . $this->fixQuote($wordArray[5]);
				$this->queryType = 'ADD_COLUMN';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$this->fixQuote($wordArray[5]));
			}
			elseif ($alterCommand === 'ADD INDEX' || $alterCommand ===
'ADD KEY')
			{
				if ($pos = strpos($wordArray[5], '('))
				{
					$index = $this->fixQuote(substr($wordArray[5], 0, $pos));
				}
				else
				{
					$index = $this->fixQuote($wordArray[5]);
				}

				$result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE
Key_name = ' . $index;
				$this->queryType = 'ADD_INDEX';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$index);
			}
			elseif ($alterCommand === 'ADD UNIQUE')
			{
				$idxIndexName = 5;

				if (isset($wordArray[6]))
				{
					$addCmdCheck = strtoupper($wordArray[5]);

					if ($addCmdCheck === 'INDEX' || $addCmdCheck ===
'KEY')
					{
						$idxIndexName = 6;
					}
				}

				if ($pos = strpos($wordArray[$idxIndexName], '('))
				{
					$index = $this->fixQuote(substr($wordArray[$idxIndexName], 0,
$pos));
				}
				else
				{
					$index = $this->fixQuote($wordArray[$idxIndexName]);
				}

				$result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE
Key_name = ' . $index;
				$this->queryType = 'ADD_INDEX';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$index);
			}
			elseif ($alterCommand === 'DROP INDEX' || $alterCommand ===
'DROP KEY')
			{
				$index = $this->fixQuote($wordArray[5]);
				$result = 'SHOW INDEXES IN ' . $wordArray[2] . ' WHERE
Key_name = ' . $index;
				$this->queryType = 'DROP_INDEX';
				$this->checkQueryExpected = 0;
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$index);
			}
			elseif ($alterCommand === 'DROP COLUMN')
			{
				$index = $this->fixQuote($wordArray[5]);
				$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE
Field = ' . $index;
				$this->queryType = 'DROP_COLUMN';
				$this->checkQueryExpected = 0;
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$index);
			}
			elseif (strtoupper($wordArray[3]) === 'MODIFY')
			{
				// Kludge to fix problem with "integer unsigned"
				$type = $wordArray[5];

				if (isset($wordArray[6]))
				{
					$type = $this->fixInteger($wordArray[5], $wordArray[6]);
				}

				// Detect changes in NULL and in DEFAULT column attributes
				$changesArray = array_slice($wordArray, 6);
				$defaultCheck = $this->checkDefault($changesArray, $type);
				$nullCheck = $this->checkNull($changesArray);

				/**
				 * When we made the UTF8MB4 conversion then text becomes medium text -
so loosen the checks to these two types
				 * otherwise (for example) the profile fields profile_value check fails
- see https://github.com/joomla/joomla-cms/issues/9258
				 */
				$typeCheck = $this->fixUtf8mb4TypeChecks($type);

				$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE
field = ' . $this->fixQuote($wordArray[4])
					. ' AND ' . $typeCheck
					. ($defaultCheck ? ' AND ' . $defaultCheck : '')
					. ($nullCheck ? ' AND ' . $nullCheck : '');
				$this->queryType = 'CHANGE_COLUMN_TYPE';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$this->fixQuote($wordArray[4]), $type);
			}
			elseif (strtoupper($wordArray[3]) === 'CHANGE')
			{
				// Kludge to fix problem with "integer unsigned"
				$type = $wordArray[6];

				if (isset($wordArray[7]))
				{
					$type = $this->fixInteger($wordArray[6], $wordArray[7]);
				}
				
				// Detect changes in NULL and in DEFAULT column attributes
				$changesArray = array_slice($wordArray, 6);
				$defaultCheck = $this->checkDefault($changesArray, $type);
				$nullCheck = $this->checkNull($changesArray);

				/**
				 * When we made the UTF8MB4 conversion then text becomes medium text -
so loosen the checks to these two types
				 * otherwise (for example) the profile fields profile_value check fails
- see https://github.com/joomla/joomla-cms/issues/9258
				 */
				$typeCheck = $this->fixUtf8mb4TypeChecks($type);

				$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE
field = ' . $this->fixQuote($wordArray[5])
					. ' AND ' . $typeCheck
					. ($defaultCheck ? ' AND ' . $defaultCheck : '')
					. ($nullCheck ? ' AND ' . $nullCheck : '');
				$this->queryType = 'CHANGE_COLUMN_TYPE';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$this->fixQuote($wordArray[5]), $type);
			}
		}

		if ($command === 'CREATE TABLE')
		{
			if (strtoupper($wordArray[2] . $wordArray[3] . $wordArray[4]) ===
'IFNOTEXISTS')
			{
				$table = $wordArray[5];
			}
			else
			{
				$table = $wordArray[2];
			}

			$result = 'SHOW TABLES LIKE ' . $this->fixQuote($table);
			$this->queryType = 'CREATE_TABLE';
			$this->msgElements = array($this->fixQuote($table));
		}

		// Set fields based on results
		if ($this->checkQuery = $result)
		{
			// Unchecked status
			$this->checkStatus = 0;
		}
		else
		{
			// Skipped
			$this->checkStatus = -1;
		}
	}

	/**
	 * Fix up integer. Fixes problem with MySQL integer descriptions.
	 * On MySQL 8 display length is not shown anymore.
	 * This means we have to match e.g. both "int(10) unsigned" and
	 * "int unsigned", or both "int(11)" and
"int" and so on.
	 * The same applies to tinyint.
	 *
	 * @param   string  $type1  the column type
	 * @param   string  $type2  the column attributes
	 *
	 * @return  string  The original or changed column type.
	 *
	 * @since   2.5
	 */
	private function fixInteger($type1, $type2)
	{
		$result = $type1;

		if (strtolower(substr($type2, 0, 8)) === 'unsigned')
		{
			if (strtolower(substr($type1, 0, 7)) === 'tinyint')
			{
				$result = 'tinyint unsigned';
			}
			elseif (strtolower(substr($type1, 0, 3)) === 'int')
			{
				$result = 'int unsigned';
			}
			else
			{
				$result = $type1 . ' unsigned';
			}
		}
		elseif (strtolower(substr($type1, 0, 7)) === 'tinyint')
		{
			$result = 'tinyint';
		}
		elseif (strtolower(substr($type1, 0, 3)) === 'int')
		{
			$result = 'int';
		}

		return $result;
	}

	/**
	 * Fixes up a string for inclusion in a query.
	 * Replaces name quote character with normal quote for literal.
	 * Drops trailing semicolon. Injects the database prefix.
	 *
	 * @param   string  $string  The input string to be cleaned up.
	 *
	 * @return  string  The modified string.
	 *
	 * @since   2.5
	 */
	private function fixQuote($string)
	{
		$string = str_replace('`', '', $string);
		$string = str_replace(';', '', $string);
		$string = str_replace('#__', $this->db->getPrefix(),
$string);

		return $this->db->quote($string);
	}

	/**
	 * Make check query for column changes/modifications tolerant
	 * for automatic type changes of text columns, e.g. from TEXT
	 * to MEDIUMTEXT, after conversion from utf8 to utf8mb4, and
	 * fix integer (int or tinyint) columns without display length
	 * for MySQL 8.
	 *
	 * @param   string  $type  The column type found in the update query
	 *
	 * @return  string  The condition for type check in the check query
	 *
	 * @since   3.5
	 */
	private function fixUtf8mb4TypeChecks($type)
	{
		$uType = strtoupper(str_replace(';', '', $type));

		if ($uType === 'TINYINT UNSIGNED')
		{
			$typeCheck = 'UPPER(LEFT(type, 7)) = ' .
$this->db->quote('TINYINT')
				. ' AND UPPER(RIGHT(type, 9)) = ' .
$this->db->quote(' UNSIGNED');
		}
		elseif ($uType === 'TINYINT')
		{
			$typeCheck = 'UPPER(LEFT(type, 7)) = ' .
$this->db->quote('TINYINT');
		}
		elseif ($uType === 'INT UNSIGNED')
		{
			$typeCheck = 'UPPER(LEFT(type, 3)) = ' .
$this->db->quote('INT')
				. ' AND UPPER(RIGHT(type, 9)) = ' .
$this->db->quote(' UNSIGNED');
		}
		elseif ($uType === 'INT')
		{
			$typeCheck = 'UPPER(LEFT(type, 3)) = ' .
$this->db->quote('INT');
		}
		elseif ($this->db->hasUTF8mb4Support())
		{
			if ($uType === 'TINYTEXT')
			{
				$typeCheck = 'UPPER(type) IN (' .
$this->db->quote('TINYTEXT') . ',' .
$this->db->quote('TEXT') . ')';
			}
			elseif ($uType === 'TEXT')
			{
				$typeCheck = 'UPPER(type) IN (' .
$this->db->quote('TEXT') . ',' .
$this->db->quote('MEDIUMTEXT') . ')';
			}
			elseif ($uType === 'MEDIUMTEXT')
			{
				$typeCheck = 'UPPER(type) IN (' .
$this->db->quote('MEDIUMTEXT') . ',' .
$this->db->quote('LONGTEXT') . ')';
			}
			else
			{
				$typeCheck = 'UPPER(type) = ' .
$this->db->quote($uType);
			}
		}
		else
		{
			$typeCheck = 'UPPER(type) = ' .
$this->db->quote($uType);
		}

		return $typeCheck;
	}

	/**
	 * Create query clause for column changes/modifications for NULL attribute
	 *
	 * @param   array  $changesArray  The array of words after COLUMN name
	 *
	 * @return  string  The query clause for NULL check in the check query
	 *
	 * @since   3.8.6
	 */
	private function checkNull($changesArray)
	{
		// Find NULL keyword
		$index = array_search('null', array_map('strtolower',
$changesArray));

		// Create the check
		if ($index !== false)
		{
			if ($index == 0 || strtolower($changesArray[$index - 1]) !==
'not')
			{
				return ' `null` = ' .
$this->db->quote('YES');
			}
			else
			{
				return ' `null` = ' . $this->db->quote('NO');
			}
		}

		return false;
	}

	/**
	 * Create query clause for column changes/modifications for DEFAULT
attribute
	 *
	 * @param   array   $changesArray  The array of words after COLUMN name
	 * @param   string  $type          The type of the COLUMN
	 *
	 * @return  string  The query clause for DEFAULT check in the check query
	 *
	 * @since   3.8.6
	 */
	private function checkDefault($changesArray, $type)
	{
		// Skip types that do not support default values
		$type = strtolower($type);
		if (substr($type, -4) === 'text' || substr($type, -4) ===
'blob')
		{
			return false;
		}

		// Find DEFAULT keyword
		$index = array_search('default',
array_map('strtolower', $changesArray));
	
		// Create the check
		if ($index !== false)
		{
			if (strtolower($changesArray[$index + 1]) === 'null')
			{
				return ' `default` IS NULL';
			}
			else
			{
				return ' `default` = ' . $changesArray[$index + 1];
			}
		}

		return false;
	}
}
PK�}�[��}p�(�(#ChangeItem/PostgresqlChangeItem.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Schema\ChangeItem;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Schema\ChangeItem;

/**
 * Checks the database schema against one PostgreSQL DDL query to see if it
has been run.
 *
 * @since  3.0
 */
class PostgresqlChangeItem extends ChangeItem
{
	/**
	 * Checks a DDL query to see if it is a known type
	 * If yes, build a check query to see if the DDL has been run on the
database.
	 * If successful, the $msgElements, $queryType, $checkStatus and
$checkQuery fields are populated.
	 * The $msgElements contains the text to create the user message.
	 * The $checkQuery contains the SQL query to check whether the schema
change has
	 * been run against the current database. The $queryType contains the type
of
	 * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN,
CHANGE_COLUMN_TYPE, ADD_INDEX).
	 * The $checkStatus field is set to zero if the query is created
	 *
	 * If not successful, $checkQuery is empty and , and $checkStatus is -1.
	 * For example, this will happen if the current line is a non-DDL
statement.
	 *
	 * @return void
	 *
	 * @since  3.0
	 */
	protected function buildCheckQuery()
	{
		// Initialize fields in case we can't create a check query
		$this->checkStatus = -1; // change status to skipped

		$result = null;
		$splitIntoWords = "~'[^']*'(*SKIP)(*F)|\s+~";
		$splitIntoActions =
"~'[^']*'(*SKIP)(*F)|\([^)]*\)(*SKIP)(*F)|,~";

		// Remove any newlines
		$this->updateQuery = str_replace("\n", '',
$this->updateQuery);

		// Remove trailing whitespace and semicolon
		$this->updateQuery = rtrim($this->updateQuery, ";
\t\n\r\0\x0B");

		// Fix up extra spaces around () and in general
		$find = array('#((\s*)\(\s*([^)\s]+)\s*)(\))#',
'#(\s)(\s*)#');
		$replace = array('($3)', '$1');
		$updateQuery = preg_replace($find, $replace, $this->updateQuery);
		$wordArray = preg_split($splitIntoWords, $updateQuery, null,
PREG_SPLIT_NO_EMPTY);

		$totalWords = count($wordArray);

		// First, make sure we have an array of at least 6 elements
		// if not, we can't make a check query for this one
		if ($totalWords < 6)
		{
			// Done with method
			return;
		}

		// We can only make check queries for alter table and create table
queries
		$command = strtoupper($wordArray[0] . ' ' . $wordArray[1]);

		if ($command === 'ALTER TABLE')
		{
			// Check only the last action
			$actions = ltrim(substr($updateQuery, strpos($updateQuery,
$wordArray[2]) + strlen($wordArray[2])));
			$actions = preg_split($splitIntoActions, $actions);

			// Get the last action
			$lastActionArray = preg_split($splitIntoWords, end($actions), null,
PREG_SPLIT_NO_EMPTY);

			// Replace all actions by the last one
			array_splice($wordArray, 3, $totalWords, $lastActionArray);

			$alterCommand = strtoupper($wordArray[3] . ' ' .
$wordArray[4]);

			if ($alterCommand === 'ADD COLUMN')
			{
				$result = 'SELECT column_name'
					. ' FROM information_schema.columns'
					. ' WHERE table_name='
					. $this->fixQuote($wordArray[2])
					. ' AND column_name=' . $this->fixQuote($wordArray[5]);

				$this->queryType = 'ADD_COLUMN';
				$this->msgElements = array(
					$this->fixQuote($wordArray[2]),
					$this->fixQuote($wordArray[5])
				);
			}
			elseif ($alterCommand === 'DROP COLUMN')
			{
				$result = 'SELECT column_name'
					. ' FROM information_schema.columns'
					. ' WHERE table_name='
					. $this->fixQuote($wordArray[2])
					. ' AND column_name=' . $this->fixQuote($wordArray[5]);

				$this->queryType = 'DROP_COLUMN';
				$this->checkQueryExpected = 0;
				$this->msgElements = array(
					$this->fixQuote($wordArray[2]),
					$this->fixQuote($wordArray[5])
				);
			}
			elseif ($alterCommand === 'ALTER COLUMN')
			{
				$alterAction = strtoupper($wordArray[6]);

				if ($alterAction === 'TYPE')
				{
					$type = implode(' ', array_slice($wordArray, 7));

					if ($pos = stripos($type, ' USING '))
					{
						$type = substr($type, 0, $pos);
					}

					if ($pos = strpos($type, '('))
					{
						$datatype = substr($type, 0, $pos);
					}
					else
					{
						$datatype = $type;
					}

					$result = 'SELECT column_name, data_type '
						. 'FROM information_schema.columns WHERE table_name='
						. $this->fixQuote($wordArray[2]) . ' AND column_name='
						. $this->fixQuote($wordArray[5])
						. ' AND data_type=' . $this->fixQuote($datatype);

					if ($datatype === 'character varying')
					{
						$result .= ' AND character_maximum_length = ' . (int)
substr($type, $pos + 1);
					}

					$this->queryType = 'CHANGE_COLUMN_TYPE';
					$this->msgElements = array(
						$this->fixQuote($wordArray[2]),
						$this->fixQuote($wordArray[5]),
						$type
					);
				}
				elseif ($alterAction === 'SET')
				{
					$alterType = strtoupper($wordArray[7]);

					if ($alterType === 'NOT' &&
strtoupper($wordArray[8]) === 'NULL')
					{
						$result = 'SELECT column_name, data_type, is_nullable'
							. ' FROM information_schema.columns'
							. ' WHERE table_name=' . $this->fixQuote($wordArray[2])
							. ' AND column_name=' . $this->fixQuote($wordArray[5])
							. ' AND is_nullable=' .
$this->fixQuote('NO');

						$this->queryType = 'CHANGE_COLUMN_TYPE';
						$this->msgElements = array(
							$this->fixQuote($wordArray[2]),
							$this->fixQuote($wordArray[5]),
							'NOT NULL'
						);
					}
					elseif ($alterType === 'DEFAULT')
					{
						$result = 'SELECT column_name, data_type, is_nullable'
							. ' FROM information_schema.columns'
							. ' WHERE table_name=' . $this->fixQuote($wordArray[2])
							. ' AND column_name=' . $this->fixQuote($wordArray[5])
							. ' AND (CASE (position(' .
$this->db->quote('::') . ' in column_default))'
							. ' WHEN 0 THEN '
							. ' column_default = ' .
$this->db->quote($wordArray[8])
							. ' ELSE '
							. ' substring(column_default, 1, (position(' .
$this->db->quote('::')
							. ' in column_default) -1))  = ' .
$this->db->quote($wordArray[8])
							. ' END)';

						$this->queryType = 'CHANGE_COLUMN_TYPE';
						$this->msgElements = array(
							$this->fixQuote($wordArray[2]),
							$this->fixQuote($wordArray[5]),
							'DEFAULT ' . $wordArray[8]
						);
					}
				}
				elseif ($alterAction === 'DROP')
				{
					$alterType = strtoupper($wordArray[7]);

					if ($alterType === 'DEFAULT')
					{
						$result = 'SELECT column_name, data_type, is_nullable ,
column_default'
							. ' FROM information_schema.columns'
							. ' WHERE table_name=' . $this->fixQuote($wordArray[2])
							. ' AND column_name=' . $this->fixQuote($wordArray[5])
							. ' AND column_default IS NOT NULL';

						$this->queryType = 'CHANGE_COLUMN_TYPE';
						$this->checkQueryExpected = 0;
						$this->msgElements = array(
							$this->fixQuote($wordArray[2]),
							$this->fixQuote($wordArray[5]),
							'NOT DEFAULT'
						);
					}
					elseif ($alterType === 'NOT' &&
strtoupper($wordArray[8]) === 'NULL')
					{
						$result = 'SELECT column_name, data_type, is_nullable ,
column_default'
							. ' FROM information_schema.columns'
							. ' WHERE table_name=' . $this->fixQuote($wordArray[2])
							. ' AND column_name=' . $this->fixQuote($wordArray[5])
							. ' AND is_nullable = ' .
$this->fixQuote('NO');

						$this->queryType = 'CHANGE_COLUMN_TYPE';
						$this->checkQueryExpected = 0;
						$this->msgElements = array(
							$this->fixQuote($wordArray[2]),
							$this->fixQuote($wordArray[5]),
							'NULL'
						);
					}
				}
			}
		}
		elseif ($command === 'DROP INDEX')
		{
			if (strtoupper($wordArray[2] . $wordArray[3]) === 'IFEXISTS')
			{
				$idx = $this->fixQuote($wordArray[4]);
			}
			else
			{
				$idx = $this->fixQuote($wordArray[2]);
			}

			$result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx;
			$this->queryType = 'DROP_INDEX';
			$this->checkQueryExpected = 0;
			$this->msgElements = array($this->fixQuote($idx));
		}
		elseif ($command === 'CREATE INDEX' || (strtoupper($command .
$wordArray[2]) === 'CREATE UNIQUE INDEX'))
		{
			if ($wordArray[1] === 'UNIQUE')
			{
				$idx = $this->fixQuote($wordArray[3]);
				$table = $this->fixQuote($wordArray[5]);
			}
			else
			{
				$idx = $this->fixQuote($wordArray[2]);
				$table = $this->fixQuote($wordArray[4]);
			}

			$result = 'SELECT * FROM pg_indexes WHERE indexname=' . $idx .
' AND tablename=' . $table;
			$this->queryType = 'ADD_INDEX';
			$this->checkQueryExpected = 1;
			$this->msgElements = array($table, $idx);
		}

		if ($command === 'CREATE TABLE')
		{
			if (strtoupper($wordArray[2] . $wordArray[3] . $wordArray[4]) ===
'IFNOTEXISTS')
			{
				$table = $this->fixQuote($wordArray[5]);
			}
			else
			{
				$table = $this->fixQuote($wordArray[2]);
			}

			$result = 'SELECT table_name FROM information_schema.tables WHERE
table_name=' . $table;
			$this->queryType = 'CREATE_TABLE';
			$this->checkQueryExpected = 1;
			$this->msgElements = array($table);
		}

		// Set fields based on results
		if ($this->checkQuery = $result)
		{
			// Unchecked status
			$this->checkStatus = 0;
		}
		else
		{
			// Skipped
			$this->checkStatus = -1;
		}
	}

	/**
	 * Fix up integer. Fixes problem with PostgreSQL integer descriptions.
	 * If you change a column to "integer unsigned" it shows
	 * as "int(10) unsigned" in the check query.
	 *
	 * @param   string  $type1  the column type
	 * @param   string  $type2  the column attributes
	 *
	 * @return  string  The original or changed column type.
	 *
	 * @since   3.0
	 */
	private function fixInteger($type1, $type2)
	{
		$result = $type1;

		if (strtolower($type1) === 'integer' &&
strtolower(substr($type2, 0, 8)) === 'unsigned')
		{
			$result = 'unsigned int(10)';
		}

		return $result;
	}

	/**
	 * Fixes up a string for inclusion in a query.
	 * Replaces name quote character with normal quote for literal.
	 * Drops trailing semicolon. Injects the database prefix.
	 *
	 * @param   string  $string  The input string to be cleaned up.
	 *
	 * @return  string  The modified string.
	 *
	 * @since   3.0
	 */
	private function fixQuote($string)
	{
		$string = str_replace('"', '', $string);
		$string = str_replace(';', '', $string);
		$string = str_replace('#__', $this->db->getPrefix(),
$string);

		return $this->db->quote($string);
	}
}
PK�}�[�^�yyChangeItem/SqlsrvChangeItem.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Schema\ChangeItem;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Schema\ChangeItem;

/**
 * Checks the database schema against one SQL Server DDL query to see if it
has been run.
 *
 * @since  2.5
 */
class SqlsrvChangeItem extends ChangeItem
{
	/**
	 * Checks a DDL query to see if it is a known type
	 * If yes, build a check query to see if the DDL has been run on the
database.
	 * If successful, the $msgElements, $queryType, $checkStatus and
$checkQuery fields are populated.
	 * The $msgElements contains the text to create the user message.
	 * The $checkQuery contains the SQL query to check whether the schema
change has
	 * been run against the current database. The $queryType contains the type
of
	 * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN,
CHANGE_COLUMN_TYPE, ADD_INDEX).
	 * The $checkStatus field is set to zero if the query is created
	 *
	 * If not successful, $checkQuery is empty and , and $checkStatus is -1.
	 * For example, this will happen if the current line is a non-DDL
statement.
	 *
	 * @return void
	 *
	 * @since  2.5
	 */
	protected function buildCheckQuery()
	{
		// Initialize fields in case we can't create a check query
		$this->checkStatus = -1; // change status to skipped
		$result = null;

		// Remove any newlines
		$this->updateQuery = str_replace("\n", '',
$this->updateQuery);

		// Fix up extra spaces around () and in general
		$find = array('#((\s*)\(\s*([^)\s]+)\s*)(\))#',
'#(\s)(\s*)#');
		$replace = array('($3)', '$1');
		$updateQuery = preg_replace($find, $replace, $this->updateQuery);
		$wordArray = explode(' ', $updateQuery);

		// First, make sure we have an array of at least 6 elements
		// if not, we can't make a check query for this one
		if (count($wordArray) < 6)
		{
			// Done with method
			return;
		}

		// We can only make check queries for alter table and create table
queries
		$command = strtoupper($wordArray[0] . ' ' . $wordArray[1]);

		if ($command === 'ALTER TABLE')
		{
			$alterCommand = strtoupper($wordArray[3] . ' ' .
$wordArray[4]);

			if ($alterCommand === 'ADD')
			{
				$result = 'SELECT * FROM INFORMATION_SCHEMA.Columns ' .
$wordArray[2] . ' WHERE COLUMN_NAME = ' .
$this->fixQuote($wordArray[5]);
				$this->queryType = 'ADD';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$this->fixQuote($wordArray[5]));
			}
			elseif ($alterCommand === 'CREATE INDEX')
			{
				$index = $this->fixQuote(substr($wordArray[5], 0,
strpos($wordArray[5], '(')));
				$result = 'SELECT * FROM SYS.INDEXES ' . $wordArray[2] .
' WHERE name = ' . $index;
				$this->queryType = 'CREATE INDEX';
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$index);
			}
			elseif (strtoupper($wordArray[3]) === 'MODIFY' ||
strtoupper($wordArray[3]) === 'CHANGE')
			{
				$result = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS 
WHERE table_name = ' . $this->fixQuote($wordArray[2]);
				$this->queryType = 'ALTER COLUMN COLUMN_NAME =' .
$this->fixQuote($wordArray[4]);
				$this->msgElements = array($this->fixQuote($wordArray[2]),
$this->fixQuote($wordArray[4]));
			}
		}

		if ($command === 'CREATE TABLE')
		{
			$table = $wordArray[2];
			$result = 'SELECT * FROM sys.TABLES WHERE NAME = ' .
$this->fixQuote($table);
			$this->queryType = 'CREATE_TABLE';
			$this->msgElements = array($this->fixQuote($table));
		}

		// Set fields based on results
		if ($this->checkQuery = $result)
		{
			// Unchecked status
			$this->checkStatus = 0;
		}
		else
		{
			// Skipped
			$this->checkStatus = -1;
		}
	}

	/**
	 * Fix up integer. Fixes problem with MySQL integer descriptions.
	 * If you change a column to "integer unsigned" it shows
	 * as "int(10) unsigned" in the check query.
	 *
	 * @param   string  $type1  the column type
	 * @param   string  $type2  the column attributes
	 *
	 * @return  string  The original or changed column type.
	 *
	 * @since   2.5
	 */
	private function fixInteger($type1, $type2)
	{
		$result = $type1;

		if (strtolower($type1) === 'integer' &&
strtolower(substr($type2, 0, 8)) === 'unsigned')
		{
			$result = 'int';
		}

		return $result;
	}

	/**
	 * Fixes up a string for inclusion in a query.
	 * Replaces name quote character with normal quote for literal.
	 * Drops trailing semicolon. Injects the database prefix.
	 *
	 * @param   string  $string  The input string to be cleaned up.
	 *
	 * @return  string  The modified string.
	 *
	 * @since   2.5
	 */
	private function fixQuote($string)
	{
		$string = str_replace('[', '', $string);
		$string = str_replace(']', '', $string);
		$string = str_replace('"', '', $string);
		$string = str_replace(';', '', $string);
		$string = str_replace('#__', $this->db->getPrefix(),
$string);

		return $this->db->quote($string);
	}
}
PK�}�[��yj��ChangeItem.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Schema;

defined('JPATH_PLATFORM') or die;

/**
 * Each object represents one query, which is one line from a DDL SQL
query.
 * This class is used to check the site's database to see if the DDL
query has been run.
 * If not, it provides the ability to fix the database by re-running the
DDL query.
 * The queries are parsed from the update files in the folder
 * `administrator/components/com_admin/sql/updates/<database>`.
 * These updates are run automatically if the site was updated using
com_installer.
 * However, it is possible that the program files could be updated without
udpating
 * the database (for example, if a user just copies the new files over the
top of an
 * existing installation).
 *
 * This is an abstract class. We need to extend it for each database and
add a
 * buildCheckQuery() method that creates the query to check that a DDL
query has been run.
 *
 * @since  2.5
 */
abstract class ChangeItem
{
	/**
	 * Update file: full path file name where query was found
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $file = null;

	/**
	 * Update query: query used to change the db schema (one line from the
file)
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $updateQuery = null;

	/**
	 * Check query: query used to check the db schema
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $checkQuery = null;

	/**
	 * Check query result: expected result of check query if database is up to
date
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $checkQueryExpected = 1;

	/**
	 * \JDatabaseDriver object
	 *
	 * @var    \JDatabaseDriver
	 * @since  2.5
	 */
	public $db = null;

	/**
	 * Query type: To be used in building a language key for a
	 * message to tell user what was checked / changed
	 * Possible values: ADD_TABLE, ADD_COLUMN, CHANGE_COLUMN_TYPE, ADD_INDEX
	 *
	 * @var    string
	 * @since  2.5
	 */
	public $queryType = null;

	/**
	 * Array with values for use in a \JText::sprintf statment indicating what
was checked
	 *
	 * Tells you what the message should be, based on which elements are
defined, as follows:
	 *     For ADD_TABLE: table
	 *     For ADD_COLUMN: table, column
	 *     For CHANGE_COLUMN_TYPE: table, column, type
	 *     For ADD_INDEX: table, index
	 *
	 * @var    array
	 * @since  2.5
	 */
	public $msgElements = array();

	/**
	 * Checked status
	 *
	 * @var    integer   0=not checked, -1=skipped, -2=failed, 1=succeeded
	 * @since  2.5
	 */
	public $checkStatus = 0;

	/**
	 * Rerun status
	 *
	 * @var    int   0=not rerun, -1=skipped, -2=failed, 1=succeeded
	 * @since  2.5
	 */
	public $rerunStatus = 0;

	/**
	 * Constructor: builds check query and message from $updateQuery
	 *
	 * @param   \JDatabaseDriver  $db     Database connector object
	 * @param   string            $file   Full path name of the sql file
	 * @param   string            $query  Text of the sql query (one line of
the file)
	 *
	 * @since   2.5
	 */
	public function __construct($db, $file, $query)
	{
		$this->updateQuery = $query;
		$this->file = $file;
		$this->db = $db;
		$this->buildCheckQuery();
	}

	/**
	 * Returns a reference to the ChangeItem object.
	 *
	 * @param   \JDatabaseDriver  $db     Database connector object
	 * @param   string            $file   Full path name of the sql file
	 * @param   string            $query  Text of the sql query (one line of
the file)
	 *
	 * @return  ChangeItem  instance based on the database driver
	 *
	 * @since   2.5
	 * @throws  \RuntimeException if class for database driver not found
	 */
	public static function getInstance($db, $file, $query)
	{
		// Get the class name
		$serverType = $db->getServerType();

		// For `mssql` server types, convert the type to `sqlsrv`
		if ($serverType === 'mssql')
		{
			$serverType = 'sqlsrv';
		}

		$class = '\\Joomla\\CMS\\Schema\\ChangeItem\\' .
ucfirst($serverType) . 'ChangeItem';

		// If the class exists, return it.
		if (class_exists($class))
		{
			return new $class($db, $file, $query);
		}

		throw new \RuntimeException(sprintf('ChangeItem child class not
found for the %s database driver', $serverType), 500);
	}

	/**
	 * Checks a DDL query to see if it is a known type
	 * If yes, build a check query to see if the DDL has been run on the
database.
	 * If successful, the $msgElements, $queryType, $checkStatus and
$checkQuery fields are populated.
	 * The $msgElements contains the text to create the user message.
	 * The $checkQuery contains the SQL query to check whether the schema
change has
	 * been run against the current database. The $queryType contains the type
of
	 * DDL query that was run (for example, CREATE_TABLE, ADD_COLUMN,
CHANGE_COLUMN_TYPE, ADD_INDEX).
	 * The $checkStatus field is set to zero if the query is created
	 *
	 * If not successful, $checkQuery is empty and , and $checkStatus is -1.
	 * For example, this will happen if the current line is a non-DDL
statement.
	 *
	 * @return void
	 *
	 * @since  2.5
	 */
	abstract protected function buildCheckQuery();

	/**
	 * Runs the check query and checks that 1 row is returned
	 * If yes, return true, otherwise return false
	 *
	 * @return  boolean  true on success, false otherwise
	 *
	 * @since  2.5
	 */
	public function check()
	{
		$this->checkStatus = -1;

		if ($this->checkQuery)
		{
			$this->db->setQuery($this->checkQuery);

			try
			{
				$rows = $this->db->loadRowList(0);
			}
			catch (\RuntimeException $e)
			{
				// Still render the error message from the Exception object
				\JFactory::getApplication()->enqueueMessage($e->getMessage(),
'error');
				$this->checkStatus = -2;

				return $this->checkStatus;
			}

			if (count($rows) === $this->checkQueryExpected)
			{
				$this->checkStatus = 1;

				return $this->checkStatus;
			}

			$this->checkStatus = -2;
		}

		return $this->checkStatus;
	}

	/**
	 * Runs the update query to apply the change to the database
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function fix()
	{
		if ($this->checkStatus === -2)
		{
			// At this point we have a failed query
			$query =
$this->db->convertUtf8mb4QueryToUtf8($this->updateQuery);
			$this->db->setQuery($query);

			if ($this->db->execute())
			{
				if ($this->check())
				{
					$this->checkStatus = 1;
					$this->rerunStatus = 1;
				}
				else
				{
					$this->rerunStatus = -2;
				}
			}
			else
			{
				$this->rerunStatus = -2;
			}
		}
	}
}
PK�}�[D=����
ChangeSet.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Schema;

defined('JPATH_PLATFORM') or die;

jimport('joomla.filesystem.folder');

/**
 * Contains a set of JSchemaChange objects for a particular instance of
Joomla.
 * Each of these objects contains a DDL query that should have been run
against
 * the database when this database was created or updated. This enables the
 * Installation Manager to check that the current database schema is up to
date.
 *
 * @since  2.5
 */
class ChangeSet
{
	/**
	 * Array of ChangeItem objects
	 *
	 * @var    ChangeItem[]
	 * @since  2.5
	 */
	protected $changeItems = array();

	/**
	 * \JDatabaseDriver object
	 *
	 * @var    \JDatabaseDriver
	 * @since  2.5
	 */
	protected $db = null;

	/**
	 * Folder where SQL update files will be found
	 *
	 * @var    string
	 * @since  2.5
	 */
	protected $folder = null;

	/**
	 * The singleton instance of this object
	 *
	 * @var    ChangeSet
	 * @since  3.5.1
	 */
	protected static $instance;

	/**
	 * Constructor: builds array of $changeItems by processing the .sql files
in a folder.
	 * The folder for the Joomla core updates is
`administrator/components/com_admin/sql/updates/<database>`.
	 *
	 * @param   \JDatabaseDriver  $db      The current database object
	 * @param   string            $folder  The full path to the folder
containing the update queries
	 *
	 * @since   2.5
	 */
	public function __construct($db, $folder = null)
	{
		$this->db = $db;
		$this->folder = $folder;
		$updateFiles = $this->getUpdateFiles();
		$updateQueries = $this->getUpdateQueries($updateFiles);

		foreach ($updateQueries as $obj)
		{
			$changeItem = ChangeItem::getInstance($db, $obj->file,
$obj->updateQuery);

			if ($changeItem->queryType === 'UTF8CNV')
			{
				// Execute the special update query for utf8mb4 conversion status reset
				try
				{
					$this->db->setQuery($changeItem->updateQuery)->execute();
				}
				catch (\RuntimeException $e)
				{
					\JFactory::getApplication()->enqueueMessage($e->getMessage(),
'error');
				}
			}
			else
			{
				// Normal change item
				$this->changeItems[] = $changeItem;
			}
		}

		// If on mysql, add a query at the end to check for utf8mb4 conversion
status
		if ($this->db->getServerType() === 'mysql')
		{
			// Let the update query do nothing when being executed
			$tmpSchemaChangeItem = ChangeItem::getInstance(
				$db,
				'database.php',
				'UPDATE ' .
$this->db->quoteName('#__utf8_conversion')
				. ' SET ' . $this->db->quoteName('converted')
. ' = '
				. $this->db->quoteName('converted') . ';');

			// Set to not skipped
			$tmpSchemaChangeItem->checkStatus = 0;

			// Set the check query
			if ($this->db->hasUTF8mb4Support())
			{
				$converted = 5;
				$tmpSchemaChangeItem->queryType =
'UTF8_CONVERSION_UTF8MB4';
			}
			else
			{
				$converted = 3;
				$tmpSchemaChangeItem->queryType = 'UTF8_CONVERSION_UTF8';
			}

			$tmpSchemaChangeItem->checkQuery = 'SELECT '
				. $this->db->quoteName('converted')
				. ' FROM ' .
$this->db->quoteName('#__utf8_conversion')
				. ' WHERE ' .
$this->db->quoteName('converted') . ' = ' .
$converted;

			// Set expected records from check query
			$tmpSchemaChangeItem->checkQueryExpected = 1;

			$tmpSchemaChangeItem->msgElements = array();

			$this->changeItems[] = $tmpSchemaChangeItem;
		}
	}

	/**
	 * Returns a reference to the ChangeSet object, only creating it if it
doesn't already exist.
	 *
	 * @param   \JDatabaseDriver  $db      The current database object
	 * @param   string            $folder  The full path to the folder
containing the update queries
	 *
	 * @return  ChangeSet
	 *
	 * @since   2.5
	 */
	public static function getInstance($db, $folder = null)
	{
		if (!is_object(static::$instance))
		{
			static::$instance = new ChangeSet($db, $folder);
		}

		return static::$instance;
	}

	/**
	 * Checks the database and returns an array of any errors found.
	 * Note these are not database errors but rather situations where
	 * the current schema is not up to date.
	 *
	 * @return   array Array of errors if any.
	 *
	 * @since    2.5
	 */
	public function check()
	{
		$errors = array();

		foreach ($this->changeItems as $item)
		{
			if ($item->check() === -2)
			{
				// Error found
				$errors[] = $item;
			}
		}

		return $errors;
	}

	/**
	 * Runs the update query to apply the change to the database
	 *
	 * @return  void
	 *
	 * @since   2.5
	 */
	public function fix()
	{
		$this->check();

		foreach ($this->changeItems as $item)
		{
			$item->fix();
		}
	}

	/**
	 * Returns an array of results for this set
	 *
	 * @return  array  associative array of changeitems grouped by unchecked,
ok, error, and skipped
	 *
	 * @since   2.5
	 */
	public function getStatus()
	{
		$result = array('unchecked' => array(), 'ok' =>
array(), 'error' => array(), 'skipped' =>
array());

		foreach ($this->changeItems as $item)
		{
			switch ($item->checkStatus)
			{
				case 0:
					$result['unchecked'][] = $item;
					break;
				case 1:
					$result['ok'][] = $item;
					break;
				case -2:
					$result['error'][] = $item;
					break;
				case -1:
					$result['skipped'][] = $item;
					break;
			}
		}

		return $result;
	}

	/**
	 * Gets the current database schema, based on the highest version number.
	 * Note that the .sql files are named based on the version and date, so
	 * the file name of the last file should match the database schema version
	 * in the #__schemas table.
	 *
	 * @return  string  the schema version for the database
	 *
	 * @since   2.5
	 */
	public function getSchema()
	{
		$updateFiles = $this->getUpdateFiles();
		$result = new \SplFileInfo(array_pop($updateFiles));

		return $result->getBasename('.sql');
	}

	/**
	 * Get list of SQL update files for this database
	 *
	 * @return  array  list of sql update full-path names
	 *
	 * @since   2.5
	 */
	private function getUpdateFiles()
	{
		// Get the folder from the database name
		$sqlFolder = $this->db->getServerType();

		// For `mssql` server types, convert the type to `sqlazure`
		if ($sqlFolder === 'mssql')
		{
			$sqlFolder = 'sqlazure';
		}

		// Default folder to core com_admin
		if (!$this->folder)
		{
			$this->folder = JPATH_ADMINISTRATOR .
'/components/com_admin/sql/updates/';
		}

		return \JFolder::files(
			$this->folder . '/' . $sqlFolder, '\.sql$', 1,
true, array('.svn', 'CVS', '.DS_Store',
'__MACOSX'), array('^\..*', '.*~'), true
		);
	}

	/**
	 * Get array of SQL queries
	 *
	 * @param   array  $sqlfiles  Array of .sql update filenames.
	 *
	 * @return  array  Array of \stdClass objects where:
	 *                    file=filename,
	 *                    update_query = text of SQL update query
	 *
	 * @since   2.5
	 */
	private function getUpdateQueries(array $sqlfiles)
	{
		// Hold results as array of objects
		$result = array();

		foreach ($sqlfiles as $file)
		{
			$buffer = file_get_contents($file);

			// Create an array of queries from the sql file
			$queries = \JDatabaseDriver::splitSql($buffer);

			foreach ($queries as $query)
			{
				$fileQueries = new \stdClass;
				$fileQueries->file = $file;
				$fileQueries->updateQuery = $query;
				$result[] = $fileQueries;
			}
		}

		return $result;
	}
}
PK�}�[��9��1�1ChangeItem/MysqlChangeItem.phpnu�[���PK�}�[��}p�(�(#B2ChangeItem/PostgresqlChangeItem.phpnu�[���PK�}�[�^�yyI[ChangeItem/SqlsrvChangeItem.phpnu�[���PK�}�[��yj��oChangeItem.phpnu�[���PK�}�[D=����
�ChangeSet.phpnu�[���PK��