Fix 5.29 regression using temp tables
authoreileen <emcnaughton@wikimedia.org>
Wed, 12 Aug 2020 04:20:38 +0000 (16:20 +1200)
committereileen <emcnaughton@wikimedia.org>
Sat, 15 Aug 2020 00:07:16 +0000 (12:07 +1200)
I just pushed 5.29rc through our WMF CI & it failed on a situation where it created a temp
table and that got joined on a non-temp table with a collation mix error. The issue is that the
DB default is set to utf8_general_ci (at least on our development DBs) whereas the
civicrm tables are utf8_unicode_ci. Until https://github.com/civicrm/civicrm-core/pull/18012/files
there was a list of permitted matches for db collation but now any 'utf' string matches.

From what I can tell utf8_general_ci  is a pretty common collation on drupal DBs and from my tests
it causes problems if not excluded here

CRM/Core/BAO/SchemaHandler.php
CRM/Utils/SQL/TempTable.php
tests/phpunit/api/v3/ReportTemplateTest.php

index bbe11ae3af9a33da3b4671d87a8f3c747daaa26a..5523858d6ac9fa69be7ed35e13f216d9b41daa68 100644 (file)
@@ -879,6 +879,23 @@ MODIFY      {$columnName} varchar( $length )
     return CRM_Core_DAO::singleValueQuery('SELECT @@collation_database');
   }
 
+  /**
+   * Get the collation actually being used by the tables in the database.
+   *
+   * The db collation may not match the collation used by the tables, get what is
+   * set on the tables (represented by civicrm_contact).
+   *
+   * @return string
+   */
+  public static function getInUseCollation() {
+    if (!isset(\Civi::$statics[__CLASS__][__FUNCTION__])) {
+      $dao = CRM_Core_DAO::executeQuery('SHOW TABLE STATUS LIKE \'civicrm_contact\'');
+      $dao->fetch();
+      \Civi::$statics[__CLASS__][__FUNCTION__] = $dao->Collation;
+    }
+    return \Civi::$statics[__CLASS__][__FUNCTION__];
+  }
+
   /**
    * Get the database collation.
    *
index f0024d7ef79a5a15728fa8b35687061d04ca5c3b..8993807f3cc5e03a12d7722ab69960b9fde469bb 100644 (file)
@@ -135,23 +135,13 @@ class CRM_Utils_SQL_TempTable {
   /**
    * Get the utf8 string for the table.
    *
-   * If the db collation is already utf8 by default (either
-   * utf8 or utf84mb) then rely on that. Otherwise set to utf8.
-   *
-   * Respecting the DB collation supports utf8mb4 adopters, which is currently
-   * not the norm in civi installs.
+   * Our tables are either utf8_unicode_ci OR utf8mb8_unicode_ci - check the contact table
+   * to see which & use the matching one.
    *
    * @return string
    */
   public function getUtf8String() {
-    if (!$this->utf8) {
-      return '';
-    }
-    $dbUTF = CRM_Core_BAO_SchemaHandler::getDBCollation();
-    if (strpos($dbUTF, 'utf8') !== FALSE) {
-      return '';
-    }
-    return self::UTF8;
+    return $this->utf8 ? ('COLLATE ' . CRM_Core_BAO_SchemaHandler::getInUseCollation()) : '';
   }
 
   /**
index d2fc418cd4894b8b3cd235b2dc72907d27e26ea9..340d7040d53e5479ddd5af147ab8566559b1a36d 100644 (file)
@@ -493,7 +493,7 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
 
     $this->assertEquals(2, $rows['count'], 'Report failed - the sql used to generate the results was ' . print_r($rows['metadata']['sql'], TRUE));
 
-    $expected = preg_replace('/\s+/', ' ', 'DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci AS
+    $expected = preg_replace('/\s+/', ' ', 'COLLATE utf8_unicode_ci AS
       SELECT SQL_CALC_FOUND_ROWS contact_civireport.id as cid  FROM civicrm_contact contact_civireport    INNER JOIN civicrm_contribution contribution_civireport USE index (received_date) ON contribution_civireport.contact_id = contact_civireport.id
          AND contribution_civireport.is_test = 0
          AND contribution_civireport.receive_date BETWEEN \'20140701000000\' AND \'20150630235959\'