Skip to content

Commit

Permalink
Add some migration logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jdarwood007 committed Mar 2, 2024
1 parent fd97a09 commit c6e4966
Show file tree
Hide file tree
Showing 12 changed files with 771 additions and 29 deletions.
77 changes: 77 additions & 0 deletions Sources/Maintenance/Database/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

namespace SMF\Maintenance\Database;

use SMF\Config;
use SMF\Db\DatabaseApi as Db;
use SMF\Maintenance\DatabaseInterface;

Expand Down Expand Up @@ -170,6 +171,82 @@ public function setSqlMode(string $mode = 'default'): bool

return true;
}

/**
* {@inheritDoc}
*/
public function processError(string $error_msg, string $query): mixed
{
$mysqli_errno = mysqli_errno(Db::$db_connection);

$error_query = in_array(substr(trim($query), 0, 11), ['INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR', 'INSERT IGNO']);

// Error numbers:
// 1016: Can't open file '....MYI'
// 1050: Table already exists.
// 1054: Unknown column name.
// 1060: Duplicate column name.
// 1061: Duplicate key name.
// 1062: Duplicate entry for unique key.
// 1068: Multiple primary keys.
// 1072: Key column '%s' doesn't exist in table.
// 1091: Can't drop key, doesn't exist.
// 1146: Table doesn't exist.
// 2013: Lost connection to server during query.

if ($mysqli_errno == 1016) {
if (preg_match('~\'([^\.\']+)~', $error_msg, $match) != 0 && !empty($match[1])) {
mysqli_query(Db::$db_connection, 'REPAIR TABLE `' . $match[1] . '`');
$result = mysqli_query(Db::$db_connection, $query);

if ($result !== false) {
return $result;
}
}
} elseif ($mysqli_errno == 2013) {
Db::$db_connection = mysqli_connect(Config::$db_server, Config::$db_user, Config::$db_passwd);
mysqli_select_db(Db::$db_connection, Config::$db_name);

if (Db::$db_connection) {
$result = mysqli_query(Db::$db_connection, $query);

if ($result !== false) {
return $result;
}
}
}
// Duplicate column name... should be okay ;).
elseif (in_array($mysqli_errno, [1060, 1061, 1068, 1091])) {
return false;
}
// Duplicate insert... make sure it's the proper type of query ;).
elseif (in_array($mysqli_errno, [1054, 1062, 1146]) && $error_query) {
return false;
}
// Creating an index on a non-existent column.
elseif ($mysqli_errno == 1072) {
return false;
} elseif ($mysqli_errno == 1050 && substr(trim($query), 0, 12) == 'RENAME TABLE') {
return false;
}
// Testing for legacy tables or columns? Needed for 1.0 & 1.1 scripts.
elseif (in_array($mysqli_errno, [1054, 1146]) && in_array(substr(trim($query), 0, 7), ['SELECT ', 'SHOW CO'])) {
return false;
}

// If a table already exists don't go potty.
if (in_array(substr(trim($query), 0, 8), ['CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U'])) {
if (strpos($error_msg, 'exist') !== false) {
return false;
}
} elseif (strpos(trim($query), 'INSERT ') !== false) {
if (strpos($error_msg, 'duplicate') !== false) {
return false;
}
}

return true;
}
}

?>
19 changes: 19 additions & 0 deletions Sources/Maintenance/Database/PostgreSQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,25 @@ public function setSqlMode(string $mode = 'default'): bool
{
return true;
}

/**
* {@inheritDoc}
*/
public function processError(string $error_msg, string $query): mixed
{
if (in_array(substr(trim($query), 0, 8), ['CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U'])) {
if (strpos($error_msg, 'exist') !== false) {
return false;
}
} elseif (strpos(trim($query), 'INSERT ') !== false) {
if (strpos($error_msg, 'duplicate') !== false) {
return false;
}
}

return true;
}

}

?>
12 changes: 12 additions & 0 deletions Sources/Maintenance/DatabaseInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,18 @@ public function utf8Configured(): bool;
* @return bool
*/
public function setSqlMode(string $mode = 'default'): bool;

/**
* When an error occurs with a query ran through a wrapper, we send errors here.
*
* @param string $error_msg as returend by the database interfaces call.
* @param string $query Query we ran
* @return mixed
* False if we should not do anything,
* True if we should stop for error.
* Result from a query can also be returned, if we are able to correct the query.
*/
public function processError(string $error_msg, string $query): mixed;
}

?>
209 changes: 209 additions & 0 deletions Sources/Maintenance/Migration/v2_1/Migration0201.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?php

/**
* Simple Machines Forum (SMF)
*
* @package SMF
* @author Simple Machines https://www.simplemachines.org
* @copyright 2024 Simple Machines and individual contributors
* @license https://www.simplemachines.org/about/smf/license.php BSD
*
* @version 3.0 Alpha 1
*/

declare(strict_types=1);

namespace SMF\Maintenance\Migration\v2_1;

use SMF\Config;
use SMF\Maintenance;
use SMF\Maintenance\Migration;

class Migration0001 extends Migration
{
/**
* {@inheritDoc}
*/
public string $name = 'Fixing sequences (PostgreSQL)';

private array $sequences = [
'admin_info_files_seq' => [
'table' => 'admin_info_files',
'field' => 'id_file',
],
'attachments_seq' => [
'table' => 'attachments',
'field' => 'id_attach',
],
'ban_groups_seq' => [
'table' => 'ban_groups',
'field' => 'id_ban_group',
],
'ban_items_seq' => [
'table' => 'ban_items',
'field' => 'id_ban',
],
'boards_seq' => [
'table' => 'boards',
'field' => 'id_board',
],
'calendar_seq' => [
'table' => 'calendar',
'field' => 'id_event',
],
'calendar_holidays_seq' => [
'table' => 'calendar_holidays',
'field' => 'id_holiday',
],
'categories_seq' => [
'table' => 'categories',
'field' => 'id_cat',
],
'custom_fields_seq' => [
'table' => 'custom_fields',
'field' => 'id_field',
],
'log_actions_seq' => [
'table' => 'log_actions',
'field' => 'id_action',
],
'log_banned_seq' => [
'table' => 'log_banned',
'field' => 'id_ban_log',
],
'log_comments_seq' => [
'table' => 'log_comments',
'field' => 'id_comment',
],
'log_errors_seq' => [
'table' => 'log_errors',
'field' => 'id_error',
],
'log_group_requests_seq' => [
'table' => 'log_group_requests',
'field' => 'id_request',
],
'log_member_notices_seq' => [
'table' => 'log_member_notices',
'field' => 'id_notice',
],
'log_packages_seq' => [
'table' => 'log_packages',
'field' => 'id_install',
],
'log_reported_seq' => [
'table' => 'log_reported',
'field' => 'id_report',
],
'log_reported_comments_seq' => [
'table' => 'log_reported_comments',
'field' => 'id_comment',
],
'log_scheduled_tasks_seq' => [
'table' => 'log_scheduled_tasks',
'field' => 'id_log',
],
'log_spider_hits_seq' => [
'table' => 'log_spider_hits',
'field' => 'id_hit',
],
'log_subscribed_seq' => [
'table' => 'log_subscribed',
'field' => 'id_sublog',
],
'mail_queue_seq' => [
'table' => 'mail_queue',
'field' => 'id_mail',
],
'membergroups_seq' => [
'table' => 'membergroups',
'field' => 'id_group',
],
'members_seq' => [
'table' => 'members',
'field' => 'id_member',
],
'message_icons_seq' => [
'table' => 'message_icons',
'field' => 'id_icon',
],
'messages_seq' => [
'table' => 'messages',
'field' => 'id_msg',
],
'package_servers_seq' => [
'table' => 'package_servers',
'field' => 'id_server',
],
'permission_profiles_seq' => [
'table' => 'permission_profiles',
'field' => 'id_profile',
],
'personal_messages_seq' => [
'table' => 'personal_messages',
'field' => 'id_pm',
],
'pm_rules_seq' => [
'table' => 'pm_rules',
'field' => 'id_rule',
],
'polls_seq' => [
'table' => 'polls',
'field' => 'id_poll',
],
'scheduled_tasks_seq' => [
'table' => 'scheduled_tasks',
'field' => 'id_task',
],
'smileys_seq' => [
'table' => 'smileys',
'field' => 'id_smiley',
],
'spiders_seq' => [
'table' => 'spiders',
'field' => 'id_spider',
],
'subscriptions_seq' => [
'table' => 'subscriptions',
'field' => 'id_subscribe',
],
'topics_seq' => [
'table' => 'topics',
'field' => 'id_topic',
],
];

/**
* {@inheritDoc}
*/
public function isCandidate(): bool
{
return Config::$db_type == POSTGRE_TITLE;
}

/**
* {@inheritDoc}
*/
public function execute(): bool
{
for ($key = Maintenance::getCurrentStart(); $key < count($this->sequences); Maintenance::setCurrentStart()) {
$this->handleTimeout();

$value = $this->sequences[$key];

$this->query(
'',
"SELECT setval('{raw:key}', (SELECT COALESCE(MAX({raw:field}),1) FROM {raw:table}))",
[
'key' => Config::$db_prefix . $key,
'field' => $value['field'],
'table' => $value['table'],
],
);
}

return true;
}
}

?>
Loading

0 comments on commit c6e4966

Please sign in to comment.