--- /dev/null
+<?php
+use wcf\system\database\table\column\BinaryDatabaseTableColumn;
+use wcf\system\database\table\column\CharDatabaseTableColumn;
+use wcf\system\database\table\column\DateDatabaseTableColumn;
+use wcf\system\database\table\column\DatetimeDatabaseTableColumn;
+use wcf\system\database\table\column\DefaultFalseBooleanDatabaseTableColumn;
+use wcf\system\database\table\column\EnumDatabaseTableColumn;
+use wcf\system\database\table\column\IntDatabaseTableColumn;
+use wcf\system\database\table\column\MediumtextDatabaseTableColumn;
+use wcf\system\database\table\column\NotNullInt10DatabaseTableColumn;
+use wcf\system\database\table\column\NotNullVarchar255DatabaseTableColumn;
+use wcf\system\database\table\column\ObjectIdDatabaseTableColumn;
+use wcf\system\database\table\column\SmallintDatabaseTableColumn;
+use wcf\system\database\table\column\TextDatabaseTableColumn;
+use wcf\system\database\table\DatabaseTable;
+use wcf\system\database\table\DatabaseTableChangeProcessor;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\database\table\index\DatabaseTablePrimaryIndex;
+use wcf\system\package\plugin\ScriptPackageInstallationPlugin;
+use wcf\system\WCF;
+
+/**
+ * Updates the database table layout from WoltLab Suite Core 3.1 to 5.2.
+ *
+ * TODO: untested, potentially incomplete
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+
+$tables = [
+ DatabaseTable::create('wcf1_bbcode_media_provider')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('isDisabled')
+ ]),
+
+ DatabaseTable::create('wcf1_blacklist_status')
+ ->columns([
+ DateDatabaseTableColumn::create('date')
+ ->notNull(),
+ DefaultFalseBooleanDatabaseTableColumn::create('delta1'),
+ DefaultFalseBooleanDatabaseTableColumn::create('delta2'),
+ DefaultFalseBooleanDatabaseTableColumn::create('delta3'),
+ DefaultFalseBooleanDatabaseTableColumn::create('delta4')
+ ]),
+
+ DatabaseTable::create('wcf1_blacklist_entry')
+ ->columns([
+ EnumDatabaseTableColumn::create('type')
+ ->enumValues(['email', 'ipv4', 'ipv6','username']),
+ BinaryDatabaseTableColumn::create('hash')
+ ->length(32),
+ DatetimeDatabaseTableColumn::create('lastSeen')
+ ->notNull(),
+ SmallintDatabaseTableColumn::create('occurrences')
+ ->length(5)
+ ->notNull()
+ ])
+ ->indices([
+ DatabaseTableIndex::create('entry')
+ ->type(DatabaseTableIndex::UNIQUE_TYPE)
+ ->columns(['type', 'hash']),
+ DatabaseTableIndex::create('numberOfReports')
+ ->columns(['type', 'occurrences'])
+ ]),
+
+ DatabaseTable::create('wcf1_box')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('isDisabled')
+ ]),
+
+ DatabaseTable::create('wcf1_category')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('descriptionUseHtml')
+ ]),
+
+ DatabaseTable::create('wcf1_comment')
+ ->columns([
+ MediumtextDatabaseTableColumn::create('message')
+ ]),
+
+ DatabaseTable::create('wcf1_comment_response')
+ ->columns([
+ MediumtextDatabaseTableColumn::create('message')
+ ]),
+
+ DatabaseTable::create('wcf1_contact_attachment')
+ ->columns([
+ NotNullInt10DatabaseTableColumn::create('attachmentID'),
+ CharDatabaseTableColumn::create('accessKey')
+ ->length(40)
+ ->notNull()
+ ])
+ ->foreignKeys([
+ DatabaseTableForeignKey::create()
+ ->columns(['attachmentID'])
+ ->referencedTable('wcf1_attachment')
+ ->referencedColumns(['attachmentID'])
+ ->onDelete('CASCADE')
+ ]),
+
+ DatabaseTable::create('wcf1_language_item')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('isCustomLanguageItem')
+ ]),
+
+ DatabaseTable::create('wcf1_like')
+ ->columns([
+ NotNullInt10DatabaseTableColumn::create('reactionTypeID')
+ ])
+ ->foreignKeys([
+ DatabaseTableForeignKey::create()
+ ->columns(['reactionTypeID'])
+ ->referencedTable('wcf1_reaction_type')
+ ->referencedColumns(['reactionTypeID'])
+ ->onDelete('CASCADE')
+ ]),
+
+ DatabaseTable::create('wcf1_like_object')
+ ->columns([
+ TextDatabaseTableColumn::create('cachedReactions')
+ ]),
+
+ DatabaseTable::create('wcf1_media')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('captionEnableHtml'),
+ NotNullInt10DatabaseTableColumn::create('downloads')
+ ->defaultValue(0),
+ NotNullInt10DatabaseTableColumn::create('lastDownload')
+ ->defaultValue(0)
+ ]),
+
+ DatabaseTable::create('wcf1_page')
+ ->columns([
+ IntDatabaseTableColumn::create('overrideApplicationPackageID')
+ ->length(10),
+ DefaultFalseBooleanDatabaseTableColumn::create('enableShareButtons')
+ ])
+ ->foreignKeys([
+ DatabaseTableForeignKey::create()
+ ->columns(['overrideApplicationPackageID'])
+ ->referencedTable('wcf1_package')
+ ->referencedColumns(['packageID'])
+ ->onDelete('SET NULL')
+ ]),
+
+ DatabaseTable::create('wcf1_reaction_type')
+ ->columns([
+ ObjectIdDatabaseTableColumn::create('reactionTypeID'),
+ // TODO: is currently not `not null`
+ NotNullVarchar255DatabaseTableColumn::create('title'),
+ NotNullInt10DatabaseTableColumn::create('showOrder')
+ ->defaultValue(0),
+ // TODO: should be varchar
+ MediumtextDatabaseTableColumn::create('iconFile'),
+ DefaultFalseBooleanDatabaseTableColumn::create('isDisabled')
+ ])
+ ->indices([
+ DatabaseTablePrimaryIndex::create()
+ ->columns(['reactionTypeID'])
+ ]),
+
+ DatabaseTable::create('wcf1_style')
+ ->columns([
+ EnumDatabaseTableColumn::create('apiVersion')
+ ->enumValues(['3.0', '3.1', '5.2'])
+ ]),
+
+ DatabaseTable::create('wcf1_trophy')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('revokeAutomatically'),
+ DefaultFalseBooleanDatabaseTableColumn::create('trophyUseHtml'),
+ NotNullInt10DatabaseTableColumn::create('showOrder')
+ ->defaultValue(0)
+ ]),
+
+ DatabaseTable::create('wcf1_user')
+ ->columns([
+ NotNullInt10DatabaseTableColumn::create('articles')
+ ->defaultValue(0),
+ NotNullVarchar255DatabaseTableColumn::create('blacklistMatches')
+ ->defaultValue('')
+ ]),
+
+ DatabaseTable::create('wcf1_user_group')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('allowMention')
+ ]),
+
+ DatabaseTable::create('wcf1_user_trophy')
+ ->columns([
+ DefaultFalseBooleanDatabaseTableColumn::create('trophyUseHtml')
+ ]),
+];
+
+(new DatabaseTableChangeProcessor(
+ /** @var ScriptPackageInstallationPlugin $this */
+ $this->installation->getPackage(),
+ $tables,
+ WCF::getDB()->getEditor())
+)->process();
*/
abstract public function alterColumn($tableName, $oldColumnName, $newColumnName, $newColumnData);
+ /**
+ * Adds, alters and drops multiple columns at once.
+ *
+ * @param string $tableName
+ * @param array $alterData
+ */
+ public function alterColumns($tableName, $alterData) {
+ throw new NotImplementedException();
+ }
+
/**
* Drops an existing column.
*
*/
public function getColumns($tableName) {
$columns = [];
- $regex = new Regex('([a-z]+)\((.+)\)', Regex::CASE_INSENSITIVE);
+ $regex = new Regex('([a-z]+)\((.+)\)( unsigned)?', Regex::CASE_INSENSITIVE);
$sql = "SHOW COLUMNS FROM `".$tableName."`";
$statement = $this->dbObj->prepareStatement($sql);
$length = '';
$decimals = '';
$enumValues = '';
+ $unsigned = false;
if (!empty($typeMatches)) {
$type = $typeMatches[1];
}
break;
}
+
+ if (isset($typeMatches[3])) {
+ $unsigned = true;
+ }
}
$columns[] = ['name' => $row['Field'], 'data' => [
'default' => $row['Default'],
'autoIncrement' => $row['Extra'] == 'auto_increment' ? true : false,
'enumValues' => $enumValues,
- 'decimals' => $decimals
+ 'decimals' => $decimals,
+ 'unsigned' => $unsigned
]];
}
$statement->execute();
}
+ /**
+ * @inheritDoc
+ */
+ public function alterColumns($tableName, $alterData) {
+ $queries = "";
+ foreach ($alterData as $columnName => $data) {
+ switch ($data['action']) {
+ case 'add':
+ $queries .= "ADD COLUMN {$this->buildColumnDefinition($columnName, $data['data'])},";
+ break;
+
+ case 'alter':
+ $queries .= "CHANGE COLUMN `{$columnName}` {$this->buildColumnDefinition($data['oldColumnName'], $data['data'])},";
+ break;
+
+ case 'drop':
+ $queries .= "DROP COLUMN `{$columnName}`,";
+ break;
+ }
+ }
+
+ $this->dbObj->prepareStatement("ALTER TABLE `{$tableName}` " . rtrim($queries, ','))->execute();
+ }
+
/**
* @inheritDoc
*/
$definition = "`".$columnName."`";
// column type
$definition .= " ".$columnData['type'];
+
+ if (!empty($columnData['unsigned'])) {
+ $definition .= ' UNSIGNED';
+ }
+
// column length and decimals
if (!empty($columnData['length'])) {
$definition .= "(".$columnData['length'].(!empty($columnData['decimals']) ? ",".$columnData['decimals'] : "").")";
--- /dev/null
+<?php
+namespace wcf\system\database\table;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\database\editor\DatabaseEditor;
+use wcf\system\database\table\column\IDatabaseTableColumn;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\index\DatabaseTableIndex;
+
+/**
+ * PHP representation of an existing database table or the intended layout of an non-existing or
+ * existing database table.
+ *
+ * @author Alexander Ebert, Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table
+ * @since 5.2
+ */
+class DatabaseTable {
+ use TDroppableDatabaseComponent;
+
+ /**
+ * intended database table's columns
+ * @var IDatabaseTableColumn[]
+ */
+ protected $columns = [];
+
+ /**
+ * intended database table's foreign keys
+ * @var DatabaseTableForeignKey[]
+ */
+ protected $foreignKeys = [];
+
+ /**
+ * intended database table's indices
+ * @var DatabaseTableIndex[]
+ */
+ protected $indices = [];
+
+ /**
+ * name of the database table
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * Creates a new instance of `DatabaseTable`.
+ *
+ * @param string $name name of the database table
+ */
+ protected function __construct($name) {
+ $this->name = ApplicationHandler::insertRealDatabaseTableNames($name);
+ }
+
+ /**
+ * Sets the columns of the database table.
+ *
+ * @param IDatabaseTableColumn[] $columns added/dropped columns
+ * @return $this this database table
+ * @throws \InvalidArgumentException if any column is invalid or duplicate column names exist
+ */
+ public function columns(array $columns) {
+ $this->columns = [];
+ foreach ($columns as $column) {
+ if (!($column instanceof IDatabaseTableColumn)) {
+ throw new \InvalidArgumentException("Added columns have to be instances of '" . IDatabaseTableColumn::class . "'.");
+ }
+
+ if (isset($this->columns[$column->getName()])) {
+ throw new \InvalidArgumentException("Duplicate column with name '{$column->getName()}'.");
+ }
+
+ $this->columns[$column->getName()] = $column;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the foreign keys of the database table.
+ *
+ * @param DatabaseTableForeignKey[] $foreignKeys added/dropped foreign keys
+ * @return $this this database table
+ * @throws \InvalidArgumentException if any foreign key is invalid or duplicate foreign key names exist
+ */
+ public function foreignKeys(array $foreignKeys) {
+ $this->foreignKeys = [];
+ foreach ($foreignKeys as $foreignKey) {
+ if (!($foreignKey instanceof DatabaseTableForeignKey)) {
+ throw new \InvalidArgumentException("Added foreign keys have to be instances of '" . DatabaseTableForeignKey::class . "'.");
+ }
+
+ if (empty($foreignKey->getColumns())) {
+ throw new \InvalidArgumentException("Missing columns for foreign key.");
+ }
+
+ if ($foreignKey->getName() === '') {
+ $foreignKey->name(md5($this->getName() . '_' . $foreignKey->getColumns()[0]) . '_fk');
+ }
+
+ if (isset($this->foreignKeys[$foreignKey->getName()])) {
+ throw new \InvalidArgumentException("Duplicate foreign key with name '{$foreignKey->getName()}'.");
+ }
+
+ $this->foreignKeys[$foreignKey->getName()] = $foreignKey;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the columns of the table.
+ *
+ * @return IDatabaseTableColumn[]
+ */
+ public function getColumns() {
+ return $this->columns;
+ }
+
+ /**
+ * Returns the foreign keys of the table.
+ *
+ * @return DatabaseTableForeignKey[]
+ */
+ public function getForeignKeys() {
+ return $this->foreignKeys;
+ }
+
+ /**
+ * Returns the indices of the table.
+ *
+ * @return DatabaseTableIndex[]
+ */
+ public function getIndices() {
+ return $this->indices;
+ }
+
+ /**
+ * Returns the name of the database table.
+ *
+ * @return string database table name
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns a `DatabaseTable` object with the given name.
+ *
+ * @param string $tableName
+ * @return static
+ */
+ public static function create($tableName) {
+ return new static($tableName);
+ }
+
+ /**
+ * Returns a `DatabaseTable` object for an existing database table with the given name.
+ *
+ * @param DatabaseEditor $dbEditor
+ * @param string $tableName
+ * @return DatabaseTable
+ */
+ public static function createFromExistingTable(DatabaseEditor $dbEditor, $tableName) {
+ $table = new static($tableName);
+
+ $columns = [];
+ foreach ($dbEditor->getColumns($tableName) as $columnData) {
+ $className = 'wcf\system\database\table\column\\' . ucfirst(strtolower($columnData['data']['type'])) . 'DatabaseTableColumn';
+ if (!class_exists($className)) {
+ throw new \InvalidArgumentException("Unknown database table column type '{$columnData['data']['type']}'.");
+ }
+
+ $columns[$columnData['name']] = $className::createFromData($columnData['name'], $columnData['data']);
+ }
+ $table->columns($columns);
+
+ $foreignKeys = [];
+ foreach ($dbEditor->getForeignKeys($tableName) as $foreignKeysName => $foreignKeyData) {
+ $foreignKeys[$foreignKeysName] = DatabaseTableForeignKey::createFromData($foreignKeysName, $foreignKeyData);
+ }
+ $table->foreignKeys($foreignKeys);
+
+ $indices = [];
+ foreach ($dbEditor->getIndexInformation($tableName) as $indexName => $indexData) {
+ if (!isset($foreignKeys[$indexName])) {
+ $indices[$indexName] = DatabaseTableIndex::createFromData($indexName, $indexData);
+ }
+ }
+ $table->indices($indices);
+
+ return $table;
+ }
+
+ /**
+ * Sets the indices of the database table.
+ *
+ * @param DatabaseTableIndex[] $indices added/dropped indices
+ * @return $this this database table
+ * @throws \InvalidArgumentException if any index is invalid or duplicate index key names exist
+ */
+ public function indices(array $indices) {
+ $this->indices = [];
+ foreach ($indices as $index) {
+ if (!($index instanceof DatabaseTableIndex)) {
+ throw new \InvalidArgumentException("Added indices have to be instances of '" . DatabaseTableIndex::class . "'.");
+ }
+
+ if ($index->getName() === '') {
+ $index->name(md5($this->getName() . '_' . $index->getColumns()[0]));
+ }
+
+ if (isset($this->foreignKeys[$index->getName()])) {
+ throw new \InvalidArgumentException("Duplicate index with name '{$index->getName()}'.");
+ }
+
+ $this->indices[$index->getName()] = $index;
+ }
+
+ return $this;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table;
+use wcf\data\package\Package;
+use wcf\system\database\editor\DatabaseEditor;
+use wcf\system\database\table\column\IDatabaseTableColumn;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\package\SplitNodeException;
+use wcf\system\WCF;
+
+/**
+ * Processes a given set of changes to database tables.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table
+ * @since 5.2
+ */
+class DatabaseTableChangeProcessor {
+ /**
+ * added columns grouped by the table they belong to
+ * @var IDatabaseTableColumn[][]
+ */
+ protected $addedColumns = [];
+
+ /**
+ * added indices grouped by the table they belong to
+ * @var DatabaseTableIndex[][]
+ */
+ protected $addedIndices = [];
+
+ /**
+ * added tables
+ * @var DatabaseTable[]
+ */
+ protected $addedTables = [];
+
+ /**
+ * maps the registered database table column names to the ids of the packages they belong to
+ * @var int[][]
+ */
+ protected $columnPackageIDs = [];
+
+ /**
+ * database editor to apply the relevant changes to the table layouts
+ * @var DatabaseEditor
+ */
+ protected $dbEditor;
+
+ /**
+ * dropped columns grouped by the table they belong to
+ * @var IDatabaseTableColumn[][]
+ */
+ protected $droppedColumns = [];
+
+ /**
+ * dropped indices grouped by the table they belong to
+ * @var DatabaseTableIndex[][]|DatabaseTableForeignKey[][]
+ */
+ protected $droppedIndices = [];
+
+ /**
+ * dropped tables
+ * @var DatabaseTable[]
+ */
+ protected $droppedTables = [];
+
+ /**
+ * list of all existing tables in the used database
+ * @var string[]
+ */
+ protected $existingTableNames = [];
+
+ /**
+ * maps the registered database table index names to the ids of the packages they belong to
+ * @var int[][]
+ */
+ protected $indexPackageIDs = [];
+
+ /**
+ * maps the registered database table foreign key names to the ids of the packages they belong to
+ * @var int[][]
+ */
+ protected $foreignKeyPackageIDs = [];
+
+ /**
+ * is `true` if only one change will be handled per request
+ * @var bool
+ */
+ protected $oneChangePerRequest = true;
+
+ /**
+ * package that wants to apply the changes
+ * @var Package
+ */
+ protected $package;
+
+ /**
+ * layouts/layout changes of the relevant database table
+ * @var DatabaseTable[]
+ */
+ protected $tables;
+
+ /**
+ * maps the registered database table names to the ids of the packages they belong to
+ * @var int[]
+ */
+ protected $tablePackageIDs = [];
+
+ /**
+ * Creates a new instance of `DatabaseTableChangeProcessor`.
+ *
+ * @param Package $package
+ * @param DatabaseTable[] $tables
+ * @param DatabaseEditor $dbEditor
+ * @param bool $oneChangePerRequest
+ */
+ public function __construct(Package $package, array $tables, DatabaseEditor $dbEditor, $oneChangePerRequest = true) {
+ $this->package = $package;
+
+ $tableNames = [];
+ foreach ($tables as $table) {
+ if (!($table instanceof DatabaseTable)) {
+ throw new \InvalidArgumentException("Tables must be instance of '" . DatabaseTable::class . "'");
+ }
+
+ $tableNames[] = $table->getName();
+ }
+
+ $this->tables = $tables;
+ $this->dbEditor = $dbEditor;
+ $this->oneChangePerRequest = $oneChangePerRequest;
+
+ $this->existingTableNames = $dbEditor->getTableNames();
+
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add('sqlTable IN (?)', [$tableNames]);
+
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_package_installation_sql_log
+ " . $conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ while ($row = $statement->fetchArray()) {
+ if ($row['sqlIndex'] === '' && $row['sqlColumn'] === '') {
+ $this->tablePackageIDs[$row['sqlTable']] = $row['packageID'];
+ }
+ else if ($row['sqlIndex'] === '') {
+ $this->columnPackageIDs[$row['sqlTable']][$row['sqlColumn']] = $row['packageID'];
+ }
+ else if (substr($row['sqlIndex'], -3) === '_fk') {
+ $this->foreignKeyPackageIDs[$row['sqlTable']][$row['sqlIndex']] = $row['packageID'];
+ }
+ else {
+ $this->indexPackageIDs[$row['sqlTable']][$row['sqlIndex']] = $row['packageID'];
+ }
+ }
+ }
+
+ /**
+ * Creates the given table.
+ *
+ * @param DatabaseTable $table
+ * @throws SplitNodeException
+ */
+ protected function createTable(DatabaseTable $table) {
+ $columnData = array_map(function(IDatabaseTableColumn $column) {
+ return [
+ 'data' => $column->getData(),
+ 'name' => $column->getName()
+ ];
+ }, $table->getColumns());
+ $indexData = array_map(function(DatabaseTableIndex $index) {
+ return [
+ 'data' => $index->getData(),
+ 'name' => $index->getName()
+ ];
+ }, $table->getIndices());
+
+ $this->dbEditor->createTable($table->getName(), $columnData, $indexData);
+
+ foreach ($table->getForeignKeys() as $foreignKey) {
+ $this->dbEditor->addForeignKey($table->getName(), $foreignKey->getName(), $foreignKey->getData());
+ }
+
+ $this->addedTables[] = $table;
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Created table '{$table->getName()}'.");
+ }
+ }
+
+ /**
+ * Drops the given table.
+ *
+ * @param DatabaseTable $table
+ * @throws SplitNodeException
+ */
+ protected function dropTable(DatabaseTable $table) {
+ $this->dbEditor->dropTable($table->getName());
+
+ $this->droppedTables[] = $table;
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Dropped table '{$table->getName()}'.");
+ }
+ }
+
+ /**
+ * Returns the id of the package to with the given column belongs to. If there is no specific
+ * log entry for the given column, the table log is checked and the relevant package id of
+ * the whole table is returned. If the package of the table is also unknown, `null` is returned.
+ *
+ * @param DatabaseTable $table
+ * @param IDatabaseTableColumn $column
+ * @return null|int
+ */
+ protected function getColumnPackageID(DatabaseTable $table, IDatabaseTableColumn $column) {
+ if (isset($this->columnPackageIDs[$table->getName()][$column->getName()])) {
+ return $this->columnPackageIDs[$table->getName()][$column->getName()];
+ }
+ else if (isset($this->tablePackageIDs[$table->getName()])) {
+ return $this->tablePackageIDs[$table->getName()];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the id of the package to with the given foreign key belongs to. If there is no specific
+ * log entry for the given foreign key, the table log is checked and the relevant package id of
+ * the whole table is returned. If the package of the table is also unknown, `null` is returned.
+ *
+ * @param DatabaseTable $table
+ * @param DatabaseTableForeignKey $foreignKey
+ * @return null|int
+ */
+ protected function getForeignKeyPackageID(DatabaseTable $table, DatabaseTableForeignKey $foreignKey) {
+ if (isset($this->foreignKeyPackageIDs[$table->getName()][$foreignKey->getName()])) {
+ return $this->foreignKeyPackageIDs[$table->getName()][$foreignKey->getName()];
+ }
+ else if (isset($this->tablePackageIDs[$table->getName()])) {
+ return $this->tablePackageIDs[$table->getName()];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the id of the package to with the given index belongs to. If there is no specific
+ * log entry for the given index, the table log is checked and the relevant package id of
+ * the whole table is returned. If the package of the table is also unknown, `null` is returned.
+ *
+ * @param DatabaseTable $table
+ * @param DatabaseTableIndex $index
+ * @return null|int
+ */
+ protected function getIndexPackageID(DatabaseTable $table, DatabaseTableIndex $index) {
+ if (isset($this->indexPackageIDs[$table->getName()][$index->getName()])) {
+ return $this->indexPackageIDs[$table->getName()][$index->getName()];
+ }
+ else if (isset($this->tablePackageIDs[$table->getName()])) {
+ return $this->tablePackageIDs[$table->getName()];
+ }
+
+ return null;
+ }
+
+ /**
+ * Logs all of the executed changes.
+ */
+ protected function logChanges() {
+ if (!empty($this->droppedTables)) {
+ $sql = "DELETE FROM wcf".WCF_N."_package_installation_sql_log
+ WHERE sqlTable = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ WCF::getDB()->beginTransaction();
+ foreach ($this->droppedTables as $table) {
+ $statement->execute([$table->getName()]);
+ }
+ WCF::getDB()->commitTransaction();
+ }
+
+ if (!empty($this->droppedColumns)) {
+ $sql = "DELETE FROM wcf".WCF_N."_package_installation_sql_log
+ WHERE sqlTable = ?
+ AND sqlColumn = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ WCF::getDB()->beginTransaction();
+ foreach ($this->droppedColumns as $tableName => $columns) {
+ foreach ($columns as $column) {
+ $statement->execute([$tableName, $column->getName()]);
+ }
+ }
+ WCF::getDB()->commitTransaction();
+ }
+
+ if (!empty($this->droppedIndices)) {
+ $sql = "DELETE FROM wcf".WCF_N."_package_installation_sql_log
+ WHERE sqlTable = ?
+ AND sqlIndex = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ WCF::getDB()->beginTransaction();
+ foreach ($this->droppedIndices as $tableName => $indices) {
+ foreach ($indices as $index) {
+ $statement->execute([$tableName, $index->getName()]);
+ }
+ }
+ WCF::getDB()->commitTransaction();
+ }
+
+ $insertionData = [];
+ foreach ($this->addedTables as $table) {
+ $insertionData[] = ['sqlTable' => $table->getName()];
+ }
+
+ foreach ($this->addedColumns as $tableName => $columns) {
+ foreach ($columns as $column) {
+ $insertionData[] = ['sqlTable' => $tableName, 'sqlColumn' => $column->getName()];
+ }
+ }
+
+ foreach ($this->addedIndices as $tableName => $indices) {
+ foreach ($indices as $index) {
+ $insertionData[] = ['sqlTable' => $tableName, 'sqlIndex' => $index->getName()];
+ }
+ }
+
+ if (!empty($insertionData)) {
+ $sql = "INSERT INTO wcf".WCF_N."_package_installation_sql_log
+ (packageID, sqlTable, sqlColumn, sqlIndex)
+ VALUES (?, ?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ WCF::getDB()->beginTransaction();
+ foreach ($insertionData as $data) {
+ $statement->execute([
+ $this->package->packageID,
+ $data['sqlTable'],
+ $data['sqlColumn'] ?? '',
+ $data['sqlIndex'] ?? ''
+ ]);
+ }
+ WCF::getDB()->commitTransaction();
+ }
+ }
+
+ /**
+ * Processes all tables and updates the current table layouts to match the specified layouts.
+ *
+ * @throws \RuntimeException if validation of the required layout changes fails
+ */
+ public function process() {
+ $errors = $this->validate();
+ if (!empty($errors)) {
+ throw new \RuntimeException(WCF::getLanguage()->getDynamicVariable('wcf.acp.package.error.databaseChange', [
+ 'errors' => $errors
+ ]));
+ }
+
+ foreach ($this->tables as $table) {
+ if ($table->willBeDropped()) {
+ if (in_array($table->getName(), $this->existingTableNames)) {
+ $this->dropTable($table);
+ }
+ }
+ else if (!in_array($table->getName(), $this->existingTableNames)) {
+ $this->createTable($table);
+ }
+ else {
+ // calculate difference between tables
+ $existingTable = DatabaseTable::createFromExistingTable($this->dbEditor, $table->getName());
+ $existingColumns = $existingTable->getColumns();
+ $existingForeignKeys = $existingTable->getForeignKeys();
+ $existingIndices = $existingTable->getIndices();
+
+ $addedColumns = $alteredColumns = $droppedColumns = [];
+ foreach ($table->getColumns() as $column) {
+ if (!isset($existingColumns[$column->getName()]) && !$column->willBeDropped()) {
+ $addedColumns[$column->getName()] = $column;
+ }
+ else if (isset($existingColumns[$column->getName()])) {
+ if ($column->willBeDropped()) {
+ $droppedColumns[$column->getName()] = $column;
+ }
+ else if (!empty(array_diff($column->getData(), $existingColumns[$column->getName()]->getData()))) {
+ $alteredColumns[$column->getName()] = $column;
+ }
+ }
+ }
+
+ $this->processColumns($table, $addedColumns, $alteredColumns, $droppedColumns);
+
+ $addedForeignKeys = $droppedForeignKeys = [];
+ foreach ($table->getForeignKeys() as $foreignKey) {
+ $matchingExistingForeignKey = null;
+ foreach ($existingForeignKeys as $existingForeignKey) {
+ if (empty(array_diff($foreignKey->getData(), $existingForeignKey->getData()))) {
+ $matchingExistingForeignKey = $existingForeignKey;
+ break;
+ }
+ }
+
+ if ($foreignKey->willBeDropped()) {
+ if ($matchingExistingForeignKey !== null) {
+ $droppedForeignKeys[$foreignKey->getName()] = $foreignKey;
+ }
+ }
+ else if ($matchingExistingForeignKey === null) {
+ $addedForeignKeys[$foreignKey->getName()] = $foreignKey;
+ }
+ }
+
+ $this->processForeignKeys($table, $addedForeignKeys, $droppedForeignKeys);
+
+ $addedIndices = $droppedIndices = [];
+ foreach ($table->getIndices() as $index) {
+ $matchingExistingIndex = null;
+ foreach ($existingIndices as $existingIndex) {
+ if (empty(array_diff($index->getData(), $existingIndex->getData()))) {
+ $matchingExistingIndex = $existingIndex;
+ break;
+ }
+ }
+
+ if ($index->willBeDropped()) {
+ if ($matchingExistingIndex !== null) {
+ $droppedIndices[$index->getName()] = $index;
+ }
+ }
+ else if ($matchingExistingIndex === null) {
+ $addedIndices[$index->getName()] = $index;
+ }
+ }
+
+ $this->processIndices($table, $addedIndices, $droppedIndices);
+ }
+ }
+
+ $this->logChanges();
+ }
+
+ /**
+ * Adds, alters and drops the given columns.
+ *
+ * @param DatabaseTable $table
+ * @param IDatabaseTableColumn[] $addedColumns
+ * @param IDatabaseTableColumn[] $alteredColumns
+ * @param IDatabaseTableColumn[] $droppedColumns
+ * @throws SplitNodeException
+ */
+ protected function processColumns(DatabaseTable $table, array $addedColumns, array $alteredColumns, array $droppedColumns) {
+ $columnData = [];
+ foreach ($droppedColumns as $droppedColumn) {
+ $columnData[$droppedColumn->getName()] = [
+ 'action' => 'drop'
+ ];
+
+ $this->droppedColumns[$table->getName()][$droppedColumn->getName()] = $droppedColumn;
+ }
+ foreach ($addedColumns as $addedColumn) {
+ $columnData[$addedColumn->getName()] = [
+ 'action' => 'add',
+ 'data' => $addedColumn->getData()
+ ];
+
+ if ($this->tablePackageIDs[$table->getName()] !== $this->package->packageID) {
+ $this->addedColumns[$table->getName()][$addedColumn->getName()] = $addedColumn;
+ }
+ }
+ foreach ($alteredColumns as $alteredColumn) {
+ $columnData[$alteredColumn->getName()] = [
+ 'action' => 'alter',
+ 'data' => $alteredColumn->getData(),
+ 'oldColumnName' => $alteredColumn->getName()
+ ];
+ }
+
+ if (!empty($columnData)) {
+ $this->dbEditor->alterColumns($table->getName(), $columnData);
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Altered columns of table '{$table->getName()}'.");
+ }
+ }
+ }
+
+ /**
+ * Adds and drops the given foreign keys.
+ *
+ * @param DatabaseTable $table
+ * @param DatabaseTableForeignKey[] $addedForeignKeys
+ * @param DatabaseTableForeignKey[] $droppedForeignKeys
+ * @throws SplitNodeException
+ */
+ protected function processForeignKeys(DatabaseTable $table, array $addedForeignKeys, array $droppedForeignKeys) {
+ if (empty($addedForeignKeys) && empty($droppedForeignKeys)) {
+ return;
+ }
+
+ foreach ($addedForeignKeys as $addedForeignKey) {
+ if ($this->tablePackageIDs[$table->getName()] !== $this->package->packageID) {
+ $this->addedIndices[$table->getName()][$addedForeignKey->getName()] = $addedForeignKey;
+ }
+
+ $this->dbEditor->addForeignKey($table->getName(), $addedForeignKey->getName(), $addedForeignKey->getData());
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Added foreign key '{$table->getName()}." . implode(',', $addedForeignKey->getColumns()) . "'");
+ }
+ }
+
+ foreach ($droppedForeignKeys as $droppedForeignKey) {
+ $this->droppedIndices[$table->getName()][$droppedForeignKey->getName()] = $droppedForeignKey;
+
+ $this->dbEditor->dropForeignKey($table->getName(), $droppedForeignKey->getName());
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Dropped foreign key '{$table->getName()}." . implode(',', $droppedForeignKey->getColumns()) . "' ({$droppedForeignKey->getName()})");
+ }
+ }
+ }
+
+ /**
+ * Adds and drops the given indices.
+ *
+ * @param DatabaseTable $table
+ * @param DatabaseTableIndex[] $addedIndices
+ * @param DatabaseTableIndex[] $droppedIndices
+ * @throws SplitNodeException
+ */
+ protected function processIndices(DatabaseTable $table, array $addedIndices, array $droppedIndices) {
+ if (empty($addedIndices) && empty($droppedIndices)) {
+ return;
+ }
+
+ foreach ($addedIndices as $addedIndex) {
+ if ($this->tablePackageIDs[$table->getName()] !== $this->package->packageID) {
+ $this->addedIndices[$table->getName()][$addedIndex->getName()] = $addedIndex;
+ }
+
+ $this->dbEditor->addIndex($table->getName(), $addedIndex->getName(), $addedIndex->getData());
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Added index '{$table->getName()}." . implode(',', $addedIndex->getColumns()) . "'");
+ }
+ }
+
+ foreach ($droppedIndices as $droppedIndex) {
+ $this->droppedIndices[$table->getName()][$droppedIndex->getName()] = $droppedIndex;
+
+ $this->dbEditor->dropIndex($table->getName(), $droppedIndex->getName());
+
+ if ($this->oneChangePerRequest) {
+ $this->logChanges();
+
+ throw new SplitNodeException("Dropped index '{$table->getName()}." . implode(',', $droppedIndex->getColumns()) . "'");
+ }
+ }
+ }
+
+ /**
+ * Checks if the relevant table layout changes can be executed and returns an array with information
+ * on any validation error.
+ *
+ * @return array
+ */
+ public function validate() {
+ $errors = [];
+ foreach ($this->tables as $table) {
+ if ($table->willBeDropped()) {
+ if (in_array($table->getName(), $this->existingTableNames)) {
+ if (!isset($this->tablePackageIDs[$table->getName()])) {
+ $errors[] = [
+ 'tableName' => $table->getName(),
+ 'type' => 'unregisteredTableDrop'
+ ];
+ }
+ else if ($this->tablePackageIDs[$table->getName()] !== $this->package->packageID) {
+ $errors[] = [
+ 'tableName' => $table->getName(),
+ 'type' => 'foreignTableDrop'
+ ];
+ }
+ }
+ }
+ else if (in_array($table->getName(), $this->existingTableNames)) {
+ if (!isset($this->tablePackageIDs[$table->getName()])) {
+ $errors[] = [
+ 'tableName' => $table->getName(),
+ 'type' => 'unregisteredTableChange'
+ ];
+ }
+ else {
+ $existingTable = DatabaseTable::createFromExistingTable($this->dbEditor, $table->getName());
+ $existingColumns = $existingTable->getColumns();
+ $existingIndices = $existingTable->getIndices();
+ $existingForeignKeys = $existingTable->getForeignKeys();
+
+ foreach ($table->getColumns() as $column) {
+ if (isset($existingColumns[$column->getName()])) {
+ $columnPackageID = $this->getColumnPackageID($table, $column);
+ if ($column->willBeDropped()) {
+ if ($columnPackageID !== $this->package->packageID) {
+ $errors[] = [
+ 'columnName' => $column->getName(),
+ 'tableName' => $table->getName(),
+ 'type' => 'foreignColumnDrop'
+ ];
+ }
+ }
+ else if ($columnPackageID !== $this->package->packageID) {
+ $errors[] = [
+ 'columnName' => $column->getName(),
+ 'tableName' => $table->getName(),
+ 'type' => 'foreignColumnChange'
+ ];
+ }
+ }
+ }
+
+ foreach ($table->getIndices() as $index) {
+ foreach ($existingIndices as $existingIndex) {
+ if (empty(array_diff($index->getData(), $existingIndex->getData()))) {
+ if ($index->willBeDropped()) {
+ if ($this->getIndexPackageID($table, $index) !== $this->package->packageID) {
+ $errors[] = [
+ 'columnNames' => implode(',', $existingIndex->getColumns()),
+ 'tableName' => $table->getName(),
+ 'type' => 'foreignIndexDrop'
+ ];
+ }
+ }
+
+ continue 2;
+ }
+ }
+ }
+
+ foreach ($table->getForeignKeys() as $foreignKey) {
+ foreach ($existingForeignKeys as $existingForeignKey) {
+ if (empty(array_diff($foreignKey->getData(), $existingForeignKey->getData()))) {
+ if ($foreignKey->willBeDropped()) {
+ if ($this->getForeignKeyPackageID($table, $foreignKey) !== $this->package->packageID) {
+ $errors[] = [
+ 'columnNames' => implode(',', $existingForeignKey->getColumns()),
+ 'tableName' => $table->getName(),
+ 'type' => 'foreignForeignKeyDrop'
+ ];
+ }
+ }
+
+ continue 2;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $errors;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table;
+
+/**
+ * Provides methods for database components which can be dropped.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table
+ * @since 5.2
+ */
+trait TDroppableDatabaseComponent {
+ /**
+ * is `true` if the component will be dropped
+ * @var bool
+ */
+ protected $drop = false;
+
+ /**
+ * Marks the component to be dropped.
+ *
+ * @return $this
+ */
+ public function drop() {
+ $this->drop = true;
+
+ return $this;
+ }
+
+ /**
+ * Returns `true` if the component will be dropped.
+ *
+ * @return bool
+ */
+ public function willBeDropped() {
+ return $this->drop;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+use wcf\system\database\table\TDroppableDatabaseComponent;
+
+/**
+ * Abstract implementation of a database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+abstract class AbstractDatabaseTableColumn implements IDatabaseTableColumn {
+ use TDroppableDatabaseComponent;
+
+ /**
+ * default value of the database table column
+ * @var mixed
+ */
+ protected $defaultValue;
+
+ /**
+ * name of the database table column
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * is `true` if the values of the column may not be `null`
+ * @var bool
+ */
+ protected $notNull = false;
+
+ /**
+ * type of the database table column
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * @inheritDoc
+ */
+ public function defaultValue($defaultValue) {
+ $this->validateDefaultValue($defaultValue);
+
+ $this->defaultValue = $defaultValue;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getData() {
+ $data = [
+ 'default' => $this->defaultValue,
+ 'notNull' => $this->isNotNull() ? 1 : 0,
+ 'type' => $this->getType()
+ ];
+
+ if ($this instanceof IAutoIncrementDatabaseTableColumn) {
+ $data['autoIncrement'] = $this->isAutoIncremented() ? 1 : 0;
+ }
+
+ if ($this instanceof IDecimalsDatabaseTableColumn && $this->getDecimals() !== null) {
+ $data['decimals'] = $this->getDecimals();
+ }
+
+ if ($this instanceof IEnumDatabaseTableColumn) {
+ $values = array_map(function($value) {
+ return str_replace(["'", '\\'], ["''", '\\\\'], $value);
+ }, $this->getEnumValues());
+
+ $data['values'] = "'" . implode("','", $values) . "'";
+ }
+
+ if ($this instanceof ILengthDatabaseTableColumn) {
+ $data['length'] = $this->getLength();
+ }
+
+ if ($this instanceof IUnsignedDatabaseTableColumn) {
+ $data['unsigned'] = $this->isUnsigned();
+ }
+
+ return $data;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getDefaultValue() {
+ return $this->defaultValue;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getName() {
+ if ($this->name === null) {
+ throw new \BadMethodCallException("Name of the database table column has not been set yet");
+ }
+
+ return $this->name;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getType() {
+ if ($this->type === null) {
+ throw new \BadMethodCallException("Type of the database table column " . get_class($this) . " has not been set yet");
+ }
+
+ return $this->type;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function isNotNull() {
+ return $this->notNull;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function name($name) {
+ if ($this->name !== null) {
+ throw new \BadMethodCallException("Name of the database table column has already been set.");
+ }
+
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notNull($notNull = true) {
+ $this->notNull = $notNull;
+
+ return $this;
+ }
+
+ /**
+ * Checks if the given default value is valid.
+ *
+ * @param mixed $defaultValue validated default value
+ * @throws \InvalidArgumentException if given default value is invalid
+ */
+ protected function validateDefaultValue($defaultValue) {
+ // does nothing
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ return (new static())->name($name);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function createFromData($name, array $data) {
+ $column = static::create($name)
+ ->defaultValue($data['default'])
+ ->notNull($data['notNull']);
+
+ if ($column instanceof IAutoIncrementDatabaseTableColumn) {
+ $column->autoIncrement($data['autoIncrement'] ?: null);
+ }
+
+ if ($column instanceof IDecimalsDatabaseTableColumn) {
+ $column->decimals($data['decimals'] ?: null);
+ }
+
+ if ($column instanceof IEnumDatabaseTableColumn) {
+ $values = explode(',', $data['enumValues'] ?? []);
+
+ $values = array_map(function($value) {
+ // trim one leading and one trailing `'`
+ $value = substr($value, 1, -1);
+
+ return str_replace(['\\\\', "''"], ['\\', "'"], $value);
+ }, $values);
+
+ $column->enumValues($values);
+ }
+
+ if ($column instanceof ILengthDatabaseTableColumn) {
+ $column->length($data['length'] ?: null);
+ }
+
+ if ($column instanceof IUnsignedDatabaseTableColumn) {
+ $column->unsigned($data['unsigned'] ?? false);
+ }
+
+ return $column;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Abstract implementation of a decimal (data) type for database table columns.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+abstract class AbstractDecimalDatabaseTableColumn extends AbstractDatabaseTableColumn implements IDecimalsDatabaseTableColumn, IUnsignedDatabaseTableColumn {
+ use TDecimalsDatabaseTableColumn;
+ use TUnsignedDatabaseTableColumn;
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Abstract implementation of an integer database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+abstract class AbstractIntDatabaseTableColumn extends AbstractDatabaseTableColumn implements IAutoIncrementDatabaseTableColumn, ILengthDatabaseTableColumn, IUnsignedDatabaseTableColumn {
+ use TAutoIncrementDatabaseTableColumn;
+ use TLengthDatabaseTableColumn;
+ use TUnsignedDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ public function getMinimumLength() {
+ return 1;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `bigint` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class BigintDatabaseTableColumn extends AbstractIntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'bigint';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 20;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumUnsignedLength() {
+ return 19;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `binary` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class BinaryDatabaseTableColumn extends AbstractDatabaseTableColumn implements ILengthDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'binary';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 255;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMinimumLength() {
+ return 0;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `blob` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class BlobDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'blob';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `char` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class CharDatabaseTableColumn extends AbstractDatabaseTableColumn implements ILengthDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'char';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 255;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMinimumLength() {
+ return 0;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `date` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DateDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'date';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `datetime` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DatetimeDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'datetime';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `decimal` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DecimalDatabaseTableColumn extends AbstractDecimalDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'decimal';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumDecimals() {
+ return 30;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 65;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMinimumLength() {
+ return 1;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `tinyint` database table column with length `1`, default value `0` and whose values
+ * cannot be `null`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DefaultFalseBooleanDatabaseTableColumn extends TinyintDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ /** @var TinyintDatabaseTableColumn $column */
+ $column = parent::create($name);
+
+ return $column
+ ->length(1)
+ ->notNull()
+ ->defaultValue(0);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `tinyint` database table column with length `1`, default value `1` and whose values
+ * cannot be `null`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DefaultTrueBooleanDatabaseTableColumn extends TinyintDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ /** @var TinyintDatabaseTableColumn $column */
+ $column = parent::create($name);
+
+ return $column
+ ->length(1)
+ ->notNull()
+ ->defaultValue(1);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `double` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class DoubleDatabaseTableColumn extends AbstractDecimalDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'double';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumDecimals() {
+ return 30;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 255;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `enum` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class EnumDatabaseTableColumn extends AbstractDatabaseTableColumn implements IEnumDatabaseTableColumn {
+ use TEnumDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'enum';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `float` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class FloatDatabaseTableColumn extends AbstractDecimalDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'float';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumDecimals() {
+ return 30;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 255;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Every database table column whose values can be auto-incremented must implement this interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface IAutoIncrementDatabaseTableColumn {
+ /**
+ * Sets if the values of the database table column are auto-increment and returns this column.
+ *
+ * @param bool $autoIncrement
+ * @return $this
+ */
+ public function autoIncrement($autoIncrement = true);
+
+ /**
+ * Returns `true` if the values of the database table column are auto-increment.
+ *
+ * @return bool
+ */
+ public function isAutoIncremented();
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a column of a database table.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface IDatabaseTableColumn {
+ /**
+ * Sets the default value of the column and returns the column.
+ *
+ * @param mixed $defaultValue
+ * @return $this
+ */
+ public function defaultValue($defaultValue);
+
+ /**
+ * Marks the column to be dropped and returns the column.
+ *
+ * @return $this
+ */
+ public function drop();
+
+ /**
+ * Returns the data used by `DatabaseEditor` to add the column to a table.
+ *
+ * @return array
+ */
+ public function getData();
+
+ /**
+ * Returns the default value of the column.
+ *
+ * @return $this
+ */
+ public function getDefaultValue();
+
+ /**
+ * Returns the name of the column.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Returns the type of the column.
+ *
+ * @return string
+ */
+ public function getType();
+
+ /**
+ * Returns `true` if the values of the column cannot be `null`.
+ *
+ * @return bool
+ */
+ public function isNotNull();
+
+ /**
+ * Sets the name of the column and returns the column.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function name($name);
+
+ /**
+ * Sets if the values of the column cannot be `null`.
+ *
+ * @param bool $notNull
+ * @return $this
+ */
+ public function notNull($notNull = true);
+
+ /**
+ * Returns `true` if the column will be dropped.
+ *
+ * @return bool
+ */
+ public function willBeDropped();
+
+ /**
+ * Returns a `DatabaseTableColumn` object with the given name.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public static function create($name);
+
+ /**
+ * Returns a `DatabaseTableColumn` object with the given name and data.
+ *
+ * @param string $name
+ * @param array $data data returned by `DatabaseEditor::getColumns()`
+ * @return $this
+ */
+ public static function createFromData($name, array $data);
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Every database table column whose values supports decimals must implement this interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface IDecimalsDatabaseTableColumn extends ILengthDatabaseTableColumn {
+ /**
+ * Sets the number of decimals the database table column supports or unsets the previously
+ * set value if `null` is passed and returns this column.
+ *
+ * @param null|int $decimals
+ * @return $this
+ */
+ public function decimals($decimals);
+
+ /**
+ * Returns the number of decimals the database table column supports or `null` if the number
+ * of decimals has not be specified.
+ *
+ * @return null|int
+ */
+ public function getDecimals();
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Every database table column that supports specifying a predetermined set of valid values must
+ * implement this interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface IEnumDatabaseTableColumn extends IDatabaseTableColumn {
+ /**
+ * Sets the predetermined set of valid values for the database table column and returns this
+ * column.
+ *
+ * @param array $values
+ * @return $this
+ */
+ public function enumValues(array $values);
+
+ /**
+ * Returns the predetermined set of valid values for the database table column.
+ *
+ * @return array
+ */
+ public function getEnumValues();
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Every database table column that supports specifying a (maximum) value length must implement this
+ * interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface ILengthDatabaseTableColumn extends IDatabaseTableColumn {
+ /**
+ * Returns the (maximum) length of the column's values or `null` if no length has been set.
+ *
+ * @return null|int
+ */
+ public function getLength();
+
+ /**
+ * Sets the (maximum) length of the column's values.
+ *
+ * @param null|int $length (maximum) column value length or `null` to unset previously set value
+ * @return $this this column
+ * @throws \InvalidArgumentException if given length is invalid
+ */
+ public function length($length);
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Every database table column whose values can be unsigned must implement this interface.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+interface IUnsignedDatabaseTableColumn extends IDatabaseTableColumn {
+ /**
+ * Returns `true` if the values of the database table column are unsigned.
+ *
+ * @return bool
+ */
+ public function isUnsigned();
+
+ /**
+ * Sets if the values of the database table column are unsigned and returns this column.
+ *
+ * @param bool $unsigned
+ * @return $this
+ */
+ public function unsigned($unsigned = true);
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `int` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class IntDatabaseTableColumn extends AbstractIntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'int';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 11;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `longblob` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class LongblobDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'longblob';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `longtext` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class LongtextDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'longtext';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `mediumblob` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class MediumblobDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'mediumblob';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `mediumint` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class MediumintDatabaseTableColumn extends AbstractIntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'mediumint';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 8;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumUnsignedLength() {
+ return 7;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `mediumtext` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class MediumtextDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'mediumtext';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `int` database table column with length `10` and whose values cannot be null.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class NotNullInt10DatabaseTableColumn extends IntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ return parent::create($name)
+ ->notNull()
+ ->length(10);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `varchar` database table column with length `191` and whose values cannot be null.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class NotNullVarchar191DatabaseTableColumn extends VarcharDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ return parent::create($name)
+ ->notNull()
+ ->length(191);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `varchar` database table column with length `255` and whose values cannot be null.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class NotNullVarchar255DatabaseTableColumn extends VarcharDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($name) {
+ return parent::create($name)
+ ->notNull()
+ ->length(255);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `int` database table column with length `10`, whose values cannot be null, and whose
+ * values are auto-incremented.
+ *
+ * This class should be used for the id column of DBO tables.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class ObjectIdDatabaseTableColumn extends NotNullInt10DatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ public static function create($columnName) {
+ return parent::create($columnName)
+ ->autoIncrement();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `set` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class SetDatabaseTableColumn extends AbstractDatabaseTableColumn implements IEnumDatabaseTableColumn {
+ use TEnumDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'set';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `smallint` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class SmallintDatabaseTableColumn extends AbstractIntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'smallint';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 5;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Provides default implementation of the methods of `IAutoIncrementDatabaseTableColumn`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+trait TAutoIncrementDatabaseTableColumn {
+ /**
+ * is `true` if the values of the database table column are auto-increment
+ * @var bool
+ */
+ protected $autoIncrement = false;
+
+ /**
+ * Sets if the values of the database table column are auto-increment and returns this column.
+ *
+ * @param bool $autoIncrement
+ * @return $this
+ */
+ public function autoIncrement($autoIncrement = true) {
+ $this->autoIncrement = $autoIncrement;
+
+ return $this;
+ }
+
+ /**
+ * Returns `true` if the values of the database table column are auto-increment.
+ *
+ * @return bool
+ */
+ public function isAutoIncremented() {
+ return $this->autoIncrement;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Provides default implementation of the methods of `IDecimalsDatabaseTableColumn`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+trait TDecimalsDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * number of decimals the database table column supports
+ * @var null|int
+ */
+ protected $decimals;
+
+ /**
+ * Sets the number of decimals the database table column supports or unsets the previously
+ * set value if `null` is passed and returns this column.
+ *
+ * @param null|int $decimals
+ * @return $this
+ */
+ public function decimals($decimals) {
+ if ($this->getMaximumDecimals() !== null && $decimals > $this->getMaximumDecimals()) {
+ throw new \InvalidArgumentException("Given number of decimals is greater than the maximum number '{$this->getMaximumDecimals()}'.");
+ }
+
+ $this->decimals = $decimals;
+
+ return $this;
+ }
+
+ /**
+ * Returns the number of decimals the database table column supports or `null` if the number
+ * of decimals has not be specified.
+ *
+ * @return null|int
+ */
+ public function getDecimals() {
+ return $this->decimals;
+ }
+
+ /**
+ * Returns the maxium number of decimals supported by this column or `null` if there is no such
+ * maximum.
+ *
+ * @return null|int
+ */
+ public function getMaximumDecimals() {
+ return null;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Provides default implementation of the methods of `IEnumDatabaseTableColumn`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+trait TEnumDatabaseTableColumn {
+ /**
+ * predetermined set of valid values for the database table column
+ * @var array
+ */
+ protected $enumValues = [];
+
+ /**
+ * Sets the predetermined set of valid values for the database table column and returns this
+ * column.
+ *
+ * @param array $values
+ * @return $this
+ */
+ public function enumValues(array $values) {
+ $this->enumValues = $values;
+
+ return $this;
+ }
+
+ /**
+ * Returns the predetermined set of valid values for the database table column.
+ *
+ * @return array
+ */
+ public function getEnumValues() {
+ return $this->enumValues;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Provides default implementation of the methods of `ILengthDatabaseTableColumn`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+trait TLengthDatabaseTableColumn {
+ /**
+ * (maximum) length of the column's values
+ * @var null|int
+ */
+ protected $length;
+
+ /**
+ * Returns the maxium length value supported by this column or `null` if there is no such
+ * maximum.
+ *
+ * @return null|int
+ */
+ public function getMaximumLength() {
+ if ($this instanceof IUnsignedDatabaseTableColumn && $this->getMaximumUnsignedLength() !== null) {
+ return $this->getMaximumUnsignedLength();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the maximum length value supported by the values of the column are unsigned values
+ * or `null` if there is no such maximum.
+ *
+ * @return null|int
+ */
+ public function getMaximumUnsignedLength() {
+ return null;
+ }
+
+ /**
+ * Returns the minimum length value supported by this column or `null` if there is no such
+ * minimum.
+ *
+ * @return null|int
+ */
+ public function getMinimumLength() {
+ return 0;
+ }
+
+ /**
+ * Returns the (maximum) length of the column's values or `null` if no length has been set.
+ *
+ * @return null|int
+ */
+ public function getLength() {
+ return $this->length;
+ }
+
+ /**
+ * Sets the (maximum) length of the column's values.
+ *
+ * @param null|int $length (maximum) column value length or `null` to unset previously set value
+ * @return $this this column
+ * @throws \InvalidArgumentException if given length is invalid
+ */
+ public function length($length) {
+ if ($length !== null) {
+ $this->validateLength($length);
+ }
+
+ $this->length = $length;
+
+ return $this;
+ }
+
+ /**
+ * Validates the given length.
+ *
+ * @param int $length
+ * @throws \InvalidArgumentException if given length is invalid
+ */
+ protected function validateLength($length) {
+ if ($this->getMinimumLength() !== null && $length < $this->getMinimumLength()) {
+ throw new \InvalidArgumentException("Given length is smaller than the minimum length '{$this->getMinimumLength()}'.");
+ }
+ if ($this->getMaximumLength() !== null && $length > $this->getMaximumLength()) {
+ throw new \InvalidArgumentException("Given length is greater than the maximum length '{$this->getMaximumLength()}'.");
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Provides default implementation of the methods of `IUnsignedDatabaseTableColumn`.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+trait TUnsignedDatabaseTableColumn {
+ /**
+ * `true` if the values of the database table column are unsigned
+ * @var bool
+ */
+ protected $unsigned = false;
+
+ /**
+ * Returns `true` if the values of the database table column are unsigned.
+ *
+ * @return bool
+ */
+ public function isUnsigned() {
+ return $this->unsigned;
+ }
+
+ /**
+ * Sets if the values of the database table column are unsigned and returns this column.
+ *
+ * @param bool $unsigned
+ * @return $this
+ */
+ public function unsigned($unsigned = true) {
+ $this->unsigned = $unsigned;
+
+ return $this;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `text` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class TextDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'text';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `time` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class TimeDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'time';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `tinyblob` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class TinyblobDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'tinyblob';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `tinyint` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class TinyintDatabaseTableColumn extends AbstractIntDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'tinyint';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 3;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `tinytext` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class TinytextDatabaseTableColumn extends AbstractDatabaseTableColumn {
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'tinytext';
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `varbinary` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class VarbinaryDatabaseTableColumn extends AbstractDatabaseTableColumn implements ILengthDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'varbinary';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 65535;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `varchar` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class VarcharDatabaseTableColumn extends AbstractDatabaseTableColumn implements ILengthDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'varchar';
+
+ /**
+ * @inheritDoc
+ */
+ public function getMaximumLength() {
+ return 65535;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMinimumLength() {
+ return 0;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\column;
+
+/**
+ * Represents a `year` database table column.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Column
+ * @since 5.2
+ */
+class YearDatabaseTableColumn extends AbstractDatabaseTableColumn implements ILengthDatabaseTableColumn {
+ use TLengthDatabaseTableColumn;
+
+ /**
+ * @inheritDoc
+ */
+ protected $type = 'year';
+
+ /**
+ * @inheritDoc
+ */
+ protected function validateLength($length) {
+ if ($length !== 2 && $length !== 4) {
+ throw new \InvalidArgumentException("Only '2' and '4' are valid lengths for year columns");
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\index;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\database\table\TDroppableDatabaseComponent;
+
+/**
+ * Represents a foreign key of a database table.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Index
+ * @since 5.2
+ */
+class DatabaseTableForeignKey {
+ use TDroppableDatabaseComponent;
+
+ /**
+ * columns affected by the foreign key
+ * @var string[]
+ */
+ protected $columns;
+
+ /**
+ * name of the foreign key
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * action executed in referenced table if row is deleted
+ * @var null|string
+ */
+ protected $onDelete;
+
+ /**
+ * action executed in referenced table if row is updated
+ * @var null|string
+ */
+ protected $onUpdate;
+
+ /**
+ * relevant columns in referenced table
+ * @var string[]
+ */
+ protected $referencedColumns;
+
+ /**
+ * name of referenced table
+ * @var string
+ */
+ protected $referencedTable;
+
+ /**
+ * valid on delete/update actions
+ * @var string[]
+ */
+ const VALID_ACTIONS = [
+ 'CASCADE',
+ 'NO ACTION',
+ 'SET NULL'
+ ];
+
+ /**
+ * Creates a new `DatabaseTableForeignKey` object.
+ *
+ * @param string $name column name
+ */
+ protected function __construct($name) {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets the columns affected by the foreign key and returns the foreign key.
+ *
+ * @param string[] $columns columns affected by foreign key
+ * @return $this this foreign key
+ */
+ public function columns(array $columns) {
+ $this->columns = array_values($columns);
+
+ return $this;
+ }
+
+ /**
+ * Returns the name of the foreign key.
+ *
+ * If the key belongs to a database table layout not created from an existing database table,
+ * the name might be empty.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns the columns affected by the foreign key
+ *
+ * @return string[] columns affected by foreign key
+ * @throws \BadMethodCallException if not columns have been set
+ */
+ public function getColumns() {
+ if ($this->columns === null) {
+ throw new \BadMethodCallException("Before getting the columns, they must be set for foreign key '{$this->getName()}'.");
+ }
+
+ return $this->columns;
+ }
+
+ /**
+ * Returns the data used by `DatabaseEditor` to add the foreign key to a table.
+ *
+ * @return array
+ */
+ public function getData() {
+ return [
+ 'columns' => implode(',', $this->getColumns()),
+ 'ON DELETE' => $this->getOnDelete(),
+ 'ON UPDATE' => $this->getOnUpdate(),
+ 'referencedColumns' => implode(',', $this->getReferencedColumns()),
+ 'referencedTable' => $this->getReferencedTable()
+ ];
+ }
+
+ /**
+ * Returns the action executed in referenced table if row is deleted or `null` if no such
+ * action has been set.
+ *
+ * @return null|string
+ */
+ public function getOnDelete() {
+ return $this->onDelete;
+ }
+
+ /**
+ * Returns the action executed in referenced table if row is updated or `null` if no such
+ * action has been set.
+ *
+ * @return null|string
+ */
+ public function getOnUpdate() {
+ return $this->onUpdate;
+ }
+
+ /**
+ * Returns the relevant columns in referenced table.
+ *
+ * @return string[]
+ * @throws \BadMethodCallException if referenced columns have not been set
+ */
+ public function getReferencedColumns() {
+ if ($this->referencedColumns === null) {
+ throw new \BadMethodCallException("Before getting the referenced columns, they must be set for foreign key '{$this->getName()}'.");
+ }
+
+ return $this->referencedColumns;
+ }
+
+ /**
+ * Returns the name of the referenced table.
+ *
+ * @return string
+ * @throws \BadMethodCallException if referenced table has not been set
+ */
+ public function getReferencedTable() {
+ if ($this->referencedTable === null) {
+ throw new \BadMethodCallException("Before getting the referenced table, it must be set for foreign key '{$this->getName()}'.");
+ }
+
+ return $this->referencedTable;
+ }
+
+ /**
+ * Sets the name of the foreign key.
+ *
+ * @param string $name index name
+ * @return $this this index
+ */
+ public function name($name) {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Sets the action executed in referenced table if row is deleted and returns the foreign
+ * key.
+ *
+ * @param string $onDelete action executed in referenced table if row is deleted
+ * @return $this this foreign key
+ * @throws \InvalidArgumentException if given action is invalid
+ */
+ public function onDelete($onDelete) {
+ if ($onDelete !== null && !in_array($onDelete, static::VALID_ACTIONS)) {
+ throw new \InvalidArgumentException("Unknown on delete action '{$onDelete}'.");
+ }
+
+ $this->onDelete = $onDelete;
+
+ return $this;
+ }
+
+ /**
+ * Sets the action executed in referenced table if row is updated and returns the foreign
+ * key.
+ *
+ * @param string $onUpdate action executed in referenced table if row is updated
+ * @return $this this foreign key
+ * @throws \InvalidArgumentException if given action is invalid
+ */
+ public function onUpdate($onUpdate) {
+ if ($onUpdate !== null && !in_array($onUpdate, static::VALID_ACTIONS)) {
+ throw new \InvalidArgumentException("Unknown on update action '{$onUpdate}'.");
+ }
+
+ $this->onUpdate = $onUpdate;
+
+ return $this;
+ }
+
+ /**
+ * Sets the relevant columns of the referenced table and returns the foreign key.
+ *
+ * @param string[] $referencedColumns columns of referenced table
+ * @return $this this foreign key
+ */
+ public function referencedColumns(array $referencedColumns) {
+ $this->referencedColumns = $referencedColumns;
+
+ return $this;
+ }
+
+ /**
+ * Sets the name of the referenced table and returns the foreign key.
+ *
+ * @param string $referencedTable name of referenced table
+ * @return $this this foreign key
+ */
+ public function referencedTable($referencedTable) {
+ $this->referencedTable = ApplicationHandler::insertRealDatabaseTableNames($referencedTable);
+
+ return $this;
+ }
+
+ /**
+ * Returns a `DatabaseTableForeignKey` object with the given name.
+ *
+ * @param string $name
+ * @return static
+ */
+ public static function create($name = '') {
+ return new static($name);
+ }
+
+ /**
+ * Returns a `DatabaseTableForeignKey` object with the given name and data.
+ *
+ * @param string $name
+ * @param array $data data returned by `DatabaseEditor::getForeignKeys()`
+ * @return static
+ */
+ public static function createFromData($name, $data) {
+ return static::create($name)
+ ->columns($data['columns'])
+ ->onDelete($data['ON DELETE'])
+ ->onUpdate($data['ON UPDATE'])
+ ->referencedColumns($data['referencedColumns'])
+ ->referencedTable($data['referencedTable']);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\index;
+use wcf\system\database\table\TDroppableDatabaseComponent;
+
+/**
+ * Represents an index of a database table.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Index
+ * @since 5.2
+ */
+class DatabaseTableIndex {
+ use TDroppableDatabaseComponent;
+
+ /**
+ * indexed columns
+ * @var string[]
+ */
+ protected $columns;
+
+ /**
+ * name of index
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * type of index (see `*_TYPE` constants)
+ * @var null|string
+ */
+ protected $type;
+
+ const DEFAULT_TYPE = null;
+ const PRIMARY_TYPE = 'PRIMARY';
+ const UNIQUE_TYPE = 'UNIQUE';
+ const FULLTEXT_TYPE = 'FULLTEXT';
+
+ /**
+ * Creates a new `DatabaseTableIndex` object.
+ *
+ * @param string $name column name
+ */
+ protected function __construct($name) {
+ $this->name = $name;
+ }
+
+ /**
+ * Sets the indexed columns and returns the index.
+ *
+ * @param string[] $columns indexed columns
+ * @return $this this index
+ */
+ public function columns($columns) {
+ $this->columns = array_values($columns);
+
+ return $this;
+ }
+
+ /**
+ * Returns the index columns.
+ *
+ * @return string[]
+ */
+ public function getColumns() {
+ return $this->columns;
+ }
+
+ /**
+ * Returns the data used by `DatabaseEditor` to add the index to a table.
+ *
+ * @return array
+ */
+ public function getData() {
+ return [
+ 'columns' => implode(',', $this->columns),
+ 'type' => $this->type
+ ];
+ }
+
+ /**
+ * Returns the name of the index.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns the type of the index (see `*_TYPE` constants).
+ *
+ * @return null|string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Sets the name of the index.
+ *
+ * @param string $name index name
+ * @return $this this index
+ */
+ public function name($name) {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Sets the type of the index and returns the index
+ *
+ * @param null|string $type index type
+ * @return $this this index
+ * @throws \InvalidArgumentException if given type is invalid
+ */
+ public function type($type) {
+ if ($type !== static::DEFAULT_TYPE && $type !== static::PRIMARY_TYPE && $type !== static::UNIQUE_TYPE && $type !== static::FULLTEXT_TYPE) {
+ throw new \InvalidArgumentException("Unknown index type '{$type}'.");
+ }
+
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * Returns a `DatabaseTableIndex` object with the given name.
+ *
+ * @param string $name
+ * @return static
+ */
+ public static function create($name = '') {
+ return new static($name);
+ }
+
+ /**
+ * Returns a `DatabaseTableIndex` object with the given name and data.
+ *
+ * @param string $name
+ * @param array $data data returned by `DatabaseEditor::getIndexInformation()`
+ * @return static
+ */
+ public static function createFromData($name, array $data) {
+ return static::create($name)
+ ->type($data['type'])
+ ->columns($data['columns']);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\database\table\index;
+
+/**
+ * Represents a primary index of a database table.
+ *
+ * This class just provides a shorter factory method that automatically sets the name and type of
+ * the primary index.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Database\Table\Index
+ * @since 5.2
+ */
+class DatabaseTablePrimaryIndex extends DatabaseTableIndex {
+ /**
+ * Returns a `PrimaryDatabaseTableIndex` object with `PRIMARY` as name and primary as type.
+ *
+ * @return $this
+ */
+ public static function create() {
+ return parent::create('PRIMARY')
+ ->type(static::PRIMARY_TYPE);
+ }
+}
<item name="wcf.acp.package.search.status.refreshDatabase"><![CDATA[Die Paketlisten werden aktualisiert …]]></item>
<item name="wcf.acp.package.search.result.thirdParty"><![CDATA[{if $count === 0}Keine{elseif $count === 1}Ein{else}{#$count}{/if} Treffer in Paketquellen von Drittanbietern]]></item>
<item name="wcf.acp.package.search.result.trusted"><![CDATA[{if $count === 0}Keine{elseif $count === 1}Ein{else}{#$count}{/if} Treffer in offizielle Paketquellen]]></item>
+ <item name="wcf.acp.package.error.databaseChange"><![CDATA[Das Datenbanklayout konnte aufgrund folgender Fehler nicht aktualisiert werden:\r
+{implode from=$errors item=error glue=' '}\r
+ {if $error[type] === 'unregisteredTableDrop'}\r
+ Die unbekannte Tabelle {$error[tableName]} kann nicht gelöscht werden.\r
+ {else if $error[type] === 'foreignTableDrop'}\r
+ Die Tabelle {$error[tableName]} gehört zu einem anderen Paket und kann deshalb nicht gelöscht werden.\r
+ {else if $error[type] === 'unregisteredTableChange'}\r
+ Die unbekannte Tabelle {$error[tableName]} kann nicht verändert werden.\r
+ {else if $error[type] === 'foreignColumnDrop'}\r
+ Die Spalte {$error[tableName]}.{$error[columnName]} gehört zu einem anderen Paket und kann deshalb nicht gelöscht werden.\r
+ {else if $error[type] === 'foreignColumnChange'}\r
+ Die Spalte {$error[tableName]}.{$error[columnName]} gehört zu einem anderen Paket und kann deshalb nicht gelöscht werden.\r
+ {else if $error[type] === 'foreignIndexDrop'}\r
+ Der Index {$error[tableName]} ({$error[columnNames]}) gehört zu einem anderen Paket und kann deshalb nicht gelöscht werden.\r
+ {else if $error[type] === 'foreignForeignKeyDrop'}\r
+ Der Fremdschlüssel {$error[tableName]} ({$error[columnNames]}) gehört zu einem anderen Paket und kann deshalb nicht gelöscht werden.\r
+ {else}\r
+ Unbekannter Fehler.\r
+ {/if}\r
+{/implode}]]></item>
</category>
<category name="wcf.acp.page">
<item name="wcf.acp.page.add"><![CDATA[Seite hinzufügen]]></item>
<item name="wcf.acp.package.search.status.refreshDatabase"><![CDATA[Retrieving the package lists…]]></item>
<item name="wcf.acp.package.search.result.thirdParty"><![CDATA[{if $count === 0}No matches{elseif $count === 1}One match{else}{#$count} matches{/if} in package sources maintained by third parties]]></item>
<item name="wcf.acp.package.search.result.trusted"><![CDATA[{if $count === 0}No matches{elseif $count === 1}One match{else}{#$count} matches{/if} in official package sources]]></item>
+ <item name="wcf.acp.package.error.databaseChange"><![CDATA[The database layout could not be changed because of the following errors:\r
+{implode from=$errors item=error glue=' '}\r
+ {if $error[type] === 'unregisteredTableDrop'}\r
+ The unknown table {$error[tableName]} cannot be dropped.\r
+ {else if $error[type] === 'foreignTableDrop'}\r
+ The table {$error[tableName]} belongs to a different package and thus cannot be dropped.\r
+ {else if $error[type] === 'unregisteredTableChange'}\r
+ The unknown table {$error[tableName]} cannot be altered.\r
+ {else if $error[type] === 'foreignColumnDrop'}\r
+ The column {$error[tableName]}.{$error[columnName]} belongs to a different package and thus cannot be dropped.\r
+ {else if $error[type] === 'foreignColumnChange'}\r
+ The column {$error[tableName]}.{$error[columnName]} belongs to a different package and thus cannot be dropped.\r
+ {else if $error[type] === 'foreignIndexDrop'}\r
+ The index {$error[tableName]} ({$error[columnNames]}) belongs to a different package and thus cannot be dropped.\r
+ {else if $error[type] === 'foreignForeignKeyDrop'}\r
+ The foreign key {$error[tableName]} ({$error[columnNames]}) belongs to a different package and thus cannot be dropped.\r
+ {else}\r
+ Unknown error.\r
+ {/if}\r
+{/implode}]]></item>
</category>
<category name="wcf.acp.paidSubscription">
<item name="wcf.acp.paidSubscription.list"><![CDATA[Paid Subscriptions]]></item>