From 40c8b8293bc5311c0a521f801002a80be8883519 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 15:51:25 -0700 Subject: [PATCH 1/9] (dev/core#183) Add more robust TempTable generator --- CRM/Core/Config.php | 1 + CRM/Utils/SQL/TempTable.php | 266 ++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 CRM/Utils/SQL/TempTable.php diff --git a/CRM/Core/Config.php b/CRM/Core/Config.php index 349c672b83de..a1b3eb7f307b 100644 --- a/CRM/Core/Config.php +++ b/CRM/Core/Config.php @@ -392,6 +392,7 @@ public static function clearTempTables($timeInterval = FALSE) { OR TABLE_NAME LIKE 'civicrm_export_temp%' OR TABLE_NAME LIKE 'civicrm_task_action_temp%' OR TABLE_NAME LIKE 'civicrm_report_temp%' + OR TABLE_NAME LIKE 'civicrm_tmp_d%' ) "; if ($timeInterval) { diff --git a/CRM/Utils/SQL/TempTable.php b/CRM/Utils/SQL/TempTable.php new file mode 100644 index 000000000000..8e419789b49d --- /dev/null +++ b/CRM/Utils/SQL/TempTable.php @@ -0,0 +1,266 @@ +getName(); + * $name = CRM_Utils_SQL_TempTable::build()->setDurable()->getName(); + * + * Example 2: Create a temp table using the results of a SELECT query. + * + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->createWithQuery('SELECT id, display_name FROM civicrm_contact'); + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->createWithQuery(CRM_Utils_SQL_Select::from('civicrm_contact')->select('display_name')); + * + * Example 3: Create an empty temp table with list of columns. + * + * $tmpTbl = CRM_Utils_SQL_TempTable::build()->setDurable()->setUtf8()->createWithColumns('id int(10, name varchar(64)'); + * + * Example 4: Drop a table that you previously created. + * + * $tmpTbl->drop(); + * + * Example 5: Auto-drop a temp table when $tmpTbl falls out of scope + * + * $tmpTbl->setAutodrop(); + * + */ +class CRM_Utils_SQL_TempTable { + + const UTF8 = 'DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci'; + const CATEGORY_LENGTH = 12; + const CATEGORY_REGEXP = ';^[a-zA-Z0-9]+$;'; + const ID_LENGTH = 37; // MAX{64} - CATEGORY_LENGTH{12} - CONST_LENGHTH{15} = 37 + const ID_REGEXP = ';^[a-zA-Z0-9_]+$;'; + + /** + * @var bool + */ + protected $durable, $utf8; + + protected $category; + + protected $id; + + protected $autodrop; + + /** + * @return CRM_Utils_SQL_TempTable + */ + public static function build() { + $t = new CRM_Utils_SQL_TempTable(); + $t->category = NULL; + $t->id = md5(uniqid('', TRUE)); + // The constant CIVICRM_TEMP_FORCE_DURABLE is for local debugging. + $t->durable = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_DURABLE', FALSE); + // I suspect it would be better to just say utf8=true, but a lot of existing queries don't do the utf8 bit. + $t->utf8 = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_UTF8', FALSE); + $t->autodrop = FALSE; + return $t; + } + + public function __destruct() { + if ($this->autodrop) { + $this->drop(); + } + } + + /** + * Determine the full table name. + * + * @return string + * Ex: 'civicrm_tmp_d_foo_abcd1234abcd1234' + */ + public function getName() { + $parts = ['civicrm', 'tmp']; + $parts[] = ($this->durable ? 'd' : 'e'); + $parts[] = $this->category ? $this->category : 'dflt'; + $parts[] = $this->id ? $this->id : 'dflt'; + return implode('_', $parts); + } + + /** + * Create the table using results from a SELECT query. + * + * @param string|CRM_Utils_SQL_Select $selectQuery + * @return CRM_Utils_SQL_TempTable + */ + public function createWithQuery($selectQuery) { + $sql = sprintf('%s %s AS %s', + $this->toSQL('CREATE'), + $this->utf8 ? self::UTF8 : '', + ($selectQuery instanceof CRM_Utils_SQL_Select ? $selectQuery->toSQL() : $selectQuery) + ); + CRM_Core_DAO::executeQuery($sql, array(), TRUE, NULL, TRUE, FALSE); + return $this; + } + + /** + * Create the empty table. + * + * @parma string $columns + * SQL column listing. + * Ex: 'id int(10), name varchar(64)'. + * @return CRM_Utils_SQL_TempTable + */ + public function createWithColumns($columns) { + $sql = sprintf('%s (%s) %s', + $this->toSQL('CREATE'), + $columns, + $this->utf8 ? self::UTF8 : '' + ); + CRM_Core_DAO::executeQuery($sql, array(), TRUE, NULL, TRUE, FALSE); + return $this; + } + + /** + * Drop the table. + * + * @return CRM_Utils_SQL_TempTable + */ + public function drop() { + $sql = $this->toSQL('DROP', 'IF EXISTS'); + CRM_Core_DAO::executeQuery($sql, array(), TRUE, NULL, TRUE, FALSE); + return $this; + } + + /** + * @param string $action + * Ex: 'CREATE', 'DROP' + * @param string|NULL $ifne + * Ex: 'IF EXISTS', 'IF NOT EXISTS'. + * @return string + * Ex: 'CREATE TEMPORARY TABLE `civicrm_tmp_e_foo_abcd1234`' + * Ex: 'CREATE TABLE IF NOT EXISTS `civicrm_tmp_d_foo_abcd1234`' + */ + private function toSQL($action, $ifne = NULL) { + $parts = []; + $parts[] = $action; + if (!$this->durable) { + $parts[] = 'TEMPORARY'; + } + $parts[] = 'TABLE'; + if ($ifne) { + $parts[] = $ifne; + } + $parts[] = '`' . $this->getName() . '`'; + return implode(' ', $parts); + } + + /** + * @return string|NULL + */ + public function getCategory() { + return $this->category; + } + + /** + * @return string|NULL + */ + public function getId() { + return $this->id; + } + + /** + * @return bool + */ + public function isAutodrop() { + return $this->autodrop; + } + + /** + * @return bool + */ + public function isDurable() { + return $this->durable; + } + + /** + * @return bool + */ + public function isUtf8() { + return $this->utf8; + } + + /** + * @param bool $autodrop + * @return CRM_Utils_SQL_TempTable + */ + public function setAutodrop($autodrop = TRUE) { + $this->autodrop = $autodrop; + return $this; + } + + /** + * @param string|NULL $category + * @return CRM_Utils_SQL_TempTable + */ + public function setCategory($category) { + if ($category && !preg_match(self::CATEGORY_REGEXP, $category) || strlen($category) > self::CATEGORY_LENGTH) { + throw new \RuntimeException("Malformed temp table category"); + } + $this->category = $category; + return $this; + } + + /** + * @parma bool $value + * @return CRM_Utils_SQL_TempTable + */ + public function setDurable($durable = TRUE) { + $this->durable = $durable; + return $this; + } + + /** + * @param mixed $id + * @return CRM_Utils_SQL_TempTable + */ + public function setId($id) { + if ($id && !preg_match(self::ID_REGEXP, $id) || strlen($id) > self::ID_LENGTH) { + throw new \RuntimeException("Malformed temp table id"); + } + $this->id = $id; + return $this; + } + + public function setUtf8($value = TRUE) { + $this->utf8 = $value; + return $this; + } + +} From 00f8d61b8b8b2114cf3dec8ae7ce4a10787e0f1e Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 16:48:33 -0700 Subject: [PATCH 2/9] (dev/core#183) Deprecate CRM_Core_DAO::createTempTableName() We're changing the naming formula, but some use-cases may rely on the old naming formula. Anything based on the old formula needs to be examined. --- CRM/Core/DAO.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 584f187e09bb..4d7a6f83fe03 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -2018,6 +2018,8 @@ public static function setCreateDefaults(&$params, $defaults) { * @param null $string * * @return string + * @deprecated + * @see CRM_Utils_SQL_TempTable */ public static function createTempTableName($prefix = 'civicrm', $addRandomString = TRUE, $string = NULL) { $tableName = $prefix . "_temp"; From c78f866ee1bcacd88801275c9a8ada9119e2b558 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 16:18:16 -0700 Subject: [PATCH 3/9] (dev/core#183) Change temp table naming for search-tasks Testing notes - This is a pretty formulaic change. The main concern is some kind of typo or bad table name causing a hard-fail. To control that risk, we just run the code in a facile way: * Perform a contact search * Pick some contacts * Export them We do this procedure under a few circumstances: * With the original code * With a purposefully bad edit (provoking an expected error) * With the new code --- CRM/Contact/Form/Task.php | 2 +- CRM/Core/Config.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CRM/Contact/Form/Task.php b/CRM/Contact/Form/Task.php index 5136641e3b70..243422f771dc 100644 --- a/CRM/Contact/Form/Task.php +++ b/CRM/Contact/Form/Task.php @@ -150,7 +150,7 @@ public static function preProcessCommon(&$form, $useTable = FALSE) { $form->assign('taskName', CRM_Utils_Array::value($form->_task, $crmContactTaskTasks)); if ($useTable) { - $form->_componentTable = CRM_Core_DAO::createTempTableName('civicrm_task_action', TRUE, $qfKey); + $form->_componentTable = CRM_Utils_SQL_TempTable::build()->setCategory('tskact')->setDurable()->setId($qfKey)->getName(); $sql = " DROP TABLE IF EXISTS {$form->_componentTable}"; CRM_Core_DAO::executeQuery($sql); diff --git a/CRM/Core/Config.php b/CRM/Core/Config.php index a1b3eb7f307b..c4517d6ca282 100644 --- a/CRM/Core/Config.php +++ b/CRM/Core/Config.php @@ -390,7 +390,6 @@ public static function clearTempTables($timeInterval = FALSE) { AND ( TABLE_NAME LIKE 'civicrm_import_job_%' OR TABLE_NAME LIKE 'civicrm_export_temp%' - OR TABLE_NAME LIKE 'civicrm_task_action_temp%' OR TABLE_NAME LIKE 'civicrm_report_temp%' OR TABLE_NAME LIKE 'civicrm_tmp_d%' ) From 8e8b9e7c98abdaadbcbaf2f76a7ee2879b86ae0a Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 16:27:37 -0700 Subject: [PATCH 4/9] (dev/core#183) Change temp table naming for exports Testing notes - This is a pretty formulaic change. The main concern is some kind of typo or bad table name causing a hard-fail. To control that risk, we just run the code in a facile way: * Perform a contact search * Pick some contacts * Export them * Run the export We do this procedure under a few circumstances: * With the original code * With a purposefully bad edit (provoking an expected error) * With the new code --- CRM/Core/Config.php | 1 - CRM/Export/BAO/Export.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CRM/Core/Config.php b/CRM/Core/Config.php index c4517d6ca282..03bf57144476 100644 --- a/CRM/Core/Config.php +++ b/CRM/Core/Config.php @@ -389,7 +389,6 @@ public static function clearTempTables($timeInterval = FALSE) { WHERE TABLE_SCHEMA = %1 AND ( TABLE_NAME LIKE 'civicrm_import_job_%' - OR TABLE_NAME LIKE 'civicrm_export_temp%' OR TABLE_NAME LIKE 'civicrm_report_temp%' OR TABLE_NAME LIKE 'civicrm_tmp_d%' ) diff --git a/CRM/Export/BAO/Export.php b/CRM/Export/BAO/Export.php index c6fd1fabab2d..dd6fb35db9bd 100644 --- a/CRM/Export/BAO/Export.php +++ b/CRM/Export/BAO/Export.php @@ -1259,7 +1259,7 @@ public static function writeDetailsToTable($tableName, &$details, &$sqlColumns) */ public static function createTempTable(&$sqlColumns) { //creating a temporary table for the search result that need be exported - $exportTempTable = CRM_Core_DAO::createTempTableName('civicrm_export', TRUE); + $exportTempTable = CRM_Utils_SQL_TempTable::build()->setDurable()->setCategory('export')->getName(); // also create the sql table $sql = "DROP TABLE IF EXISTS {$exportTempTable}"; From f7e48badfcde6af82dd052062f4ce1f570f0fce8 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 17:03:54 -0700 Subject: [PATCH 5/9] (dev/core#183) Change temp table naming for reports Testing notes - This is a pretty formulaic change. The main concern is some kind of typo or bad table name causing a hard-fail. To control that risk, we just run the code in a facile way: * Navigate to "Reports => Contributions => Lybunt" * Add a "Filter" by "Group" We do this procedure under a few circumstances: * With the original code * With a purposefully bad edit (provoking an expected error) * With the new code In this commit, we changed two lines, so we do the above once for each line. --- CRM/Core/Config.php | 2 ++ CRM/Report/Form.php | 2 +- CRM/Report/Form/Contribute/Lybunt.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CRM/Core/Config.php b/CRM/Core/Config.php index 03bf57144476..196ad6f4563f 100644 --- a/CRM/Core/Config.php +++ b/CRM/Core/Config.php @@ -393,6 +393,8 @@ public static function clearTempTables($timeInterval = FALSE) { OR TABLE_NAME LIKE 'civicrm_tmp_d%' ) "; + // NOTE: Cannot find use-cases where "civicrm_report_temp" would be durable. Could probably remove. + if ($timeInterval) { $query .= " AND CREATE_TIME < DATE_SUB(NOW(), INTERVAL {$timeInterval})"; } diff --git a/CRM/Report/Form.php b/CRM/Report/Form.php index dc599e03b2ba..e745e93f63da 100644 --- a/CRM/Report/Form.php +++ b/CRM/Report/Form.php @@ -3689,7 +3689,7 @@ public function buildGroupTempTable() { WHERE smartgroup_contact.group_id IN ({$smartGroups}) "; } - $this->groupTempTable = 'civicrm_report_temp_group_' . date('Ymd_') . uniqid(); + $this->groupTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('rptgrp')->setId(date('Ymd_') . uniqid())->getName(); $this->executeReportQuery(" CREATE TEMPORARY TABLE $this->groupTempTable $this->_databaseAttributes $query diff --git a/CRM/Report/Form/Contribute/Lybunt.php b/CRM/Report/Form/Contribute/Lybunt.php index 0fb7f093a5d8..e5aeb0fcba2f 100644 --- a/CRM/Report/Form/Contribute/Lybunt.php +++ b/CRM/Report/Form/Contribute/Lybunt.php @@ -567,7 +567,7 @@ public function beginPostProcessCommon() { // @todo this acl has no test coverage and is very hard to test manually so could be fragile. $this->resetFormSqlAndWhereHavingClauses(); - $this->contactTempTable = 'civicrm_report_temp_lybunt_c_' . date('Ymd_') . uniqid(); + $this->contactTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('rptlybunt')->setId(date('Ymd_') . uniqid())->getName(); $this->limit(); $getContacts = " CREATE TEMPORARY TABLE $this->contactTempTable {$this->_databaseAttributes} From 33fa8c9da93f05f06bc8d79b82bfc7ff8fff5485 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 20:35:05 -0700 Subject: [PATCH 6/9] (dev/core#183) Change temp table naming for CRM_Activity_BAO_Activity Note: Verified that changes to this line are reflected as passes/failures in CRM_Activity_BAO_ActivityTest. --- CRM/Activity/BAO/Activity.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index 11fcfa530d61..31c3c253d932 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -920,8 +920,7 @@ public static function deprecatedGetActivities($input) { $config = CRM_Core_Config::singleton(); - $randomNum = md5(uniqid()); - $activityTempTable = "civicrm_temp_activity_details_{$randomNum}"; + $activityTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('actdetail')->getName(); $tableFields = array( 'activity_id' => 'int unsigned', @@ -1012,7 +1011,7 @@ public static function deprecatedGetActivities($input) { // step 2: Get target and assignee contacts for above activities // create temp table for target contacts - $activityContactTempTable = "civicrm_temp_activity_contact_{$randomNum}"; + $activityContactTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('actcontact')->getName(); $query = "CREATE TEMPORARY TABLE {$activityContactTempTable} ( activity_id int unsigned, contact_id int unsigned, record_type_id varchar(16), contact_name varchar(255), is_deleted int unsigned, counter int unsigned, INDEX index_activity_id( activity_id ) ) From 6ec810cf5ea20fdca0e6a6867ea0a59e7c18c965 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 20:52:56 -0700 Subject: [PATCH 7/9] (dev/core#183) Change temp table naming for CRM/Contact/Form/Search/Custom/ContribSYBNT.php Re:`r-run` -- Verified that these lines are executed by the search form -- and that the outcomes of a few basic searches are the same with the patch. --- CRM/Contact/Form/Search/Custom/ContribSYBNT.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CRM/Contact/Form/Search/Custom/ContribSYBNT.php b/CRM/Contact/Form/Search/Custom/ContribSYBNT.php index bfd961b1c275..cf2b1e950be5 100644 --- a/CRM/Contact/Form/Search/Custom/ContribSYBNT.php +++ b/CRM/Contact/Form/Search/Custom/ContribSYBNT.php @@ -192,10 +192,8 @@ public function all( "; if ($justIDs) { - CRM_Core_DAO::executeQuery("DROP TEMPORARY TABLE IF EXISTS CustomSearch_SYBNT_temp"); - $query = "CREATE TEMPORARY TABLE CustomSearch_SYBNT_temp AS ({$sql})"; - CRM_Core_DAO::executeQuery($query); - $sql = "SELECT contact_a.id as contact_id FROM CustomSearch_SYBNT_temp as contact_a"; + $tempTable = CRM_Utils_SQL_TempTable::build()->createWithQuery($sql); + $sql = "SELECT contact_a.id as contact_id FROM {$tempTable->getName()} as contact_a"; } return $sql; } From d3011c3991cbfc8c88f82cbbea80dd6f3a524ac4 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 14 Jun 2018 21:31:50 -0700 Subject: [PATCH 8/9] (dev/core#183) Change temp table naming for CRM/Contact/Form/Search/Custom/DateAdded.php Re:`r-run` -- Verified that these lines are executed by the search form -- and that the outcomes of a few basic searches are the same with the patch. --- CRM/Contact/Form/Search/Custom/DateAdded.php | 59 ++++++++++---------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/CRM/Contact/Form/Search/Custom/DateAdded.php b/CRM/Contact/Form/Search/Custom/DateAdded.php index 3ec22422e3db..34ec772f3c76 100644 --- a/CRM/Contact/Form/Search/Custom/DateAdded.php +++ b/CRM/Contact/Form/Search/Custom/DateAdded.php @@ -36,6 +36,8 @@ class CRM_Contact_Form_Search_Custom_DateAdded extends CRM_Contact_Form_Search_C protected $_aclFrom = NULL; protected $_aclWhere = NULL; + protected $_datesTable = NULL, $_xgTable = NULL, $_igTable = NULL; + /** * Class constructor. * @@ -177,11 +179,12 @@ public function all( */ public function from() { //define table name - $randomNum = md5(uniqid()); - $this->_tableName = "civicrm_temp_custom_{$randomNum}"; + $this->_datesTable = CRM_Utils_SQL_TempTable::build()->setCategory('dates')->getName(); + $this->_xgTable = CRM_Utils_SQL_TempTable::build()->setCategory('xg')->getName(); + $this->_igTable = CRM_Utils_SQL_TempTable::build()->setCategory('ig')->getName(); //grab the contacts added in the date range first - $sql = "CREATE TEMPORARY TABLE dates_{$this->_tableName} ( id int primary key, date_added date ) ENGINE=HEAP"; + $sql = "CREATE TEMPORARY TABLE {$this->_datesTable} ( id int primary key, date_added date ) ENGINE=HEAP"; if ($this->_debug > 0) { print "-- Date range query:
";
       print "$sql;";
@@ -197,7 +200,7 @@ public function from() {
       $endDateFix = "AND date_added <= '" . substr($endDate, 0, 10) . " 23:59:00'";
     }
 
-    $dateRange = "INSERT INTO dates_{$this->_tableName} ( id, date_added )
+    $dateRange = "INSERT INTO {$this->_datesTable} ( id, date_added )
           SELECT
               civicrm_contact.id,
               min(civicrm_log.modified_date) AS date_added
@@ -249,16 +252,16 @@ public function from() {
         $xGroups = 0;
       }
 
-      $sql = "DROP TEMPORARY TABLE IF EXISTS Xg_{$this->_tableName}";
+      $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_xgTable}";
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
-      $sql = "CREATE TEMPORARY TABLE Xg_{$this->_tableName} ( contact_id int primary key) ENGINE=HEAP";
+      $sql = "CREATE TEMPORARY TABLE {$this->_xgTable} ( contact_id int primary key) ENGINE=HEAP";
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
 
       //used only when exclude group is selected
       if ($xGroups != 0) {
-        $excludeGroup = "INSERT INTO  Xg_{$this->_tableName} ( contact_id )
+        $excludeGroup = "INSERT INTO  {$this->_xgTable} ( contact_id )
                   SELECT  DISTINCT civicrm_group_contact.contact_id
-                  FROM civicrm_group_contact, dates_{$this->_tableName} AS d
+                  FROM civicrm_group_contact, {$this->_datesTable} AS d
                   WHERE
                      d.id = civicrm_group_contact.contact_id AND
                      civicrm_group_contact.status = 'Added' AND
@@ -277,16 +280,16 @@ public function from() {
                               SELECT contact_id FROM civicrm_group_contact
                               WHERE civicrm_group_contact.group_id = {$values} AND civicrm_group_contact.status = 'Removed')";
 
-            $smartGroupQuery = " INSERT IGNORE INTO Xg_{$this->_tableName}(contact_id) $smartSql";
+            $smartGroupQuery = " INSERT IGNORE INTO {$this->_xgTable}(contact_id) $smartSql";
 
             CRM_Core_DAO::executeQuery($smartGroupQuery, CRM_Core_DAO::$_nullArray);
           }
         }
       }
 
-      $sql = "DROP TEMPORARY TABLE IF EXISTS Ig_{$this->_tableName}";
+      $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_igTable}";
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
-      $sql = "CREATE TEMPORARY TABLE Ig_{$this->_tableName}
+      $sql = "CREATE TEMPORARY TABLE {$this->_igTable}
                 ( id int PRIMARY KEY AUTO_INCREMENT,
                   contact_id int,
                   group_names varchar(64)) ENGINE=HEAP";
@@ -299,9 +302,9 @@ public function from() {
 
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
 
-      $includeGroup = "INSERT INTO Ig_{$this->_tableName} (contact_id, group_names)
+      $includeGroup = "INSERT INTO {$this->_igTable} (contact_id, group_names)
                  SELECT      d.id as contact_id, civicrm_group.name as group_name
-                 FROM        dates_{$this->_tableName} AS d
+                 FROM        {$this->_datesTable} AS d
                  INNER JOIN  civicrm_group_contact
                  ON          civicrm_group_contact.contact_id = d.id
                  LEFT JOIN   civicrm_group
@@ -309,8 +312,8 @@ public function from() {
 
       //used only when exclude group is selected
       if ($xGroups != 0) {
-        $includeGroup .= " LEFT JOIN        Xg_{$this->_tableName}
-                                          ON        d.id = Xg_{$this->_tableName}.contact_id";
+        $includeGroup .= " LEFT JOIN        {$this->_xgTable}
+                                          ON        d.id = {$this->_xgTable}.contact_id";
       }
       $includeGroup .= " WHERE
                                      civicrm_group_contact.status = 'Added'  AND
@@ -318,7 +321,7 @@ public function from() {
 
       //used only when exclude group is selected
       if ($xGroups != 0) {
-        $includeGroup .= " AND  Xg_{$this->_tableName}.contact_id IS null";
+        $includeGroup .= " AND  {$this->_xgTable}.contact_id IS null";
       }
 
       if ($this->_debug > 0) {
@@ -339,7 +342,7 @@ public function from() {
 
           $smartSql .= " AND contact_a.id IN (
                                    SELECT id AS contact_id
-                                   FROM dates_{$this->_tableName} )";
+                                   FROM {$this->_datesTable} )";
 
           $smartSql .= " AND contact_a.id NOT IN (
                                    SELECT contact_id FROM civicrm_group_contact
@@ -347,11 +350,11 @@ public function from() {
 
           //used only when exclude group is selected
           if ($xGroups != 0) {
-            $smartSql .= " AND contact_a.id NOT IN (SELECT contact_id FROM  Xg_{$this->_tableName})";
+            $smartSql .= " AND contact_a.id NOT IN (SELECT contact_id FROM  {$this->_xgTable})";
           }
 
           $smartGroupQuery = " INSERT IGNORE INTO
-                        Ig_{$this->_tableName}(contact_id)
+                        {$this->_igTable}(contact_id)
                         $smartSql";
 
           CRM_Core_DAO::executeQuery($smartGroupQuery, CRM_Core_DAO::$_nullArray);
@@ -360,11 +363,11 @@ public function from() {
             print "$smartGroupQuery;";
             print "
"; } - $insertGroupNameQuery = "UPDATE IGNORE Ig_{$this->_tableName} + $insertGroupNameQuery = "UPDATE IGNORE {$this->_igTable} SET group_names = (SELECT title FROM civicrm_group WHERE civicrm_group.id = $values) - WHERE Ig_{$this->_tableName}.contact_id IS NOT NULL - AND Ig_{$this->_tableName}.group_names IS NULL"; + WHERE {$this->_igTable}.contact_id IS NOT NULL + AND {$this->_igTable}.group_names IS NULL"; CRM_Core_DAO::executeQuery($insertGroupNameQuery, CRM_Core_DAO::$_nullArray); if ($this->_debug > 0) { print "-- Smart group query:
";
@@ -380,12 +383,12 @@ public function from() {
 
     /* We need to join to this again to get the date_added value */
 
-    $from .= " INNER JOIN dates_{$this->_tableName} d ON (contact_a.id = d.id) {$this->_aclFrom}";
+    $from .= " INNER JOIN {$this->_datesTable} d ON (contact_a.id = d.id) {$this->_aclFrom}";
 
     // Only include groups in the search query of one or more Include OR Exclude groups has been selected.
     // CRM-6356
     if ($this->_groups) {
-      $from .= " INNER JOIN Ig_{$this->_tableName} temptable1 ON (contact_a.id = temptable1.contact_id)";
+      $from .= " INNER JOIN {$this->_igTable} temptable1 ON (contact_a.id = temptable1.contact_id)";
     }
 
     return $from;
@@ -437,13 +440,13 @@ public function count() {
 
   public function __destruct() {
     //drop the temp. tables if they exist
-    if (!empty($this->_includeGroups)) {
-      $sql = "DROP TEMPORARY TABLE IF EXISTS Ig_{$this->_tableName}";
+    if ($this->_igTable && !empty($this->_includeGroups)) {
+      $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_igTable}";
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
     }
 
-    if (!empty($this->_excludeGroups)) {
-      $sql = "DROP TEMPORARY TABLE IF EXISTS  Xg_{$this->_tableName}";
+    if ($this->_xgTable && !empty($this->_excludeGroups)) {
+      $sql = "DROP TEMPORARY TABLE IF EXISTS {$this->_xgTable}";
       CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
     }
   }

From 0038409ffccf568ad47030693f49a24b8f9cb2f5 Mon Sep 17 00:00:00 2001
From: Tim Otten 
Date: Fri, 15 Jun 2018 17:40:39 -0700
Subject: [PATCH 9/9] CRM_Utils_SQL_TempTable - Improve docblock

---
 CRM/Utils/SQL/TempTable.php | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/CRM/Utils/SQL/TempTable.php b/CRM/Utils/SQL/TempTable.php
index 8e419789b49d..cedc46c81622 100644
--- a/CRM/Utils/SQL/TempTable.php
+++ b/CRM/Utils/SQL/TempTable.php
@@ -36,10 +36,16 @@
  *   - Durable temp tables: "civicrm_tmp_d_{12}_{32}"
  *   - Ephemeral temp tables: "civicrm_tmp_e_{12}_{32}"
  *
- * Example 1: Just create table name. You'll be responsible for CREATE/DROP actions.
+ * To use `TempTable`:
+ *   - Begin by calling `CRM_Utils_SQL_TempTable::build()`.
+ *   - Optionally, describe the table with `setDurable()`, `setCategory()`, `setId()`.
+ *   - Finally, call `getName()` or `createWithQuery()` or `createWithColumns()`.
+ *
+ * Example 1: Just create a table name. You'll be responsible for CREATE/DROP actions.
  *
  * $name = CRM_Utils_SQL_TempTable::build()->getName();
  * $name = CRM_Utils_SQL_TempTable::build()->setDurable()->getName();
+ * $name = CRM_Utils_SQL_TempTable::build()->setCategory('contactstats')->setId($contact['id'])->getName();
  *
  * Example 2: Create a temp table using the results of a SELECT query.
  *