diff --git a/Sources/Maintenance/Database/MySQL.php b/Sources/Maintenance/Database/MySQL.php
index f40d8dbcc0..ecffdb310f 100644
--- a/Sources/Maintenance/Database/MySQL.php
+++ b/Sources/Maintenance/Database/MySQL.php
@@ -15,6 +15,7 @@
namespace SMF\Maintenance\Database;
+use SMF\Config;
use SMF\Db\DatabaseApi as Db;
use SMF\Maintenance\DatabaseInterface;
@@ -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;
+ }
}
?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Database/PostgreSQL.php b/Sources/Maintenance/Database/PostgreSQL.php
index 3d79c9e926..6776c4d2f9 100644
--- a/Sources/Maintenance/Database/PostgreSQL.php
+++ b/Sources/Maintenance/Database/PostgreSQL.php
@@ -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;
+ }
+
}
?>
\ No newline at end of file
diff --git a/Sources/Maintenance/DatabaseInterface.php b/Sources/Maintenance/DatabaseInterface.php
index f630c1383b..fd3cb0f20b 100644
--- a/Sources/Maintenance/DatabaseInterface.php
+++ b/Sources/Maintenance/DatabaseInterface.php
@@ -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;
}
?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Migration/v2_1/Migration0201.php b/Sources/Maintenance/Migration/v2_1/Migration0201.php
new file mode 100644
index 0000000000..383f948879
--- /dev/null
+++ b/Sources/Maintenance/Migration/v2_1/Migration0201.php
@@ -0,0 +1,209 @@
+ [
+ '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;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Migration/v2_1/Migration0202.php b/Sources/Maintenance/Migration/v2_1/Migration0202.php
new file mode 100644
index 0000000000..f42083eab8
--- /dev/null
+++ b/Sources/Maintenance/Migration/v2_1/Migration0202.php
@@ -0,0 +1,59 @@
+query(
+ '',
+ "
+ CREATE OR REPLACE FUNCTION FIND_IN_SET(needle text, haystack text) RETURNS integer AS '
+ SELECT i AS result
+ FROM generate_series(1, array_upper(string_to_array($2,'',''), 1)) AS g(i)
+ WHERE (string_to_array($2,'',''))[i] = $1
+ UNION ALL
+ SELECT 0
+ LIMIT 1'
+ LANGUAGE 'sql';
+ ",
+ );
+
+ return true;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Migration/v2_1/Migration1001.php b/Sources/Maintenance/Migration/v2_1/Migration1001.php
new file mode 100644
index 0000000000..fa32cc4ef1
--- /dev/null
+++ b/Sources/Maintenance/Migration/v2_1/Migration1001.php
@@ -0,0 +1,82 @@
+query(
+ '',
+ '
+ DELETE FROM {db_prefix}settings
+ WHERE variable IN ({array_string:karma_vars})',
+ [
+ 'karma_vars' => ['karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'],
+ ],
+ );
+
+ $member_columns = Db::$db->list_columns('{db_prefix}members');
+
+ // Cleaning up old karma member settings.
+ if (in_array('karma_good', $member_columns)) {
+ Db::$db->remove_column('{db_prefix}members', 'karma_good');
+ }
+
+ // Does karma bad was enable?
+ if (in_array('karma_bad', $member_columns)) {
+ Db::$db->remove_column('{db_prefix}members', 'karma_bad');
+ }
+
+ // Cleaning up old karma permissions.
+ Db::$db->query(
+ '',
+ '
+ DELETE FROM {db_prefix}permissions
+ WHERE permission = {string:karma_vars}',
+ [
+ 'karma_vars' => 'karma_edit',
+ ],
+ );
+
+ // Cleaning up old log_karma table
+ Db::$db->drop_table('{db_prefix}log_karma');
+
+ return true;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Migration/v2_1/Migration1002.php b/Sources/Maintenance/Migration/v2_1/Migration1002.php
new file mode 100644
index 0000000000..814e9f5fd4
--- /dev/null
+++ b/Sources/Maintenance/Migration/v2_1/Migration1002.php
@@ -0,0 +1,165 @@
+), which would be similar the more standard DATEFROMPARTS.
+
+ // PostgreSQL does the query a bit different.
+ $is_pgsql = Config::$db_type == POSTGRE_TITLE;
+
+ if (Maintenance::getCurrentStart() < 1 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {db_prefix}calendar
+ SET start_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM start_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM start_date), EXTRACT(DAY FROM start_date))::date
+ WHERE EXTRACT(YEAR FROM start_date) < 1004');
+ } elseif (Maintenance::getCurrentStart() < 1) {
+ $this->query('', '
+ UPDATE {db_prefix}calendar
+ SET start_date = DATE(CONCAT(1004, {literal:-}, MONTH(start_date), {literal:-}, DAY(start_date)))
+ WHERE YEAR(start_date) < 1004');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 2 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {db_prefix}calendar
+ SET end_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM end_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM end_date), EXTRACT(DAY FROM end_date))::date
+ WHERE EXTRACT(YEAR FROM end_date) < 1004');
+ } elseif (Maintenance::getCurrentStart() < 2) {
+ $this->query('', '
+ UPDATE {$db_prefix}calendar
+ SET end_date = DATE(CONCAT(1004, {literal:-}, MONTH(end_date), {literal:-}, DAY(end_date)))
+ WHERE YEAR(end_date) < 1004');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 3 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {db_prefix}calendar_holidays
+ SET event_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM event_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM event_date), EXTRACT(DAY FROM event_date))::date
+ WHERE EXTRACT(YEAR FROM event_date) < 1004');
+ } elseif (Maintenance::getCurrentStart() < 3) {
+ $this->query('', '
+ UPDATE {$db_prefix}calendar_holidays
+ SET event_date = DATE(CONCAT(1004, {literal:-}, MONTH(event_date), {literal:-}, DAY(event_date)))
+ WHERE YEAR(event_date) < 1004');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 4 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {db_prefix}log_spider_stats
+ SET stat_date = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM stat_date) < 1004 THEN 1004 END, EXTRACT(MONTH FROM stat_date), EXTRACT(DAY FROM stat_date))::date
+ WHERE EXTRACT(YEAR FROM stat_date) < 1004', []);
+ } elseif (Maintenance::getCurrentStart() < 4) {
+ $this->query('', '
+ UPDATE {$db_prefix}log_spider_stats
+ SET stat_date = DATE(CONCAT(1004, {literal:-}, MONTH(stat_date), {literal:-}, DAY(stat_date)))
+ WHERE YEAR(stat_date) < 1004');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 5 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {db_prefix}log_spider_stats
+ SET birthdate = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM birthdate) < 1004 THEN 1004 END, CASE WHEN EXTRACT(MONTH FROM birthdate) < 1 THEN 1 ELSE EXTRACT(MONTH FROM birthdate) END, CASE WHEN EXTRACT(DAY FROM birthdate) < 1 THEN 1 ELSE EXTRACT(DAY FROM birthdate) END)::date
+ WHERE EXTRACT(YEAR FROM birthdate) < 1004 OR EXTRACT(MONTH FROM birthdate) < 1 OR EXTRACT(DAY FROM birthdate) < 1');
+ } elseif (Maintenance::getCurrentStart() < 5) {
+ $this->query('', '
+ UPDATE {$db_prefix}members
+ SET birthdate = DATE(CONCAT(IF(YEAR(birthdate) < 1004, 1004, YEAR(birthdate)), {literal:-}, IF(MONTH(birthdate) < 1, 1, MONTH(birthdate)), {literal:-}, IF(DAY(birthdate) < 1, 1, DAY(birthdate))))
+ WHERE YEAR(birthdate) < 1004 OR MONTH(birthdate) < 1 OR DAY(birthdate) < 1');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 6 && $is_pgsql) {
+ $this->query('', '
+ UPDATE {$db_prefix}members
+ SET birthdate = concat_ws({literal:-}, CASE WHEN EXTRACT(YEAR FROM birthdate) < 1004 THEN 1004 END, CASE WHEN EXTRACT(MONTH FROM birthdate) < 1 THEN 1 ELSE EXTRACT(MONTH FROM birthdate) END, CASE WHEN EXTRACT(DAY FROM birthdate) < 1 THEN 1 ELSE EXTRACT(DAY FROM birthdate) END)::date
+ WHERE EXTRACT(YEAR FROM birthdate) < 1004 OR EXTRACT(MONTH FROM birthdate) < 1 OR EXTRACT(DAY FROM birthdate) < 1', []);
+ } elseif (Maintenance::getCurrentStart() < 6) {
+ $this->query('', '
+ UPDATE {db_prefix}members
+ SET birthdate = DATE(CONCAT(IF(YEAR(birthdate) < 1004, 1004, YEAR(birthdate)), {literal:-}, IF(MONTH(birthdate) < 1, 1, MONTH(birthdate)), {literal:-}, IF(DAY(birthdate) < 1, 1, DAY(birthdate))))
+ WHERE YEAR(birthdate) < 1004 OR MONTH(birthdate) < 1 OR DAY(birthdate) < 1');
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ if (Maintenance::getCurrentStart() < 7) {
+ Db::$db->change_column(
+ '{db_prefix}log_activity',
+ 'DATE',
+ [
+ 'not_null' => true,
+ 'default' => null,
+ ],
+ );
+ }
+ Maintenance::setCurrentStart();
+ $this->handleTimeout();
+
+ $fixes = [
+ ['tbl' => '{db_prefix}calendar', 'col' => 'start_date'],
+ ['tbl' => '{db_prefix}calendar', 'col' => 'end_date'],
+ ['tbl' => '{db_prefix}calendar_holidays', 'col' => 'event_date'],
+ ['tbl' => '{db_prefix}log_spider_stats', 'col' => 'stat_date'],
+ ['tbl' => '{db_prefix}members', 'col' => 'birthdate'],
+ ];
+
+ for ($key = Maintenance::getCurrentStart(); $key < count($fixes); Maintenance::setCurrentStart()) {
+ $fix = $fixes[$key - 7];
+
+ Db::$db->change_column($fix['tbl'], $fix['col'], ['default' => '1004-01-01']);
+ $this->handleTimeout();
+ }
+
+ return true;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/Sources/Maintenance/Migration/v3_0/Migration0001.php b/Sources/Maintenance/Migration/v3_0/Migration0001.php
index 786851d374..7f728600ea 100644
--- a/Sources/Maintenance/Migration/v3_0/Migration0001.php
+++ b/Sources/Maintenance/Migration/v3_0/Migration0001.php
@@ -18,6 +18,7 @@
use SMF\Config;
use SMF\Db\DatabaseApi as Db;
use SMF\Lang;
+use SMF\Maintenance;
use SMF\Maintenance\Migration;
class Migration0001 extends Migration
@@ -47,7 +48,7 @@ public function execute(): bool
while (!$is_done) {
// @@ TODO: Handle sub steps.
- nextSubStep($substep);
+ $this->handleTimeout();
// Skip errors here so we don't croak if the columns don't exist...
$request = Db::$db->query(
@@ -93,6 +94,8 @@ public function execute(): bool
WHERE id_member IN ({array_int:search_members})',
$args,
);
+
+ Maintenance::setCurrentStart();
}
// Rename the privacy policy records.
@@ -118,6 +121,8 @@ public function execute(): bool
unset($new_variable);
}
}
+
+ return true;
}
}
diff --git a/Sources/Maintenance/MigrationBase.php b/Sources/Maintenance/MigrationBase.php
index 53c8dab651..dac8e39316 100644
--- a/Sources/Maintenance/MigrationBase.php
+++ b/Sources/Maintenance/MigrationBase.php
@@ -15,6 +15,12 @@
namespace SMF\Maintenance;
+use SMF\Config;
+use SMF\Db\DatabaseApi as Db;
+use SMF\Lang;
+use SMF\Maintenance;
+use SMF\Sapi;
+
/**
* Migration container for a maintenance task.
*/
@@ -28,7 +34,17 @@ class Migration
public string $name;
/**
- * Cleanup task we will execute.
+ * Check if the task should be performed or not.
+ *
+ * @return bool True if this task needs ran, false otherwise.
+ */
+ public function isCandidate(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Upgrade task we will execute.
*
* @return bool True if successful (or skipped), false otherwise.
*/
@@ -36,6 +52,89 @@ public function execute(): bool
{
return true;
}
+
+ /**
+ * Wrapper for the tool to handle timeout protection.
+ *
+ * If a timeout needs to occur, it is handled, ensure that prior to this call, all variables are updated..
+ */
+ protected function handleTimeout(?int $start = 0): void
+ {
+ Maintenance::setCurrentStart($start);
+
+ Maintenance::$tool->checkAndHandleTimeout();
+ }
+
+ /**
+ *
+ * @var DatabaseInterface The database type we are working with.
+ */
+ protected ?DatabaseInterface $db;
+
+ /**
+ * Wrapper for the database query.
+ *
+ * Ensures the query runs without handling errors, as we do not have that luxury.
+ */
+ protected function query(string $identifier, string $db_string, array $db_values = [], ?object $connection = null)
+ {
+ if (!Config::$modSettings['disableQueryCheck']) {
+ Config::$modSettings['disableQueryCheck'] = true;
+ }
+
+ if (!empty($db_values['unbuffered'])) {
+ Db::$unbuffered = true;
+ }
+
+ $db_values += [
+ 'security_override' => true,
+ 'db_error_skip' => true,
+ ];
+
+ $result = Db::$db->query($identifier, $db_string, $db_values, $connection);
+ Db::$unbuffered = false;
+
+ // Failure?!
+ if ($result !== false) {
+ return $result;
+ }
+
+ $db_error_message = Db::$db->error(Db::$db_connection);
+
+ if ($this->db === null) {
+ $this->db = Maintenance::$tool->loadMaintenanceDatabase(Config::$db_type);
+ }
+
+ // Checks if we can fix this.
+ $result = $this->db->processError($db_error_message, Db::$db->quote($db_string, $db_values, $connection));
+
+ if ($result !== false) {
+ return $result;
+ }
+
+ if (Sapi::isCLI()) {
+ echo 'Unsuccessful! Database error message:', "\n", $db_error_message, "\n";
+
+ die;
+ }
+
+ Maintenance::$context['try_again'] = true;
+ Maintenance::$fatal_error = '
+ ' . Lang::$txt['upgrade_unsuccessful'] . '
+
', + 'QUERY_ERROR' => '' . nl2br(htmlspecialchars(trim($db_string))) . ';
' . nl2br(htmlspecialchars($db_error_message)) . '', + ], + ) . + ' +