aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Display/Results.php
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Display/Results.php')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Display/Results.php5698
1 files changed, 5698 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Display/Results.php b/srcs/phpmyadmin/libraries/classes/Display/Results.php
new file mode 100644
index 0000000..0fcc5c6
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Display/Results.php
@@ -0,0 +1,5698 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Hold the PhpMyAdmin\Display\Results class
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Display;
+
+use PhpMyAdmin\Config\SpecialSchemaLinks;
+use PhpMyAdmin\Core;
+use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\Index;
+use PhpMyAdmin\Message;
+use PhpMyAdmin\Plugins\Transformations\Output\Text_Octetstream_Sql;
+use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Json;
+use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Sql;
+use PhpMyAdmin\Plugins\Transformations\Text_Plain_Link;
+use PhpMyAdmin\Plugins\TransformationsPlugin;
+use PhpMyAdmin\Relation;
+use PhpMyAdmin\Response;
+use PhpMyAdmin\Sanitize;
+use PhpMyAdmin\Sql;
+use PhpMyAdmin\SqlParser\Statements\SelectStatement;
+use PhpMyAdmin\SqlParser\Utils\Query;
+use PhpMyAdmin\Table;
+use PhpMyAdmin\Template;
+use PhpMyAdmin\Transformations;
+use PhpMyAdmin\Url;
+use PhpMyAdmin\Util;
+use stdClass;
+
+/**
+ * Handle all the functionalities related to displaying results
+ * of sql queries, stored procedure, browsing sql processes or
+ * displaying binary log.
+ *
+ * @package PhpMyAdmin
+ */
+class Results
+{
+ // Define constants
+ public const NO_EDIT_OR_DELETE = 'nn';
+ public const UPDATE_ROW = 'ur';
+ public const DELETE_ROW = 'dr';
+ public const KILL_PROCESS = 'kp';
+
+ public const POSITION_LEFT = 'left';
+ public const POSITION_RIGHT = 'right';
+ public const POSITION_BOTH = 'both';
+ public const POSITION_NONE = 'none';
+
+ public const DISPLAY_FULL_TEXT = 'F';
+ public const DISPLAY_PARTIAL_TEXT = 'P';
+
+ public const HEADER_FLIP_TYPE_AUTO = 'auto';
+ public const HEADER_FLIP_TYPE_CSS = 'css';
+ public const HEADER_FLIP_TYPE_FAKE = 'fake';
+
+ public const DATE_FIELD = 'date';
+ public const DATETIME_FIELD = 'datetime';
+ public const TIMESTAMP_FIELD = 'timestamp';
+ public const TIME_FIELD = 'time';
+ public const STRING_FIELD = 'string';
+ public const GEOMETRY_FIELD = 'geometry';
+ public const BLOB_FIELD = 'BLOB';
+ public const BINARY_FIELD = 'BINARY';
+
+ public const RELATIONAL_KEY = 'K';
+ public const RELATIONAL_DISPLAY_COLUMN = 'D';
+
+ public const GEOMETRY_DISP_GEOM = 'GEOM';
+ public const GEOMETRY_DISP_WKT = 'WKT';
+ public const GEOMETRY_DISP_WKB = 'WKB';
+
+ public const SMART_SORT_ORDER = 'SMART';
+ public const ASCENDING_SORT_DIR = 'ASC';
+ public const DESCENDING_SORT_DIR = 'DESC';
+
+ public const TABLE_TYPE_INNO_DB = 'InnoDB';
+ public const ALL_ROWS = 'all';
+ public const QUERY_TYPE_SELECT = 'SELECT';
+
+ public const ROUTINE_PROCEDURE = 'procedure';
+ public const ROUTINE_FUNCTION = 'function';
+
+ public const ACTION_LINK_CONTENT_ICONS = 'icons';
+ public const ACTION_LINK_CONTENT_TEXT = 'text';
+
+ // Declare global fields
+
+ /** array with properties of the class */
+ private $_property_array = [
+
+ /** integer server id */
+ 'server' => null,
+
+ /** string Database name */
+ 'db' => null,
+
+ /** string Table name */
+ 'table' => null,
+
+ /** string the URL to go back in case of errors */
+ 'goto' => null,
+
+ /** string the SQL query */
+ 'sql_query' => null,
+
+ /**
+ * integer the total number of rows returned by the SQL query without any
+ * appended "LIMIT" clause programmatically
+ */
+ 'unlim_num_rows' => null,
+
+ /** array meta information about fields */
+ 'fields_meta' => null,
+
+ /** boolean */
+ 'is_count' => null,
+
+ /** integer */
+ 'is_export' => null,
+
+ /** boolean */
+ 'is_func' => null,
+
+ /** integer */
+ 'is_analyse' => null,
+
+ /** integer the total number of rows returned by the SQL query */
+ 'num_rows' => null,
+
+ /** integer the total number of fields returned by the SQL query */
+ 'fields_cnt' => null,
+
+ /** double time taken for execute the SQL query */
+ 'querytime' => null,
+
+ /** string path for theme images directory */
+ 'pma_theme_image' => null,
+
+ /** string */
+ 'text_dir' => null,
+
+ /** boolean */
+ 'is_maint' => null,
+
+ /** boolean */
+ 'is_explain' => null,
+
+ /** boolean */
+ 'is_show' => null,
+
+ /** boolean */
+ 'is_browse_distinct' => null,
+
+ /** array table definitions */
+ 'showtable' => null,
+
+ /** string */
+ 'printview' => null,
+
+ /** string URL query */
+ 'url_query' => null,
+
+ /** array column names to highlight */
+ 'highlight_columns' => null,
+
+ /** array holding various display information */
+ 'display_params' => null,
+
+ /** array mime types information of fields */
+ 'mime_map' => null,
+
+ /** boolean */
+ 'editable' => null,
+
+ /** random unique ID to distinguish result set */
+ 'unique_id' => null,
+
+ /** where clauses for each row, each table in the row */
+ 'whereClauseMap' => [],
+ ];
+
+ /**
+ * This variable contains the column transformation information
+ * for some of the system databases.
+ * One element of this array represent all relevant columns in all tables in
+ * one specific database
+ */
+ public $transformation_info;
+
+ /**
+ * @var Relation
+ */
+ private $relation;
+
+ /**
+ * @var Transformations
+ */
+ private $transformations;
+
+ /**
+ * @var Template
+ */
+ public $template;
+
+ /**
+ * Constructor for PhpMyAdmin\Display\Results class
+ *
+ * @param string $db the database name
+ * @param string $table the table name
+ * @param int $server the server id
+ * @param string $goto the URL to go back in case of errors
+ * @param string $sql_query the SQL query
+ *
+ * @access public
+ */
+ public function __construct($db, $table, $server, $goto, $sql_query)
+ {
+ $this->relation = new Relation($GLOBALS['dbi']);
+ $this->transformations = new Transformations();
+ $this->template = new Template();
+
+ $this->_setDefaultTransformations();
+
+ $this->__set('db', $db);
+ $this->__set('table', $table);
+ $this->__set('server', $server);
+ $this->__set('goto', $goto);
+ $this->__set('sql_query', $sql_query);
+ $this->__set('unique_id', mt_rand());
+ }
+
+ /**
+ * Get any property of this class
+ *
+ * @param string $property name of the property
+ *
+ * @return mixed|void if property exist, value of the relevant property
+ */
+ public function __get($property)
+ {
+ return $this->_property_array[$property] ?? null;
+ }
+
+ /**
+ * Set values for any property of this class
+ *
+ * @param string $property name of the property
+ * @param mixed $value value to set
+ *
+ * @return void
+ */
+ public function __set($property, $value)
+ {
+ if (array_key_exists($property, $this->_property_array)) {
+ $this->_property_array[$property] = $value;
+ }
+ }
+
+ /**
+ * Sets default transformations for some columns
+ *
+ * @return void
+ */
+ private function _setDefaultTransformations()
+ {
+ $json_highlighting_data = [
+ 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Json.php',
+ Text_Plain_Json::class,
+ 'Text_Plain',
+ ];
+ $sql_highlighting_data = [
+ 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Sql.php',
+ Text_Plain_Sql::class,
+ 'Text_Plain',
+ ];
+ $blob_sql_highlighting_data = [
+ 'libraries/classes/Plugins/Transformations/Output/Text_Octetstream_Sql.php',
+ Text_Octetstream_Sql::class,
+ 'Text_Octetstream',
+ ];
+ $link_data = [
+ 'libraries/classes/Plugins/Transformations/Text_Plain_Link.php',
+ Text_Plain_Link::class,
+ 'Text_Plain',
+ ];
+ $this->transformation_info = [
+ 'information_schema' => [
+ 'events' => [
+ 'event_definition' => $sql_highlighting_data,
+ ],
+ 'processlist' => [
+ 'info' => $sql_highlighting_data,
+ ],
+ 'routines' => [
+ 'routine_definition' => $sql_highlighting_data,
+ ],
+ 'triggers' => [
+ 'action_statement' => $sql_highlighting_data,
+ ],
+ 'views' => [
+ 'view_definition' => $sql_highlighting_data,
+ ],
+ ],
+ 'mysql' => [
+ 'event' => [
+ 'body' => $blob_sql_highlighting_data,
+ 'body_utf8' => $blob_sql_highlighting_data,
+ ],
+ 'general_log' => [
+ 'argument' => $sql_highlighting_data,
+ ],
+ 'help_category' => [
+ 'url' => $link_data,
+ ],
+ 'help_topic' => [
+ 'example' => $sql_highlighting_data,
+ 'url' => $link_data,
+ ],
+ 'proc' => [
+ 'param_list' => $blob_sql_highlighting_data,
+ 'returns' => $blob_sql_highlighting_data,
+ 'body' => $blob_sql_highlighting_data,
+ 'body_utf8' => $blob_sql_highlighting_data,
+ ],
+ 'slow_log' => [
+ 'sql_text' => $sql_highlighting_data,
+ ],
+ ],
+ ];
+
+ $cfgRelation = $this->relation->getRelationsParam();
+ if ($cfgRelation['db']) {
+ $this->transformation_info[$cfgRelation['db']] = [];
+ $relDb = &$this->transformation_info[$cfgRelation['db']];
+ if (! empty($cfgRelation['history'])) {
+ $relDb[$cfgRelation['history']] = [
+ 'sqlquery' => $sql_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['bookmark'])) {
+ $relDb[$cfgRelation['bookmark']] = [
+ 'query' => $sql_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['tracking'])) {
+ $relDb[$cfgRelation['tracking']] = [
+ 'schema_sql' => $sql_highlighting_data,
+ 'data_sql' => $sql_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['favorite'])) {
+ $relDb[$cfgRelation['favorite']] = [
+ 'tables' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['recent'])) {
+ $relDb[$cfgRelation['recent']] = [
+ 'tables' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['savedsearches'])) {
+ $relDb[$cfgRelation['savedsearches']] = [
+ 'search_data' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['designer_settings'])) {
+ $relDb[$cfgRelation['designer_settings']] = [
+ 'settings_data' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['table_uiprefs'])) {
+ $relDb[$cfgRelation['table_uiprefs']] = [
+ 'prefs' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['userconfig'])) {
+ $relDb[$cfgRelation['userconfig']] = [
+ 'config_data' => $json_highlighting_data,
+ ];
+ }
+ if (! empty($cfgRelation['export_templates'])) {
+ $relDb[$cfgRelation['export_templates']] = [
+ 'template_data' => $json_highlighting_data,
+ ];
+ }
+ }
+ }
+
+ /**
+ * Set properties which were not initialized at the constructor
+ *
+ * @param integer $unlim_num_rows the total number of rows returned by
+ * the SQL query without any appended
+ * "LIMIT" clause programmatically
+ * @param stdClass $fields_meta meta information about fields
+ * @param boolean $is_count statement is SELECT COUNT
+ * @param integer $is_export statement contains INTO OUTFILE
+ * @param boolean $is_func statement contains a function like SUM()
+ * @param integer $is_analyse statement contains PROCEDURE ANALYSE
+ * @param integer $num_rows total no. of rows returned by SQL query
+ * @param integer $fields_cnt total no.of fields returned by SQL query
+ * @param double $querytime time taken for execute the SQL query
+ * @param string $pmaThemeImage path for theme images directory
+ * @param string $text_dir text direction
+ * @param boolean $is_maint statement contains a maintenance command
+ * @param boolean $is_explain statement contains EXPLAIN
+ * @param boolean $is_show statement contains SHOW
+ * @param array $showtable table definitions
+ * @param string $printview print view was requested
+ * @param string $url_query URL query
+ * @param boolean $editable whether the results set is editable
+ * @param boolean $is_browse_dist whether browsing distinct values
+ *
+ * @return void
+ *
+ * @see sql.php
+ */
+ public function setProperties(
+ $unlim_num_rows,
+ $fields_meta,
+ $is_count,
+ $is_export,
+ $is_func,
+ $is_analyse,
+ $num_rows,
+ $fields_cnt,
+ $querytime,
+ $pmaThemeImage,
+ $text_dir,
+ $is_maint,
+ $is_explain,
+ $is_show,
+ $showtable,
+ $printview,
+ $url_query,
+ $editable,
+ $is_browse_dist
+ ) {
+
+ $this->__set('unlim_num_rows', $unlim_num_rows);
+ $this->__set('fields_meta', $fields_meta);
+ $this->__set('is_count', $is_count);
+ $this->__set('is_export', $is_export);
+ $this->__set('is_func', $is_func);
+ $this->__set('is_analyse', $is_analyse);
+ $this->__set('num_rows', $num_rows);
+ $this->__set('fields_cnt', $fields_cnt);
+ $this->__set('querytime', $querytime);
+ $this->__set('pma_theme_image', $pmaThemeImage);
+ $this->__set('text_dir', $text_dir);
+ $this->__set('is_maint', $is_maint);
+ $this->__set('is_explain', $is_explain);
+ $this->__set('is_show', $is_show);
+ $this->__set('showtable', $showtable);
+ $this->__set('printview', $printview);
+ $this->__set('url_query', $url_query);
+ $this->__set('editable', $editable);
+ $this->__set('is_browse_distinct', $is_browse_dist);
+ }
+
+ /**
+ * Defines the parts to display for a print view
+ *
+ * @param array $displayParts the parts to display
+ *
+ * @return array the modified display parts
+ *
+ * @access private
+ *
+ */
+ private function _setDisplayPartsForPrintView(array $displayParts)
+ {
+ // set all elements to false!
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
+ $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
+ $displayParts['sort_lnk'] = (string) '0';
+ $displayParts['nav_bar'] = (string) '0';
+ $displayParts['bkm_form'] = (string) '0';
+ $displayParts['text_btn'] = (string) '0';
+ $displayParts['pview_lnk'] = (string) '0';
+
+ return $displayParts;
+ }
+
+ /**
+ * Defines the parts to display for a SHOW statement
+ *
+ * @param array $displayParts the parts to display
+ *
+ * @return array the modified display parts
+ *
+ * @access private
+ *
+ */
+ private function _setDisplayPartsForShow(array $displayParts)
+ {
+ preg_match(
+ '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?'
+ . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS'
+ . ')@i',
+ $this->__get('sql_query'),
+ $which
+ );
+
+ $bIsProcessList = isset($which[1]);
+ if ($bIsProcessList) {
+ $str = ' ' . strtoupper($which[1]);
+ $bIsProcessList = $bIsProcessList
+ && strpos($str, 'PROCESSLIST') > 0;
+ }
+
+ if ($bIsProcessList) {
+ // no edit link
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
+ // "kill process" type edit link
+ $displayParts['del_lnk'] = self::KILL_PROCESS;
+ } else {
+ // Default case -> no links
+ // no edit link
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
+ // no delete link
+ $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
+ }
+ // Other settings
+ $displayParts['sort_lnk'] = (string) '0';
+ $displayParts['nav_bar'] = (string) '0';
+ $displayParts['bkm_form'] = (string) '1';
+ $displayParts['text_btn'] = (string) '1';
+ $displayParts['pview_lnk'] = (string) '1';
+
+ return $displayParts;
+ }
+
+ /**
+ * Defines the parts to display for statements not related to data
+ *
+ * @param array $displayParts the parts to display
+ *
+ * @return array the modified display parts
+ *
+ * @access private
+ *
+ */
+ private function _setDisplayPartsForNonData(array $displayParts)
+ {
+ // Statement is a "SELECT COUNT", a
+ // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or
+ // contains a "PROC ANALYSE" part
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link
+ $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link
+ $displayParts['sort_lnk'] = (string) '0';
+ $displayParts['nav_bar'] = (string) '0';
+ $displayParts['bkm_form'] = (string) '1';
+
+ if ($this->__get('is_maint')) {
+ $displayParts['text_btn'] = (string) '1';
+ } else {
+ $displayParts['text_btn'] = (string) '0';
+ }
+ $displayParts['pview_lnk'] = (string) '1';
+
+ return $displayParts;
+ }
+
+ /**
+ * Defines the parts to display for other statements (probably SELECT)
+ *
+ * @param array $displayParts the parts to display
+ *
+ * @return array the modified display parts
+ *
+ * @access private
+ *
+ */
+ private function _setDisplayPartsForSelect(array $displayParts)
+ {
+ // Other statements (ie "SELECT" ones) -> updates
+ // $displayParts['edit_lnk'], $displayParts['del_lnk'] and
+ // $displayParts['text_btn'] (keeps other default values)
+
+ $fields_meta = $this->__get('fields_meta');
+ $prev_table = '';
+ $displayParts['text_btn'] = (string) '1';
+ $number_of_columns = $this->__get('fields_cnt');
+
+ for ($i = 0; $i < $number_of_columns; $i++) {
+ $is_link = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE)
+ || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)
+ || ($displayParts['sort_lnk'] != '0');
+
+ // Displays edit/delete/sort/insert links?
+ if ($is_link
+ && $prev_table != ''
+ && $fields_meta[$i]->table != ''
+ && $fields_meta[$i]->table != $prev_table
+ ) {
+ // don't display links
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
+ $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
+ /**
+ * @todo May be problematic with same field names
+ * in two joined table.
+ */
+ // $displayParts['sort_lnk'] = (string) '0';
+ if ($displayParts['text_btn'] == '1') {
+ break;
+ }
+ } // end if
+
+ // Always display print view link
+ $displayParts['pview_lnk'] = (string) '1';
+ if ($fields_meta[$i]->table != '') {
+ $prev_table = $fields_meta[$i]->table;
+ }
+ } // end for
+
+ if ($prev_table == '') { // no table for any of the columns
+ // don't display links
+ $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE;
+ $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE;
+ }
+
+ return $displayParts;
+ }
+
+ /**
+ * Defines the parts to display for the results of a SQL query
+ * and the total number of rows
+ *
+ * @param array $displayParts the parts to display (see a few
+ * lines above for explanations)
+ *
+ * @return array the first element is an array with explicit indexes
+ * for all the display elements
+ * the second element is the total number of rows returned
+ * by the SQL query without any programmatically appended
+ * LIMIT clause (just a copy of $unlim_num_rows if it exists,
+ * else computed inside this function)
+ *
+ *
+ * @access private
+ *
+ * @see getTable()
+ */
+ private function _setDisplayPartsAndTotal(array $displayParts)
+ {
+ $the_total = 0;
+
+ // 1. Following variables are needed for use in isset/empty or
+ // use with array indexes or safe use in foreach
+ $db = $this->__get('db');
+ $table = $this->__get('table');
+ $unlim_num_rows = $this->__get('unlim_num_rows');
+ $num_rows = $this->__get('num_rows');
+ $printview = $this->__get('printview');
+
+ // 2. Updates the display parts
+ if ($printview == '1') {
+ $displayParts = $this->_setDisplayPartsForPrintView($displayParts);
+ } elseif ($this->__get('is_count') || $this->__get('is_analyse')
+ || $this->__get('is_maint') || $this->__get('is_explain')
+ ) {
+ $displayParts = $this->_setDisplayPartsForNonData($displayParts);
+ } elseif ($this->__get('is_show')) {
+ $displayParts = $this->_setDisplayPartsForShow($displayParts);
+ } else {
+ $displayParts = $this->_setDisplayPartsForSelect($displayParts);
+ } // end if..elseif...else
+
+ // 3. Gets the total number of rows if it is unknown
+ if (isset($unlim_num_rows) && $unlim_num_rows != '') {
+ $the_total = $unlim_num_rows;
+ } elseif (($displayParts['nav_bar'] == '1')
+ || ($displayParts['sort_lnk'] == '1')
+ && (strlen($db) > 0 && strlen($table) > 0)
+ ) {
+ $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords();
+ }
+
+ // if for COUNT query, number of rows returned more than 1
+ // (may be being used GROUP BY)
+ if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) {
+ $displayParts['nav_bar'] = (string) '1';
+ $displayParts['sort_lnk'] = (string) '1';
+ }
+ // 4. If navigation bar or sorting fields names URLs should be
+ // displayed but there is only one row, change these settings to
+ // false
+ if ($displayParts['nav_bar'] == '1' || $displayParts['sort_lnk'] == '1') {
+ // - Do not display sort links if less than 2 rows.
+ // - For a VIEW we (probably) did not count the number of rows
+ // so don't test this number here, it would remove the possibility
+ // of sorting VIEW results.
+ $_table = new Table($table, $db);
+ if (isset($unlim_num_rows)
+ && ($unlim_num_rows < 2)
+ && ! $_table->isView()
+ ) {
+ $displayParts['sort_lnk'] = (string) '0';
+ }
+ } // end if (3)
+
+ return [
+ $displayParts,
+ $the_total,
+ ];
+ }
+
+ /**
+ * Return true if we are executing a query in the form of
+ * "SELECT * FROM <a table> ..."
+ *
+ * @param array $analyzed_sql_results analyzed sql results
+ *
+ * @return boolean
+ *
+ * @access private
+ *
+ * @see _getTableHeaders(), _getColumnParams()
+ */
+ private function _isSelect(array $analyzed_sql_results)
+ {
+ return ! ($this->__get('is_count')
+ || $this->__get('is_export')
+ || $this->__get('is_func')
+ || $this->__get('is_analyse'))
+ && ! empty($analyzed_sql_results['select_from'])
+ && ! empty($analyzed_sql_results['statement']->from)
+ && (count($analyzed_sql_results['statement']->from) === 1)
+ && ! empty($analyzed_sql_results['statement']->from[0]->table);
+ }
+
+ /**
+ * Get a navigation button
+ *
+ * @param string $caption iconic caption for button
+ * @param string $title text for button
+ * @param integer $pos position for next query
+ * @param string $html_sql_query query ready for display
+ * @param boolean $back whether 'begin' or 'previous'
+ * @param string $onsubmit optional onsubmit clause
+ * @param string $input_for_real_end optional hidden field for special treatment
+ * @param string $onclick optional onclick clause
+ *
+ * @return string html content
+ *
+ * @access private
+ *
+ * @see _getMoveBackwardButtonsForTableNavigation(),
+ * _getMoveForwardButtonsForTableNavigation()
+ */
+ private function _getTableNavigationButton(
+ $caption,
+ $title,
+ $pos,
+ $html_sql_query,
+ $back,
+ $onsubmit = '',
+ $input_for_real_end = '',
+ $onclick = ''
+ ) {
+ $caption_output = '';
+ if ($back) {
+ if (Util::showIcons('TableNavigationLinksMode')) {
+ $caption_output .= $caption;
+ }
+ if (Util::showText('TableNavigationLinksMode')) {
+ $caption_output .= '&nbsp;' . $title;
+ }
+ } else {
+ if (Util::showText('TableNavigationLinksMode')) {
+ $caption_output .= $title;
+ }
+ if (Util::showIcons('TableNavigationLinksMode')) {
+ $caption_output .= '&nbsp;' . $caption;
+ }
+ }
+
+ return $this->template->render('display/results/table_navigation_button', [
+ 'db' => $this->__get('db'),
+ 'table' => $this->__get('table'),
+ 'sql_query' => $html_sql_query,
+ 'pos' => $pos,
+ 'is_browse_distinct' => $this->__get('is_browse_distinct'),
+ 'goto' => $this->__get('goto'),
+ 'input_for_real_end' => $input_for_real_end,
+ 'caption_output' => $caption_output,
+ 'title' => $title,
+ 'onsubmit' => $onsubmit,
+ 'onclick' => $onclick,
+ ]);
+ }
+
+ /**
+ * Possibly return a page selector for table navigation
+ *
+ * @return array ($output, $nbTotalPage)
+ *
+ * @access private
+ */
+ private function _getHtmlPageSelector(): array
+ {
+ $pageNow = @floor(
+ $_SESSION['tmpval']['pos']
+ / $_SESSION['tmpval']['max_rows']
+ ) + 1;
+
+ $nbTotalPage = @ceil(
+ $this->__get('unlim_num_rows')
+ / $_SESSION['tmpval']['max_rows']
+ );
+
+ $output = '';
+ if ($nbTotalPage > 1) {
+ $_url_params = [
+ 'db' => $this->__get('db'),
+ 'table' => $this->__get('table'),
+ 'sql_query' => $this->__get('sql_query'),
+ 'goto' => $this->__get('goto'),
+ 'is_browse_distinct' => $this->__get('is_browse_distinct'),
+ ];
+
+ $output = $this->template->render('display/results/page_selector', [
+ 'url_params' => $_url_params,
+ 'page_selector' => Util::pageselector(
+ 'pos',
+ $_SESSION['tmpval']['max_rows'],
+ $pageNow,
+ $nbTotalPage,
+ 200,
+ 5,
+ 5,
+ 20,
+ 10
+ ),
+ ]);
+ }
+ return [
+ $output,
+ $nbTotalPage,
+ ];
+ }
+
+ /**
+ * Get a navigation bar to browse among the results of a SQL query
+ *
+ * @param integer $posNext the offset for the "next" page
+ * @param integer $posPrevious the offset for the "previous" page
+ * @param boolean $isInnodb whether its InnoDB or not
+ * @param string $sortByKeyHtml the sort by key dialog
+ *
+ * @return string html content
+ *
+ * @access private
+ *
+ * @see getTable()
+ */
+ private function _getTableNavigation(
+ $posNext,
+ $posPrevious,
+ $isInnodb,
+ $sortByKeyHtml
+ ): string {
+ $isShowingAll = $_SESSION['tmpval']['max_rows'] === self::ALL_ROWS;
+
+ // Move to the beginning or to the previous page
+ $moveBackwardButtons = '';
+ if ($_SESSION['tmpval']['pos'] && ! $isShowingAll) {
+ $moveBackwardButtons = $this->_getMoveBackwardButtonsForTableNavigation(
+ htmlspecialchars($this->__get('sql_query')),
+ $posPrevious
+ );
+ }
+
+ $pageSelector = '';
+ $numberTotalPage = 1;
+ if (! $isShowingAll) {
+ list(
+ $pageSelector,
+ $numberTotalPage
+ ) = $this->_getHtmlPageSelector();
+ }
+
+ // Move to the next page or to the last one
+ $moveForwardButtons = '';
+ if ($this->__get('unlim_num_rows') === false // view with unknown number of rows
+ || (! $isShowingAll
+ && $_SESSION['tmpval']['pos'] + $_SESSION['tmpval']['max_rows'] < $this->__get('unlim_num_rows')
+ && $this->__get('num_rows') >= $_SESSION['tmpval']['max_rows'])
+ ) {
+ $moveForwardButtons = $this->_getMoveForwardButtonsForTableNavigation(
+ htmlspecialchars($this->__get('sql_query')),
+ $posNext,
+ $isInnodb
+ );
+ }
+
+ $hiddenFields = [
+ 'db' => $this->__get('db'),
+ 'table' => $this->__get('table'),
+ 'server' => $this->__get('server'),
+ 'sql_query' => $this->__get('sql_query'),
+ 'is_browse_distinct' => $this->__get('is_browse_distinct'),
+ 'goto' => $this->__get('goto'),
+ ];
+
+ return $this->template->render('display/results/table_navigation', [
+ 'move_backward_buttons' => $moveBackwardButtons,
+ 'page_selector' => $pageSelector,
+ 'move_forward_buttons' => $moveForwardButtons,
+ 'number_total_page' => $numberTotalPage,
+ 'has_show_all' => $GLOBALS['cfg']['ShowAll'] || ($this->__get('unlim_num_rows') <= 500),
+ 'hidden_fields' => $hiddenFields,
+ 'session_max_rows' => $isShowingAll ? $GLOBALS['cfg']['MaxRows'] : 'all',
+ 'unique_id' => $this->__get('unique_id'),
+ 'is_showing_all' => $isShowingAll,
+ 'unlim_num_rows' => $this->__get('unlim_num_rows'),
+ 'max_rows' => $_SESSION['tmpval']['max_rows'],
+ 'pos' => $_SESSION['tmpval']['pos'],
+ 'sort_by_key' => $sortByKeyHtml,
+ ]);
+ }
+
+ /**
+ * Prepare move backward buttons - previous and first
+ *
+ * @param string $html_sql_query the sql encoded by html special characters
+ * @param integer $pos_prev the offset for the "previous" page
+ *
+ * @return string html content
+ *
+ * @access private
+ *
+ * @see _getTableNavigation()
+ */
+ private function _getMoveBackwardButtonsForTableNavigation(
+ $html_sql_query,
+ $pos_prev
+ ) {
+ return $this->_getTableNavigationButton(
+ '&lt;&lt;',
+ _pgettext('First page', 'Begin'),
+ 0,
+ $html_sql_query,
+ true
+ )
+ . $this->_getTableNavigationButton(
+ '&lt;',
+ _pgettext('Previous page', 'Previous'),
+ $pos_prev,
+ $html_sql_query,
+ true
+ );
+ }
+
+ /**
+ * Prepare move forward buttons - next and last
+ *
+ * @param string $html_sql_query the sql encoded by htmlspecialchars()
+ * @param integer $pos_next the offset for the "next" page
+ * @param boolean $is_innodb whether it's InnoDB or not
+ *
+ * @return string html content
+ *
+ * @access private
+ *
+ * @see _getTableNavigation()
+ */
+ private function _getMoveForwardButtonsForTableNavigation(
+ $html_sql_query,
+ $pos_next,
+ $is_innodb
+ ) {
+ // display the Next button
+ $buttons_html = $this->_getTableNavigationButton(
+ '&gt;',
+ _pgettext('Next page', 'Next'),
+ $pos_next,
+ $html_sql_query,
+ false
+ );
+
+ // prepare some options for the End button
+ if ($is_innodb
+ && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount']
+ ) {
+ $input_for_real_end = '<input id="real_end_input" type="hidden" '
+ . 'name="find_real_end" value="1">';
+ // no backquote around this message
+ $onclick = '';
+ } else {
+ $input_for_real_end = $onclick = '';
+ }
+
+ $maxRows = $_SESSION['tmpval']['max_rows'];
+ $onsubmit = 'onsubmit="return '
+ . ($_SESSION['tmpval']['pos']
+ + $maxRows
+ < $this->__get('unlim_num_rows')
+ && $this->__get('num_rows') >= $maxRows
+ ? 'true'
+ : 'false') . '"';
+
+ // display the End button
+ $buttons_html .= $this->_getTableNavigationButton(
+ '&gt;&gt;',
+ _pgettext('Last page', 'End'),
+ @((ceil(