From 194b56c872f0d61a0d196b9a8bab03bc8c4a773d Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Sat, 23 Jul 2022 06:46:46 +0100
Subject: [PATCH] Issue #3215062 by quietone, gambry, ravi.shankar, dww,
 daffie, yogeshmpawar, aarti zikre, alexpott, mfb, larowlan, longwave, catch:
 Update hook_schema for Y2038

---
 .../lib/Drupal/Core/Cache/DatabaseBackend.php |   1 +
 .../lib/Drupal/Core/Flood/DatabaseBackend.php |   2 +
 core/lib/Drupal/Core/Queue/DatabaseQueue.php  |   2 +
 core/modules/comment/comment.install          |  18 +++
 core/modules/dblog/dblog.install              |  18 +++
 core/modules/forum/forum.install              |  29 ++++
 core/modules/history/history.install          |  18 +++
 core/modules/locale/locale.install            |  28 ++++
 core/modules/migrate/migrate.install          |  21 +++
 .../migrate/src/Plugin/migrate/id_map/Sql.php |   1 +
 .../Unit/MigrateSqlIdMapEnsureTablesTest.php  |   1 +
 core/modules/statistics/statistics.install    |  19 +++
 core/modules/system/system.install            | 101 ++++++++++++
 .../tests/fixtures/update/Y2038-timestamp.php |  69 ++++++++
 .../Update/Y2038TimestampUpdateTest.php       | 147 ++++++++++++++++++
 core/modules/tracker/tracker.install          |  32 ++++
 core/modules/views/src/Tests/ViewTestData.php |   1 +
 .../src/Kernel/Handler/FieldDateTest.php      |   1 +
 18 files changed, 509 insertions(+)
 create mode 100644 core/modules/system/tests/fixtures/update/Y2038-timestamp.php
 create mode 100644 core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php

diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 879ffae6691b..4a4bf93fc2a4 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -498,6 +498,7 @@ public function schemaDefinition() {
           'type' => 'int',
           'not null' => TRUE,
           'default' => 0,
+          'size' => 'big',
         ],
         'created' => [
           'description' => 'A timestamp with millisecond precision indicating when the cache entry was created.',
diff --git a/core/lib/Drupal/Core/Flood/DatabaseBackend.php b/core/lib/Drupal/Core/Flood/DatabaseBackend.php
index 7caef173e3fc..e1f57a51868a 100644
--- a/core/lib/Drupal/Core/Flood/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Flood/DatabaseBackend.php
@@ -215,12 +215,14 @@ public function schemaDefinition() {
           'type' => 'int',
           'not null' => TRUE,
           'default' => 0,
+          'size' => 'big',
         ],
         'expiration' => [
           'description' => 'Expiration timestamp. Expired events are purged on cron run.',
           'type' => 'int',
           'not null' => TRUE,
           'default' => 0,
+          'size' => 'big',
         ],
       ],
       'primary key' => ['fid'],
diff --git a/core/lib/Drupal/Core/Queue/DatabaseQueue.php b/core/lib/Drupal/Core/Queue/DatabaseQueue.php
index 2af4360dcab2..5a77827c8f21 100644
--- a/core/lib/Drupal/Core/Queue/DatabaseQueue.php
+++ b/core/lib/Drupal/Core/Queue/DatabaseQueue.php
@@ -333,12 +333,14 @@ public function schemaDefinition() {
           'not null' => TRUE,
           'default' => 0,
           'description' => 'Timestamp when the claim lease expires on the item.',
+          'size' => 'big',
         ],
         'created' => [
           'type' => 'int',
           'not null' => TRUE,
           'default' => 0,
           'description' => 'Timestamp when the item was created.',
+          'size' => 'big',
         ],
       ],
       'primary key' => ['item_id'],
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 28c6f54be37e..d60f06442ed9 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -69,6 +69,7 @@ function comment_schema() {
         'not null' => TRUE,
         'default' => 0,
         'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.changed.',
+        'size' => 'big',
       ],
       'last_comment_name' => [
         'type' => 'varchar',
@@ -116,3 +117,20 @@ function comment_schema() {
 function comment_update_last_removed() {
   return 8701;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function comment_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('comment_entity_statistics') && $connection->databaseType() != 'sqlite') {
+    $new = [
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.changed.',
+        'size' => 'big',
+      ];
+    $connection->schema()->changeField('comment_entity_statistics', 'last_comment_timestamp', 'last_comment_timestamp', $new);
+  }
+}
diff --git a/core/modules/dblog/dblog.install b/core/modules/dblog/dblog.install
index b3164051b883..c6753ca34f19 100644
--- a/core/modules/dblog/dblog.install
+++ b/core/modules/dblog/dblog.install
@@ -78,6 +78,7 @@ function dblog_schema() {
         'not null' => TRUE,
         'default' => 0,
         'description' => 'Unix timestamp of when event occurred.',
+        'size' => 'big',
       ],
     ],
     'primary key' => ['wid'],
@@ -97,3 +98,20 @@ function dblog_schema() {
 function dblog_update_last_removed() {
   return 8600;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function dblog_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('watchdog') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+      'description' => 'Unix timestamp of when event occurred.',
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('watchdog', 'timestamp', 'timestamp', $new);
+  }
+}
diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install
index 1cc8ca3e588b..8f1ff13fd071 100644
--- a/core/modules/forum/forum.install
+++ b/core/modules/forum/forum.install
@@ -139,12 +139,14 @@ function forum_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
       'last_comment_timestamp' => [
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
         'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.timestamp.',
+        'size' => 'big',
       ],
       'comment_count' => [
         'type' => 'int',
@@ -175,3 +177,30 @@ function forum_schema() {
 
   return $schema;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function forum_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('forum_index') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The Unix timestamp when the node was created.',
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('forum_index', 'created', 'created', $new);
+
+    $new = [
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+      'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.timestamp.',
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('forum_index', 'last_comment_timestamp', 'last_comment_timestamp', $new);
+  }
+}
diff --git a/core/modules/history/history.install b/core/modules/history/history.install
index 3020d8bc24a8..8bd664275c5b 100644
--- a/core/modules/history/history.install
+++ b/core/modules/history/history.install
@@ -30,6 +30,7 @@ function history_schema() {
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
     ],
     'primary key' => ['uid', 'nid'],
@@ -47,3 +48,20 @@ function history_schema() {
 function history_update_last_removed() {
   return 8101;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function history_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('history') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The Unix timestamp at which the read occurred.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('history', 'timestamp', 'timestamp', $new);
+  }
+}
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index a33c41c58326..97f4327face7 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -230,12 +230,14 @@ function locale_schema() {
         'not null' => FALSE,
         'default' => 0,
         'description' => 'Unix timestamp of the imported file.',
+        'size' => 'big',
       ],
       'last_checked' => [
         'type' => 'int',
         'not null' => FALSE,
         'default' => 0,
         'description' => 'Unix timestamp of the last time this translation was confirmed to be the most recent release available.',
+        'size' => 'big',
       ],
     ],
     'primary key' => ['project', 'langcode'],
@@ -313,3 +315,29 @@ function locale_requirements($phase) {
 function locale_update_last_removed() {
   return 9101;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function locale_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('locale_file') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'type' => 'int',
+      'not null' => FALSE,
+      'default' => 0,
+      'description' => 'Unix timestamp of the imported file.',
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('locale_file', 'timestamp', 'timestamp', $new);
+
+    $new = [
+      'type' => 'int',
+      'not null' => FALSE,
+      'default' => 0,
+      'description' => 'Unix timestamp of the last time this translation was confirmed to be the most recent release available.',
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('locale_file', 'last_checked', 'last_checked', $new);
+  }
+}
diff --git a/core/modules/migrate/migrate.install b/core/modules/migrate/migrate.install
index c701034c81cb..72ac368b5bd0 100644
--- a/core/modules/migrate/migrate.install
+++ b/core/modules/migrate/migrate.install
@@ -11,3 +11,24 @@
 function migrate_update_last_removed() {
   return 8001;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function migrate_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  $tables = $connection->schema()->findTables('migrate_map_%');
+  if (!empty($tables) && $connection->databaseType() != 'sqlite') {
+    foreach ($tables as $table) {
+      $new = [
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'UNIX timestamp of the last time this row was imported',
+        'size' => 'big',
+      ];
+      $connection->schema()->changeField($table, 'last_imported', 'last_imported', $new);
+    }
+  }
+}
diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
index 55971fde0edd..c83b18d3edc6 100644
--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -370,6 +370,7 @@ protected function ensureTables() {
         'not null' => TRUE,
         'default' => 0,
         'description' => 'UNIX timestamp of the last time this row was imported',
+        'size' => 'big',
       ];
       $fields['hash'] = [
         'type' => 'varchar',
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
index 2963547dc3a9..cee2cd55700f 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
@@ -65,6 +65,7 @@ public function testEnsureTablesNotExist() {
       'not null' => TRUE,
       'default' => 0,
       'description' => 'UNIX timestamp of the last time this row was imported',
+      'size' => 'big',
     ];
     $fields['hash'] = [
       'type' => 'varchar',
diff --git a/core/modules/statistics/statistics.install b/core/modules/statistics/statistics.install
index f5c8a82fce24..5859971748ba 100644
--- a/core/modules/statistics/statistics.install
+++ b/core/modules/statistics/statistics.install
@@ -50,6 +50,7 @@ function statistics_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
     ],
     'primary key' => ['nid'],
@@ -64,3 +65,21 @@ function statistics_schema() {
 function statistics_update_last_removed() {
   return 8300;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function statistics_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('node_counter') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The most recent time the {node} has been viewed.',
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('node_counter', 'timestamp', 'timestamp', $new);
+  }
+}
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 44d930d730b4..1075cc1ec2c4 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -11,6 +11,7 @@
 use Drupal\Component\Utility\Environment;
 use Drupal\Component\Utility\OpCodeCache;
 use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Extension\ExtensionLifecycle;
@@ -1493,6 +1494,7 @@ function system_schema() {
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
       'session' => [
         'description' => 'The serialized contents of the user\'s session, an array of name/value pairs that persists across page requests by this session ID. Drupal loads the user\'s session from here at the start of each request and saves it at the end.',
@@ -1576,3 +1578,102 @@ function _system_advisories_requirements(array &$requirements): void {
     }
   }
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function system_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  $schema = $connection->schema();
+
+  // Update sessions table.
+  if ($schema->tableExists('sessions') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The Unix timestamp when this session last requested a page. Old records are purged by PHP automatically.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $schema->changeField('sessions', 'timestamp', 'timestamp', $new);
+  }
+
+  // Update batch table.
+  if ($schema->tableExists('batch') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.',
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'big',
+    ];
+    $schema->changeField('batch', 'timestamp', 'timestamp', $new);
+  }
+
+  // Update flood table.
+  if ($schema->tableExists('flood') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'timestamp' => [
+        'description' => 'Timestamp of the event.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'big',
+      ],
+      'expiration' => [
+        'description' => 'Expiration timestamp. Expired events are purged on cron run.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'big',
+      ],
+    ];
+    foreach ($new as $column_name => $value) {
+      $schema->changeField('flood', $column_name, $column_name, $value);
+    }
+  }
+  // Update queue table.
+  if ($schema->tableExists('queue') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'expire' => [
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Timestamp when the claim lease expires on the item.',
+        'size' => 'big',
+      ],
+      'created' => [
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Timestamp when the item was created.',
+        'size' => 'big',
+      ],
+    ];
+    foreach ($new as $column_name => $value) {
+      $schema->changeField('queue', $column_name, $column_name, $value);
+    }
+  }
+
+  // Update cache tables.
+  $cache_tables = $schema->findTables('cache_%');
+  $cache_tables = array_filter($cache_tables, function ($cache_tables) {
+    return str_starts_with($cache_tables, 'cache_');
+  });
+  if ($connection->databaseType() != 'sqlite') {
+    foreach (array_keys($cache_tables) as $table) {
+      // Truncate cache tables. They will be flushed anyway at the end of
+      // database updates, but emptying the tables now will boost the schema
+      // changes.
+      $connection->truncate($table)->execute();
+      $new = [
+        'description' => 'A Unix timestamp indicating when the cache entry should expire, or ' . Cache::PERMANENT . ' for never.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'big',
+      ];
+      $schema->changeField($table, 'expire', 'expire', $new);
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/fixtures/update/Y2038-timestamp.php b/core/modules/system/tests/fixtures/update/Y2038-timestamp.php
new file mode 100644
index 000000000000..4e458184ccff
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/Y2038-timestamp.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains database additions for testing year 2038 update.
+ */
+
+use Drupal\Core\Database\Database;
+
+// cspell:ignore destid sourceid
+
+$connection = Database::getConnection();
+
+// Add a migrate map table.
+$connection->schema()->createTable('migrate_map_d7_file', [
+  'fields' => [
+    'source_ids_hash' => [
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '64',
+    ],
+    'sourceid1' => [
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+    ],
+    'destid1' => [
+      'type' => 'int',
+      'not null' => FALSE,
+      'size' => 'normal',
+      'unsigned' => TRUE,
+    ],
+    'source_row_status' => [
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ],
+    'rollback_action' => [
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'tiny',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ],
+    'last_imported' => [
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'default' => '0',
+      'unsigned' => TRUE,
+    ],
+    'hash' => [
+      'type' => 'varchar',
+      'not null' => FALSE,
+      'length' => '64',
+    ],
+  ],
+  'primary key' => [
+    'source_ids_hash',
+  ],
+  'indexes' => [
+    'source' => [
+      'sourceid1',
+    ],
+  ],
+  'mysql_character_set' => 'utf8mb4',
+]);
diff --git a/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
new file mode 100644
index 000000000000..20d92bbb2c5e
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+use Drupal\Tests\UpdatePathTestTrait;
+
+/**
+ * Tests update of timestamp fields to bigint.
+ *
+ * @group system
+ */
+class Y2038TimestampUpdateTest extends UpdatePathTestBase {
+
+  use UpdatePathTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['forum'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * The tables and column name of the time fields.
+   *
+   * The key is the table name and the values are the names of the time fields.
+   *
+   * @var string[][]
+   */
+  protected $tables = [
+    'comment_entity_statistics' => ['last_comment_timestamp'],
+    'forum_index' => ['created', 'last_comment_timestamp'],
+    'history' => ['timestamp'],
+    'locale_file' => ['timestamp', 'last_checked'],
+    'node_counter' => ['timestamp'],
+    'sessions' => ['timestamp'],
+    'tracker_node' => ['changed'],
+    'tracker_user' => ['changed'],
+    'watchdog' => ['timestamp'],
+    'batch' => ['timestamp'],
+    'queue' => ['created', 'expire'],
+    'flood' => ['expiration', 'timestamp'],
+  ];
+
+  /**
+   * The database.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The name of the test database.
+   *
+   * @var string
+   */
+  protected $databaseName;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    /** @var \Drupal\Core\Database\Connection $connection */
+    $this->connection = \Drupal::service('database');
+    if ($this->connection->databaseType() == 'pgsql') {
+      $this->databaseName = 'public';
+    }
+    else {
+      $this->databaseName = $this->connection->getConnectionOptions()['database'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      // Start with a standard install of Drupal 9.3.0 with the following
+      // enabled modules: forum, language, locale, statistics and tracker.
+      DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.3.0.filled.standard.php.gz',
+      DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/Y2038-timestamp.php',
+    ];
+  }
+
+  /**
+   * Tests update of time fields.
+   */
+  public function testUpdate() {
+    if (\Drupal::service('database')->databaseType() == 'sqlite') {
+      $this->markTestSkipped("This test does not support the SQLite database driver.");
+    }
+
+    $this->collectTimestampFieldsFromDatabase();
+    // PostgreSQL returns the value 'integer' instead of 'int' when queried
+    // about the column type. Some PostgreSQL tables are already of the type
+    // 'bigint'.
+    $this->assertTimestampFields(['int', 'integer', 'bigint']);
+
+    $this->runUpdates();
+
+    $this->assertTimestampFields(['bigint']);
+  }
+
+  /**
+   * Collect the timestamp fields from the database and update table list.
+   */
+  public function collectTimestampFieldsFromDatabase() {
+    /** @var \Drupal\Core\Database\Connection $connection */
+    $connection = \Drupal::service('database');
+
+    // Build list of all tables and fields to check.
+    $tables = $connection->schema()->findTables('migrate_map_%');
+    foreach ($tables as $table) {
+      $this->tables[$table] = ['last_imported'];
+    }
+    $tables = $connection->schema()->findTables('cache_%');
+    $tables = array_filter($tables, function ($table) {
+      return str_starts_with($table, 'cache_');
+    });
+    $this->assertNotEmpty($tables);
+    foreach ($tables as $table) {
+      $this->tables[$table] = ['expire'];
+    }
+  }
+
+  /**
+   * Asserts the size of the timestamp fields.
+   */
+  public function assertTimestampFields($expected_values) {
+    // Check the size of all the fields.
+    foreach ($this->tables as $table => $column_names) {
+      $table_name = $this->connection->getPrefix() . $table;
+      foreach ($column_names as $column_name) {
+        $result = $this->connection->query("SELECT data_type FROM information_schema.columns WHERE table_schema = '$this->databaseName' and table_name = '$table_name' and column_name = '$column_name';")
+          ->fetchField();
+
+        $this->assertContains($result, $expected_values, "Failed for '$table_name' column '$column_name'");
+      }
+    }
+  }
+
+}
diff --git a/core/modules/tracker/tracker.install b/core/modules/tracker/tracker.install
index 28afb74c1f2b..bfec662a8deb 100644
--- a/core/modules/tracker/tracker.install
+++ b/core/modules/tracker/tracker.install
@@ -52,6 +52,7 @@ function tracker_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
     ],
     'indexes' => [
@@ -96,6 +97,7 @@ function tracker_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
+        'size' => 'big',
       ],
     ],
     'indexes' => [
@@ -116,3 +118,33 @@ function tracker_schema() {
 
   return $schema;
 }
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function tracker_update_10100(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('tracker_node') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('tracker_node', 'changed', 'changed', $new);
+  }
+  if ($connection->schema()->tableExists('tracker_user') && $connection->databaseType() != 'sqlite') {
+    $new = [
+      'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'big',
+    ];
+    $connection->schema()->changeField('tracker_user', 'changed', 'changed', $new);
+  }
+
+}
diff --git a/core/modules/views/src/Tests/ViewTestData.php b/core/modules/views/src/Tests/ViewTestData.php
index 737f4a237ac7..b5e01d9c8ef0 100644
--- a/core/modules/views/src/Tests/ViewTestData.php
+++ b/core/modules/views/src/Tests/ViewTestData.php
@@ -97,6 +97,7 @@ public static function schemaDefinition() {
           'unsigned' => TRUE,
           'not null' => TRUE,
           'default' => 0,
+          'size' => 'big',
         ],
         'status' => [
           'description' => "The status of this record",
diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php
index cd584e685983..40ef275dd478 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php
@@ -31,6 +31,7 @@ public function schemaDefinition() {
       'unsigned' => TRUE,
       'not null' => FALSE,
       'default' => 0,
+      'size' => 'big',
     ];
     return $schema;
   }
-- 
GitLab