You've already forked joomla_test
first commit
This commit is contained in:
250
libraries/cms/schema/changeitem.php
Normal file
250
libraries/cms/schema/changeitem.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
* @since 2.5
|
||||
*/
|
||||
abstract class JSchemaChangeitem
|
||||
{
|
||||
/**
|
||||
* 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 JSchemaChangeitem 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 JSchemaChangeitem 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
|
||||
$dbname = $db->name;
|
||||
|
||||
if ($dbname == 'mysqli')
|
||||
{
|
||||
$dbname = 'mysql';
|
||||
}
|
||||
elseif ($dbname == 'sqlazure')
|
||||
{
|
||||
$dbname = 'sqlsrv';
|
||||
}
|
||||
|
||||
$class = 'JSchemaChangeitem' . ucfirst($dbname);
|
||||
|
||||
// If the class exists, return it.
|
||||
if (class_exists($class))
|
||||
{
|
||||
return new $class($db, $file, $query);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('JSchemaChangeitem child class not found for the %s database driver', $dbname), 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);
|
||||
$rows = $this->db->loadObject();
|
||||
if ($rows !== false)
|
||||
{
|
||||
if (count($rows) === $this->checkQueryExpected)
|
||||
{
|
||||
$this->checkStatus = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->checkStatus = -2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$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
|
||||
$this->db->setQuery($this->updateQuery);
|
||||
if ($this->db->execute())
|
||||
{
|
||||
if ($this->check())
|
||||
{
|
||||
$this->checkStatus = 1;
|
||||
$this->rerunStatus = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rerunStatus = -2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rerunStatus = -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
libraries/cms/schema/changeitem/index.html
Normal file
1
libraries/cms/schema/changeitem/index.html
Normal file
@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
192
libraries/cms/schema/changeitem/mysql.php
Normal file
192
libraries/cms/schema/changeitem/mysql.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* Checks the database schema against one MySQL DDL query to see if it has been run.
|
||||
*
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
* @since 2.5
|
||||
*/
|
||||
class JSchemaChangeitemMysql extends JSchemaChangeitem
|
||||
{
|
||||
/**
|
||||
* 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 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 UNIQUE')
|
||||
{
|
||||
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 == 'DROP INDEX')
|
||||
{
|
||||
$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 = $this->fixQuote($wordArray[5]);
|
||||
if (isset($wordArray[6]))
|
||||
{
|
||||
$type = $this->fixQuote($this->fixInteger($wordArray[5], $wordArray[6]));
|
||||
}
|
||||
$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE field = ' . $this->fixQuote($wordArray[4]) . ' AND type = ' . $type;
|
||||
$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 = $this->fixQuote($this->fixInteger($wordArray[6], $wordArray[7]));
|
||||
$result = 'SHOW COLUMNS IN ' . $wordArray[2] . ' WHERE field = ' . $this->fixQuote($wordArray[4]) . ' AND type = ' . $type;
|
||||
$this->queryType = 'CHANGE_COLUMN_TYPE';
|
||||
$this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[4]), $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.
|
||||
* 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(10) unsigned';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes up a string for inclusion in a query.
|
||||
* Replaces name quote character with normal quote for literal.
|
||||
* Drops trailing semi-colon. 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);
|
||||
}
|
||||
}
|
246
libraries/cms/schema/changeitem/postgresql.php
Normal file
246
libraries/cms/schema/changeitem/postgresql.php
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* Checks the database schema against one PostgreSQL DDL query to see if it has been run.
|
||||
*
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
* @since 3.0
|
||||
*/
|
||||
class JSchemaChangeitemPostgresql extends JSchemaChangeitem
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// 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 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 === 'ALTER COLUMN')
|
||||
{
|
||||
if (strtoupper($wordArray[6]) == 'TYPE')
|
||||
{
|
||||
$type = '';
|
||||
for ($i = 7; $i < count($wordArray); $i++)
|
||||
{
|
||||
$type .= $wordArray[$i] . ' ';
|
||||
}
|
||||
|
||||
if ($pos = strpos($type, '('))
|
||||
{
|
||||
$type = substr($type, 0, $pos);
|
||||
}
|
||||
|
||||
if ($pos = strpos($type, ';'))
|
||||
{
|
||||
$type = substr($type, 0, $pos);
|
||||
}
|
||||
|
||||
$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($type);
|
||||
|
||||
$this->queryType = 'CHANGE_COLUMN_TYPE';
|
||||
$this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), $type);
|
||||
}
|
||||
elseif (strtoupper($wordArray[7] . ' ' . $wordArray[8]) == 'NOT NULL')
|
||||
{
|
||||
if (strtoupper($wordArray[6]) == 'SET')
|
||||
{
|
||||
// SET NOT NULL
|
||||
$isNullable = $this->fixQuote('NO');
|
||||
}
|
||||
else
|
||||
{
|
||||
// DROP NOT NULL
|
||||
$isNullable = $this->fixQuote('YES');
|
||||
}
|
||||
$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=' . $isNullable;
|
||||
|
||||
$this->queryType = 'CHANGE_COLUMN_TYPE';
|
||||
$this->checkQueryExpected = 1;
|
||||
$this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), $isNullable);
|
||||
}
|
||||
elseif (strtoupper($wordArray[7]) === 'DEFAULT')
|
||||
{
|
||||
if (strtoupper($wordArray[6]) == 'SET')
|
||||
{
|
||||
$isNullDef = 'IS NOT NULL';
|
||||
}
|
||||
else
|
||||
{
|
||||
// DROP DEFAULT
|
||||
$isNullDef = 'IS NULL';
|
||||
}
|
||||
$result = 'SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_name='
|
||||
. $this->fixQuote($wordArray[2]) . ' AND column_name=' . $this->fixQuote($wordArray[5])
|
||||
. ' AND column_default ' . $isNullDef;
|
||||
|
||||
$this->queryType = 'CHANGE_COLUMN_TYPE';
|
||||
$this->checkQueryExpected = 1;
|
||||
$this->msgElements = array($this->fixQuote($wordArray[2]), $this->fixQuote($wordArray[5]), $isNullDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 semi-colon. 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);
|
||||
}
|
||||
}
|
148
libraries/cms/schema/changeitem/sqlsrv.php
Normal file
148
libraries/cms/schema/changeitem/sqlsrv.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* Checks the database schema against one SQL Server DDL query to see if it has been run.
|
||||
*
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
* @since 2.5
|
||||
*/
|
||||
class JSchemaChangeitemSqlsrv extends JSchemaChangeitem
|
||||
{
|
||||
/**
|
||||
* 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[5];
|
||||
$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 semi-colon. 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);
|
||||
}
|
||||
}
|
276
libraries/cms/schema/changeset.php
Normal file
276
libraries/cms/schema/changeset.php
Normal file
@ -0,0 +1,276 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* @package Joomla.Libraries
|
||||
* @subpackage Schema
|
||||
* @since 2.5
|
||||
*/
|
||||
class JSchemaChangeset
|
||||
{
|
||||
/**
|
||||
* Array of JSchemaChangeitem objects
|
||||
*
|
||||
* @var array
|
||||
* @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
|
||||
*/
|
||||
protected $folder = null;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->changeItems[] = JSchemaChangeitem::getInstance($db, $obj->file, $obj->updateQuery);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the JSchemaChangeset 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 JSchemaChangeset
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function getInstance($db, $folder)
|
||||
{
|
||||
static $instance;
|
||||
|
||||
if (!is_object($instance))
|
||||
{
|
||||
$instance = new JSchemaChangeset($db, $folder);
|
||||
}
|
||||
|
||||
return $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->name;
|
||||
|
||||
if ($sqlFolder == 'mysqli')
|
||||
{
|
||||
$sqlFolder = 'mysql';
|
||||
}
|
||||
elseif ($sqlFolder == 'sqlsrv')
|
||||
{
|
||||
$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)
|
||||
{
|
||||
if ($trimmedQuery = $this->trimQuery($query))
|
||||
{
|
||||
$fileQueries = new stdClass;
|
||||
$fileQueries->file = $file;
|
||||
$fileQueries->updateQuery = $trimmedQuery;
|
||||
$result[] = $fileQueries;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim comment and blank lines out of a query string
|
||||
*
|
||||
* @param string $query query string to be trimmed
|
||||
*
|
||||
* @return string String with leading comment lines removed
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
private function trimQuery($query)
|
||||
{
|
||||
$query = trim($query);
|
||||
|
||||
while (substr($query, 0, 1) == '#' || substr($query, 0, 2) == '--' || substr($query, 0, 2) == '/*')
|
||||
{
|
||||
$endChars = (substr($query, 0, 1) == '#' || substr($query, 0, 2) == '--') ? "\n" : "*/";
|
||||
|
||||
if ($position = strpos($query, $endChars))
|
||||
{
|
||||
$query = trim(substr($query, $position + strlen($endChars)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no newline, the rest of the file is a comment, so return an empty string.
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return trim($query);
|
||||
}
|
||||
}
|
1
libraries/cms/schema/index.html
Normal file
1
libraries/cms/schema/index.html
Normal file
@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
Reference in New Issue
Block a user