From 0801ef01829f2dd4fbff6000691fbc820a3f6081 Mon Sep 17 00:00:00 2001 From: Jared Hancock <gravydish@gmail.com> Date: Wed, 22 Aug 2018 04:50:59 +0000 Subject: [PATCH] queue: Add MySQL index hint This adds the advanced option to the queue sort configuration. An index can be specified to be used for the sorting operation. In some cases, the MySQL query optimizer cannot select the most efficient index to use when dealing with large querysets and sorting. This feature, if enabled, allows an administrator to specify an index which MySQL should use when using the sort. To use the feature, an `extra` column must be added to the `%queue_sort` table to receive the index name. --- include/class.orm.php | 19 ++++++++- include/class.queue.php | 42 +++++++++++++++++++ .../templates/queue-sorting-edit.tmpl.php | 21 ++++++++++ scp/css/scp.css | 6 +++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/include/class.orm.php b/include/class.orm.php index 473be838c..72b34c81c 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -333,6 +333,11 @@ class VerySimpleModel { return static::getMeta()->newInstance($row); } + function __wakeup() { + // If a model is stashed in a session, refresh the model from the database + $this->refetch(); + } + function get($field, $default=false) { if (array_key_exists($field, $this->ht)) return $this->ht[$field]; @@ -1142,6 +1147,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl const OPT_NOSORT = 'nosort'; const OPT_NOCACHE = 'nocache'; const OPT_MYSQL_FOUND_ROWS = 'found_rows'; + const OPT_INDEX_HINT = 'indexhint'; const ITER_MODELS = 1; const ITER_HASH = 2; @@ -1477,6 +1483,14 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl return isset($this->options[$option]); } + function getOption($option) { + return @$this->options[$option] ?: false; + } + + function setOption($option, $value) { + $this->options[$option] = $value; + } + function countSelectFields() { $count = count($this->values) + count($this->annotations); if (isset($this->extra['select'])) @@ -3083,12 +3097,15 @@ class MySqlCompiler extends SqlCompiler { $group_by = $group_by ? ' GROUP BY '.implode(', ', $group_by) : ''; $joins = $this->getJoins($queryset); + if ($hint = $queryset->getOption(QuerySet::OPT_INDEX_HINT)) { + $hint = " USE INDEX ({$hint})"; + } $sql = 'SELECT '; if ($queryset->hasOption(QuerySet::OPT_MYSQL_FOUND_ROWS)) $sql .= 'SQL_CALC_FOUND_ROWS '; $sql .= implode(', ', $fields).' FROM ' - .$table.$joins.$where.$group_by.$having.$sort; + .$table.$hint.$joins.$where.$group_by.$having.$sort; // UNIONS if ($queryset->chain) { // If the main query is sorted, it will need parentheses diff --git a/include/class.queue.php b/include/class.queue.php index 643fdb712..dca048263 100644 --- a/include/class.queue.php +++ b/include/class.queue.php @@ -2716,6 +2716,7 @@ extends VerySimpleModel { ); var $_columns; + var $_extra; function getRoot($hint=false) { switch ($hint ?: $this->root) { @@ -2733,6 +2734,12 @@ extends VerySimpleModel { return $this->id; } + function getExtra() { + if (isset($this->extra) && !isset($this->_extra)) + $this->_extra = JsonDataParser::decode($this->extra); + return $this->_extra; + } + function applySort(QuerySet $query, $reverse=false, $root=false) { $fields = CustomQueue::getSearchableFields($this->getRoot($root)); foreach ($this->getColumnPaths() as $path=>$descending) { @@ -2743,6 +2750,10 @@ extends VerySimpleModel { CustomQueue::getOrmPath($path, $query)); } } + // Add index hint if defined + if (($extra = $this->getExtra()) && isset($extra['index'])) { + $query->setOption(QuerySet::OPT_INDEX_HINT, $extra['index']); + } return $query; } @@ -2776,6 +2787,11 @@ extends VerySimpleModel { array('id' => $this->id)); } + function getAdvancedConfigForm($source=false) { + return new QueueSortAdvancedConfigForm($source ?: $this->getExtra(), + array('id' => $this->id)); + } + static function forQueue(CustomQueue $queue) { return static::objects()->filter([ 'root' => $queue->root ?: 'T', @@ -2811,6 +2827,11 @@ extends VerySimpleModel { $this->columns = JsonDataEncoder::encode($columns); } + if ($this->getExtra() !== null) { + $extra = $this->getAdvancedConfigForm($vars)->getClean(); + $this->extra = JsonDataEncoder::encode($extra); + } + if (count($errors)) return false; @@ -3070,3 +3091,24 @@ extends AbstractForm { ); } } + +class QueueSortAdvancedConfigForm +extends AbstractForm { + function getInstructions() { + return __('If unsure, leave these options blank and unset'); + } + + function buildFields() { + return array( + 'index' => new TextboxField(array( + 'label' => __('Database Index'), + 'hint' => __('Use this index when sorting on this column'), + 'required' => false, + 'layout' => new GridFluidCell(12), + 'configuration' => array( + 'placeholder' => __('Automatic'), + ), + )), + ); + } +} diff --git a/include/staff/templates/queue-sorting-edit.tmpl.php b/include/staff/templates/queue-sorting-edit.tmpl.php index 0a2d98b02..a001201a2 100644 --- a/include/staff/templates/queue-sorting-edit.tmpl.php +++ b/include/staff/templates/queue-sorting-edit.tmpl.php @@ -5,6 +5,7 @@ * $column - <QueueColumn> instance for this column */ $sortid = $sort->getId(); +$advanced = in_array('extra', $sort::getMeta()->getFieldNames()); ?> <h3 class="drag-handle"><?php echo __('Manage Sort Options'); ?> — <?php echo $sort->get('name') ?></h3> @@ -14,10 +15,30 @@ $sortid = $sort->getId(); <form method="post" action="#tickets/search/sort/edit/<?php echo $sortid; ?>"> +<?php if ($advanced) { ?> + <ul class="clean tabs"> + <li class="active"><a href="#fields"><i class="icon-columns"></i> + <?php echo __('Fields'); ?></a></li> + <li><a href="#advanced"><i class="icon-cog"></i> + <?php echo __('Advanced'); ?></a></li> + </ul> + + <div class="tab_content" id="fields"> +<?php } ?> + <?php include 'queue-sorting.tmpl.php'; ?> +<?php if ($advanced) { ?> + </div> + + <div class="hidden tab_content" id="advanced"> + <?php echo $sort->getAdvancedConfigForm()->asTable(); ?> + </div> + +<?php } ?> + <hr> <p class="full-width"> <span class="buttons pull-left"> diff --git a/scp/css/scp.css b/scp/css/scp.css index 878333317..f255ae506 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -3525,6 +3525,12 @@ table.grid.form caption { margin-bottom: 5px; } +.grid.form .field > .field-hint-text { + font-style: italic; + margin: 0 10px 5px 10px; + opacity: 0.8; +} + #basic_search { background-color: #f4f4f4; margin: -10px 0; -- GitLab