2 namespace wcf\system\package
;
3 use wcf\data\package\Package
;
4 use wcf\system\database\util\SQLParser
;
5 use wcf\system\exception\SystemException
;
9 * Extends SQLParser by testing and logging functions.
12 * @copyright 2001-2018 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\System\Package
16 class PackageInstallationSQLParser
extends SQLParser
{
21 protected $package = null;
24 * activates the testing mode
27 protected $test = false;
33 protected $action = 'install';
36 * list of existing database tables
39 protected $existingTables = [];
42 * list of logged tables
45 protected $knownTables = [];
48 * list of conflicted database tables
51 protected $conflicts = [];
54 * list of created/deleted tables
57 protected $tableLog = [];
60 * list of created/deleted columns
63 protected $columnLog = [];
66 * list of created/deleted indices
69 protected $indexLog = [];
72 * Creates a new PackageInstallationSQLParser object.
74 * @param string $queries
75 * @param Package $package
76 * @param string $action
78 public function __construct($queries, Package
$package, $action = 'install') {
79 $this->package
= $package;
80 $this->action
= $action;
82 parent
::__construct($queries);
86 * Performs a test of the given queries.
88 * @return array conflicts
90 public function test() {
91 $this->conflicts
= [];
93 // get all existing tables from database
94 $this->existingTables
= WCF
::getDB()->getEditor()->getTableNames();
97 $this->getKnownTables();
99 // enable testing mode
105 // disable testing mode
109 return $this->conflicts
;
113 * Logs executed sql queries
115 public function log() {
117 foreach ($this->tableLog
as $logEntry) {
118 $sql = "DELETE FROM wcf".WCF_N
."_package_installation_sql_log
120 $statement = WCF
::getDB()->prepareStatement($sql);
121 $statement->execute([$logEntry['tableName']]);
123 if ($logEntry['action'] == 'insert') {
124 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_sql_log
125 (packageID, sqlTable)
127 $statement = WCF
::getDB()->prepareStatement($sql);
128 $statement->execute([
129 $logEntry['packageID'],
130 $logEntry['tableName']
136 if (!empty($this->columnLog
)) {
137 $sql = "DELETE FROM wcf".WCF_N
."_package_installation_sql_log
140 $deleteStatement = WCF
::getDB()->prepareStatement($sql);
142 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_sql_log
143 (packageID, sqlTable, sqlColumn)
145 $insertStatement = WCF
::getDB()->prepareStatement($sql);
147 foreach ($this->columnLog
as $logEntry) {
148 $deleteStatement->execute([
149 $logEntry['tableName'],
150 $logEntry['columnName']
153 if ($logEntry['action'] == 'insert') {
154 $insertStatement->execute([
155 $logEntry['packageID'],
156 $logEntry['tableName'],
157 $logEntry['columnName']
164 if (!empty($this->indexLog
)) {
165 $sql = "DELETE FROM wcf".WCF_N
."_package_installation_sql_log
168 $deleteStatement = WCF
::getDB()->prepareStatement($sql);
170 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_sql_log
171 (packageID, sqlTable, sqlIndex)
173 $insertStatement = WCF
::getDB()->prepareStatement($sql);
175 foreach ($this->indexLog
as $logEntry) {
176 $deleteStatement->execute([
177 $logEntry['tableName'],
178 $logEntry['indexName']
181 if ($logEntry['action'] == 'insert') {
182 $insertStatement->execute([
183 $logEntry['packageID'],
184 $logEntry['tableName'],
185 $logEntry['indexName']
193 * Fetches known sql tables and their owners from installation log.
195 protected function getKnownTables() {
196 $sql = "SELECT packageID, sqlTable
197 FROM wcf".WCF_N
."_package_installation_sql_log
200 $statement = WCF
::getDB()->prepareStatement($sql);
201 $statement->execute();
202 $this->knownTables
= $statement->fetchMap('sqlTable', 'packageID');
206 * Returns the owner of a specific database table column.
208 * @param string $tableName
209 * @param string $columnName
210 * @return integer package id
212 protected function getColumnOwnerID($tableName, $columnName) {
213 $sql = "SELECT packageID
214 FROM wcf".WCF_N
."_package_installation_sql_log
217 $statement = WCF
::getDB()->prepareStatement($sql);
218 $statement->execute([
222 $row = $statement->fetchArray();
223 if (!empty($row['packageID'])) return $row['packageID'];
224 else if (isset($this->knownTables
[$tableName])) return $this->knownTables
[$tableName];
229 * Returns the owner of a specific database index.
231 * @param string $tableName
232 * @param string $indexName
233 * @return integer package id
235 protected function getIndexOwnerID($tableName, $indexName) {
236 $sql = "SELECT packageID
237 FROM wcf".WCF_N
."_package_installation_sql_log
240 $statement = WCF
::getDB()->prepareStatement($sql);
241 $statement->execute([
245 $row = $statement->fetchArray();
246 if (!empty($row['packageID'])) return $row['packageID'];
247 else if (isset($this->knownTables
[$tableName])) return $this->knownTables
[$tableName];
254 protected function executeCreateTableStatement($tableName, $columns, $indices = []) {
256 if (in_array($tableName, $this->existingTables
)) {
257 if (isset($this->knownTables
[$tableName]) && $this->knownTables
[$tableName] != $this->package
->packageID
) {
258 throw new SystemException("Cannot recreate table '".$tableName."'. A package can only overwrite own tables.");
261 if (!isset($this->conflicts
['CREATE TABLE'])) $this->conflicts
['CREATE TABLE'] = [];
262 $this->conflicts
['CREATE TABLE'][] = $tableName;
268 $this->tableLog
[] = ['tableName' => $tableName, 'packageID' => $this->package
->packageID
, 'action' => 'insert'];
271 parent
::executeCreateTableStatement($tableName, $columns, $indices);
278 protected function executeAddColumnStatement($tableName, $columnName, $columnData) {
280 if (!isset($this->knownTables
[$tableName])) {
281 throw new SystemException("Cannot add column '".$columnName."' to table '".$tableName."'.");
286 $this->columnLog
[] = ['tableName' => $tableName, 'columnName' => $columnName, 'packageID' => $this->package
->packageID
, 'action' => 'insert'];
289 parent
::executeAddColumnStatement($tableName, $columnName, $columnData);
296 protected function executeAlterColumnStatement($tableName, $oldColumnName, $newColumnName, $newColumnData) {
298 if ($ownerPackageID = $this->getColumnOwnerID($tableName, $oldColumnName)) {
299 if ($ownerPackageID != $this->package
->packageID
) {
300 throw new SystemException("Cannot alter column '".$oldColumnName."'. A package can only change own columns.");
306 if ($oldColumnName != $newColumnName) {
307 $this->columnLog
[] = ['tableName' => $tableName, 'columnName' => $oldColumnName, 'packageID' => $this->package
->packageID
, 'action' => 'delete'];
308 $this->columnLog
[] = ['tableName' => $tableName, 'columnName' => $newColumnName, 'packageID' => $this->package
->packageID
, 'action' => 'insert'];
312 parent
::executeAlterColumnStatement($tableName, $oldColumnName, $newColumnName, $newColumnData);
319 protected function executeAddIndexStatement($tableName, $indexName, $indexData) {
322 $this->indexLog
[] = ['tableName' => $tableName, 'indexName' => $indexName, 'packageID' => $this->package
->packageID
, 'action' => 'insert'];
325 parent
::executeAddIndexStatement($tableName, $indexName, $indexData);
332 protected function executeAddForeignKeyStatement($tableName, $indexName, $indexData) {
335 $this->indexLog
[] = ['tableName' => $tableName, 'indexName' => $indexName, 'packageID' => $this->package
->packageID
, 'action' => 'insert'];
338 parent
::executeAddForeignKeyStatement($tableName, $indexName, $indexData);
345 protected function executeDropColumnStatement($tableName, $columnName) {
347 if ($ownerPackageID = $this->getColumnOwnerID($tableName, $columnName)) {
348 if ($ownerPackageID != $this->package
->packageID
) {
349 throw new SystemException("Cannot drop column '".$columnName."'. A package can only drop own columns.");
355 $this->columnLog
[] = ['tableName' => $tableName, 'columnName' => $columnName, 'packageID' => $this->package
->packageID
, 'action' => 'delete'];
358 parent
::executeDropColumnStatement($tableName, $columnName);
365 protected function executeDropIndexStatement($tableName, $indexName) {
367 if ($ownerPackageID = $this->getIndexOwnerID($tableName, $indexName)) {
368 if ($ownerPackageID != $this->package
->packageID
) {
369 throw new SystemException("Cannot drop index '".$indexName."'. A package can only drop own indices.");
375 $this->indexLog
[] = ['tableName' => $tableName, 'indexName' => $indexName, 'packageID' => $this->package
->packageID
, 'action' => 'delete'];
378 parent
::executeDropIndexStatement($tableName, $indexName);
385 protected function executeDropPrimaryKeyStatement($tableName) {
387 if ($ownerPackageID = $this->getIndexOwnerID($tableName, '')) {
388 if ($ownerPackageID != $this->package
->packageID
) {
389 throw new SystemException("Cannot drop primary key from '".$tableName."'. A package can only drop own indices.");
395 // $this->indexLog[] = array('tableName' => $tableName, 'indexName' => '', 'packageID' => $this->package->packageID, 'action' => 'delete');
398 parent
::executeDropPrimaryKeyStatement($tableName);
405 protected function executeDropForeignKeyStatement($tableName, $indexName) {
407 if ($ownerPackageID = $this->getIndexOwnerID($tableName, $indexName)) {
408 if ($ownerPackageID != $this->package
->packageID
) {
409 throw new SystemException("Cannot drop index '".$indexName."'. A package can only drop own indices.");
415 $this->indexLog
[] = ['tableName' => $tableName, 'indexName' => $indexName, 'packageID' => $this->package
->packageID
, 'action' => 'delete'];
418 parent
::executeDropForeignKeyStatement($tableName, $indexName);
425 protected function executeDropTableStatement($tableName) {
427 if (in_array($tableName, $this->existingTables
)) {
428 if (isset($this->knownTables
[$tableName]) && $this->knownTables
[$tableName] != $this->package
->packageID
) {
429 throw new SystemException("Cannot drop table '".$tableName."'. A package can only drop own tables.");
435 $this->tableLog
[] = ['tableName' => $tableName, 'packageID' => $this->package
->packageID
, 'action' => 'delete'];
438 parent
::executeDropTableStatement($tableName);
445 protected function executeStandardStatement($query) {
447 parent
::executeStandardStatement($query);