diff options
author | Hugues Peccatte <hugues.peccatte@gmail.com> | 2014-02-23 17:52:48 +0400 |
---|---|---|
committer | Hugues Peccatte <hugues.peccatte@gmail.com> | 2014-02-23 17:52:48 +0400 |
commit | 8b84cf0d91669b4a359b2f20bb014a95d7b64013 (patch) | |
tree | c06a07397d770855df4eb869177b3371abc536d4 | |
parent | b7f5acff6b5c7bcc61cea2808a0bedc0bc317aa5 (diff) |
Feature #569 Load/Save Query By Example
Implement possibility to save/load/delete a QBE on a DB.
Signed-off-by: Hugues Peccatte <hugues.peccatte@gmail.com>
-rw-r--r-- | config.sample.inc.php | 3 | ||||
-rw-r--r-- | db_qbe.php | 43 | ||||
-rw-r--r-- | examples/config.manyhosts.inc.php | 1 | ||||
-rw-r--r-- | examples/create_tables.sql | 84 | ||||
-rw-r--r-- | examples/create_tables_drizzle.sql | 72 | ||||
-rw-r--r-- | js/db_qbe.js | 55 | ||||
-rw-r--r-- | libraries/DBQbe.class.php | 223 | ||||
-rw-r--r-- | libraries/SavedSearches.php | 397 | ||||
-rw-r--r-- | libraries/config.default.php | 9 | ||||
-rw-r--r-- | libraries/config/setup.forms.php | 1 | ||||
-rw-r--r-- | libraries/relation.lib.php | 8 | ||||
-rw-r--r-- | test/libraries/PMA_relation_cleanup_test.php | 1 |
12 files changed, 796 insertions, 101 deletions
diff --git a/config.sample.inc.php b/config.sample.inc.php index 83fe50cb96..2b3024d12d 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -60,6 +60,7 @@ $cfg['Servers'][$i]['AllowNoPassword'] = false; // $cfg['Servers'][$i]['users'] = 'pma__users'; // $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups'; // $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding'; +// $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches'; /* Contrib / Swekey authentication */ // $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; @@ -145,7 +146,7 @@ $cfg['SaveDir'] = ''; /** * Should error reporting be enabled for JavaScript errors * - * default = 'ask' + * default = 'ask' */ //$cfg['SendErrorReports'] = 'ask'; diff --git a/db_qbe.php b/db_qbe.php index e1ed199d1f..37845edbb4 100644 --- a/db_qbe.php +++ b/db_qbe.php @@ -19,6 +19,47 @@ $response = PMA_Response::getInstance(); // Gets the relation settings $cfgRelation = PMA_getRelationsParam(); +$savedSearchList = array(); +$currentSearchId = null; +if ($cfgRelation['savedsearcheswork']) { + include 'libraries/SavedSearches.php'; + $header = $response->getHeader(); + $scripts = $header->getScripts(); + $scripts->addFile('db_qbe.js'); + + //Get saved search list. + $savedSearch = new PMA_SavedSearches($GLOBALS); + $savedSearch->setId($_REQUEST['searchId']) + ->setUsername($GLOBALS['cfg']['Server']['user']) + ->setDbname($_REQUEST['db']) + ->setSearchName($_REQUEST['searchName']); + + //Criterias field is filled only when clicking on "Save search". + if (!empty($_REQUEST['action'])) { + if ('save' === $_REQUEST['action']) { + $saveResult = $savedSearch->setCriterias($_REQUEST) + ->save(); + /*if (!$saveResult) { + $response->addHTML('raté'); + exit(); + }*/ + } elseif ('delete' === $_REQUEST['action']) { + $deleteResult = $savedSearch->delete(); + //After deletion, reset search. + $savedSearch = new PMA_SavedSearches($GLOBALS); + $savedSearch->setUsername($GLOBALS['cfg']['Server']['user']) + ->setDbname($_REQUEST['db']); + $_REQUEST = array(); + } elseif ('load' === $_REQUEST['action']) { + $loadResult = $savedSearch->load(); + } + //Else, it's an "update query" + } + + $savedSearchList = $savedSearch->getList(); + $currentSearchId = $savedSearch->getId(); +} + /** * A query has been submitted -> (maybe) execute it */ @@ -52,7 +93,7 @@ if ($message_to_display) { unset($message_to_display); // create new qbe search instance -$db_qbe = new PMA_DBQbe($GLOBALS['db']); +$db_qbe = new PMA_DBQbe($GLOBALS['db'], $savedSearchList, $savedSearch); /** * Displays the Query by example form diff --git a/examples/config.manyhosts.inc.php b/examples/config.manyhosts.inc.php index b188aa8b86..bced1fca2c 100644 --- a/examples/config.manyhosts.inc.php +++ b/examples/config.manyhosts.inc.php @@ -47,4 +47,5 @@ foreach ($hosts as $host) { $cfg['Servers'][$i]['users'] = 'pma__users'; $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups'; $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding'; + $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches'; } diff --git a/examples/create_tables.sql b/examples/create_tables.sql index 0ac5654dc4..8ffbf5ef84 100644 --- a/examples/create_tables.sql +++ b/examples/create_tables.sql @@ -1,40 +1,40 @@ -- -------------------------------------------------------- -- SQL Commands to set up the pmadb as described in the documentation. --- +-- -- This file is meant for use with MySQL 5 and above! --- +-- -- This script expects the user pma to already be existing. If we would put a -- line here to create him too many users might just use this script and end -- up with having the same password for the controluser. --- --- This user "pma" must be defined in config.inc.php (controluser/controlpass) --- --- Please don't forget to set up the tablenames in config.inc.php --- +-- +-- This user "pma" must be defined in config.inc.php (controluser/controlpass) +-- +-- Please don't forget to set up the tablenames in config.inc.php +-- -- -------------------------------------------------------- --- +-- -- Database : `phpmyadmin` --- +-- CREATE DATABASE IF NOT EXISTS `phpmyadmin` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; USE phpmyadmin; -- -------------------------------------------------------- --- +-- -- Privileges --- +-- -- (activate this statement if necessary) -- GRANT SELECT, INSERT, DELETE, UPDATE ON `phpmyadmin`.* TO -- 'pma'@localhost; -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__bookmark` --- +-- CREATE TABLE IF NOT EXISTS `pma__bookmark` ( `id` int(11) NOT NULL auto_increment, @@ -49,9 +49,9 @@ CREATE TABLE IF NOT EXISTS `pma__bookmark` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__column_info` --- +-- CREATE TABLE IF NOT EXISTS `pma__column_info` ( `id` int(5) unsigned NOT NULL auto_increment, @@ -70,9 +70,9 @@ CREATE TABLE IF NOT EXISTS `pma__column_info` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__history` --- +-- CREATE TABLE IF NOT EXISTS `pma__history` ( `id` bigint(20) unsigned NOT NULL auto_increment, @@ -89,9 +89,9 @@ CREATE TABLE IF NOT EXISTS `pma__history` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__pdf_pages` --- +-- CREATE TABLE IF NOT EXISTS `pma__pdf_pages` ( `db_name` varchar(64) NOT NULL default '', @@ -136,9 +136,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_uiprefs` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__relation` --- +-- CREATE TABLE IF NOT EXISTS `pma__relation` ( `master_db` varchar(64) NOT NULL default '', @@ -155,9 +155,9 @@ CREATE TABLE IF NOT EXISTS `pma__relation` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__table_coords` --- +-- CREATE TABLE IF NOT EXISTS `pma__table_coords` ( `db_name` varchar(64) NOT NULL default '', @@ -172,9 +172,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_coords` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__table_info` --- +-- CREATE TABLE IF NOT EXISTS `pma__table_info` ( `db_name` varchar(64) NOT NULL default '', @@ -187,9 +187,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_info` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__designer_coords` --- +-- CREATE TABLE IF NOT EXISTS `pma__designer_coords` ( `db_name` varchar(64) NOT NULL default '', @@ -205,9 +205,9 @@ CREATE TABLE IF NOT EXISTS `pma__designer_coords` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__tracking` --- +-- CREATE TABLE IF NOT EXISTS `pma__tracking` ( `db_name` varchar(64) NOT NULL, @@ -250,7 +250,7 @@ CREATE TABLE IF NOT EXISTS `pma__users` ( `username` varchar(64) NOT NULL, `usergroup` varchar(64) NOT NULL, PRIMARY KEY (`username`,`usergroup`) -) +) COMMENT='Users and their assignments to user groups' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; @@ -265,10 +265,10 @@ CREATE TABLE IF NOT EXISTS `pma__usergroups` ( `tab` varchar(64) NOT NULL, `allowed` enum('Y','N') NOT NULL DEFAULT 'N', PRIMARY KEY (`usergroup`,`tab`,`allowed`) -) +) COMMENT='User groups with configured menu items' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; - + -- -------------------------------------------------------- -- @@ -282,6 +282,24 @@ CREATE TABLE IF NOT EXISTS `pma__navigationhiding` ( `db_name` varchar(64) NOT NULL, `table_name` varchar(64) NOT NULL, PRIMARY KEY (`username`,`item_name`,`item_type`,`db_name`,`table_name`) -) +) COMMENT='Hidden items of navigation tree' DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__savedsearches` +-- + +CREATE TABLE IF NOT EXISTS `pma__savedsearches` ( + `id` int(5) unsigned NOT NULL auto_increment, + `username` varchar(64) NOT NULL default '', + `db_name` varchar(64) NOT NULL default '', + `search_name` varchar(64) NOT NULL default '', + `search_data` text NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `u_savedsearches_username_dbname` (`username`,`db_name`,`search_name`) +) + COMMENT='Saved searches' + DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; diff --git a/examples/create_tables_drizzle.sql b/examples/create_tables_drizzle.sql index 698a15949d..71318caf77 100644 --- a/examples/create_tables_drizzle.sql +++ b/examples/create_tables_drizzle.sql @@ -1,27 +1,27 @@ -- -------------------------------------------------------- -- SQL Commands to set up the pmadb as described in the documentation. --- +-- -- This file is meant for use with Drizzle 2011.03.13 and above! --- +-- -- This script expects that you take care of database permissions. -- -- Please don't forget to set up the tablenames in config.inc.php --- +-- -- -------------------------------------------------------- --- +-- -- Database : `phpmyadmin` --- +-- CREATE DATABASE IF NOT EXISTS `phpmyadmin` COLLATE utf8_bin; USE phpmyadmin; -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__bookmark` --- +-- CREATE TABLE IF NOT EXISTS `pma__bookmark` ( `id` int(11) NOT NULL auto_increment, @@ -36,9 +36,9 @@ CREATE TABLE IF NOT EXISTS `pma__bookmark` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__column_info` --- +-- CREATE TABLE IF NOT EXISTS `pma__column_info` ( `id` int(5) NOT NULL auto_increment, @@ -57,9 +57,9 @@ CREATE TABLE IF NOT EXISTS `pma__column_info` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__history` --- +-- CREATE TABLE IF NOT EXISTS `pma__history` ( `id` bigint(20) NOT NULL auto_increment, @@ -76,9 +76,9 @@ CREATE TABLE IF NOT EXISTS `pma__history` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__pdf_pages` --- +-- CREATE TABLE IF NOT EXISTS `pma__pdf_pages` ( `db_name` varchar(64) NOT NULL default '', @@ -123,9 +123,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_uiprefs` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__relation` --- +-- CREATE TABLE IF NOT EXISTS `pma__relation` ( `master_db` varchar(64) NOT NULL default '', @@ -142,9 +142,9 @@ CREATE TABLE IF NOT EXISTS `pma__relation` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__table_coords` --- +-- CREATE TABLE IF NOT EXISTS `pma__table_coords` ( `db_name` varchar(64) NOT NULL default '', @@ -159,9 +159,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_coords` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__table_info` --- +-- CREATE TABLE IF NOT EXISTS `pma__table_info` ( `db_name` varchar(64) NOT NULL default '', @@ -174,9 +174,9 @@ CREATE TABLE IF NOT EXISTS `pma__table_info` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__designer_coords` --- +-- CREATE TABLE IF NOT EXISTS `pma__designer_coords` ( `db_name` varchar(64) NOT NULL default '', @@ -192,9 +192,9 @@ CREATE TABLE IF NOT EXISTS `pma__designer_coords` ( -- -------------------------------------------------------- --- +-- -- Table structure for table `pma__tracking` --- +-- CREATE TABLE IF NOT EXISTS `pma__tracking` ( `db_name` varchar(64) NOT NULL, @@ -236,7 +236,7 @@ CREATE TABLE IF NOT EXISTS `pma__users` ( `username` varchar(64) NOT NULL, `usergroup` varchar(64) NOT NULL, PRIMARY KEY (`username`,`usergroup`) -) +) COMMENT='Users and their assignments to user groups' COLLATE utf8_bin; @@ -251,10 +251,10 @@ CREATE TABLE IF NOT EXISTS `pma__usergroups` ( `tab` varchar(64) NOT NULL, `allowed` enum('Y','N') NOT NULL DEFAULT 'N', PRIMARY KEY (`usergroup`,`tab`,`allowed`) -) +) COMMENT='User groups with configured menu items' COLLATE utf8_bin; - + -- -------------------------------------------------------- -- @@ -268,6 +268,24 @@ CREATE TABLE IF NOT EXISTS `pma__navigationhiding` ( `db_name` varchar(64) NOT NULL, `table_name` varchar(64) NOT NULL, PRIMARY KEY (`username`,`item_name`,`item_type`,`db_name`,`table_name`) -) +) COMMENT='Hidden items of navigation tree' COLLATE utf8_bin; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma__savedsearches` +-- + +CREATE TABLE IF NOT EXISTS `pma__savedsearches` ( + `id` int(5) unsigned NOT NULL auto_increment, + `username` varchar(64) NOT NULL default '', + `db_name` varchar(64) NOT NULL default '', + `search_name` varchar(64) NOT NULL default '', + `search_data` text NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `u_savedsearches_username_dbname` (`username`,`db_name`,`search_name`) +) + COMMENT='Saved searches' + DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; diff --git a/js/db_qbe.js b/js/db_qbe.js new file mode 100644 index 0000000000..0a37b79aba --- /dev/null +++ b/js/db_qbe.js @@ -0,0 +1,55 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * @fileoverview function used in QBE for DB + * @name Database Operations + * + * @requires jQuery + * @requires jQueryUI + * @requires js/functions.js + * + */ + +/** + * Ajax event handlers here for db_qbe.php + * + * Actions Ajaxified here: + * Select saved search + */ + +/** + * Unbind all event handlers before tearing down a page + */ +AJAX.registerTeardown('db_qbe.js', function () { + $("#searchId").die('change'); + $("#saveSearch").die('click'); + $("#deleteSearch").die('click'); +}); + +AJAX.registerOnload('db_qbe.js', function () { + + /** + * Ajax event handlers for 'Select saved search' + */ + $("#searchId").live('change', function (event) { + if ('' == $(this).val()) { + return false; + } + + $('#action').val('load'); + $('#formQBE').submit(); + }); + + /** + * Ajax event handlers for 'Save search' + */ + $("#saveSearch").live('click', function (event) { + $('#action').val('save'); + }); + + /** + * Ajax event handlers for 'Delete search' + */ + $("#deleteSearch").live('click', function (event) { + $('#action').val('delete'); + }); +});
\ No newline at end of file diff --git a/libraries/DBQbe.class.php b/libraries/DBQbe.class.php index ccaad0bd88..dd7407d1e1 100644 --- a/libraries/DBQbe.class.php +++ b/libraries/DBQbe.class.php @@ -73,6 +73,13 @@ class PMA_DbQbe */ private $_criteriaRowInsert; /** + * Whether to delete a row + * + * @access private + * @var array + */ + private $_criteriaRowDelete; + /** * Already set criteria values * * @access private @@ -150,6 +157,13 @@ class PMA_DbQbe */ private $_curAndOrCol; /** + * Current criteria AND/OR row relations + * + * @access private + * @var array + */ + private $_curAndOrRow; + /** * New column count in case of add/delete * * @access private @@ -163,15 +177,64 @@ class PMA_DbQbe * @var integer */ private $_new_row_count; + /** + * List of saved searches + * + * @access private + * @var array + */ + private $_savedSearchList = null; + /** + * Current search + * + * @access private + * @var PMA_SavedSearches + */ + private $_currentSearch = null; /** - * Public Constructor + * Initialize criterias * - * @param string $db Database name + * @return static */ - public function __construct($db) + private function _loadCriterias() { - $this->_db = $db; + if (null === $this->_currentSearch + || null === $this->_currentSearch->getCriterias() + ) { + return $this; + } + + $criterias = $this->_currentSearch->getCriterias(); + $_REQUEST = $criterias + $_REQUEST; + + return $this; + } + + /** + * Getter for current search + * + * @return PMA_SavedSearches + */ + private function _getCurrentSearch() + { + return $this->_currentSearch; + } + + /** + * Public Constructor + * + * @param string $dbname Database name + * @param array $savedSearchList List of saved searches + * @param PMA_SavedSearches $currentSearch Current search id + */ + public function __construct( + $dbname, $savedSearchList = array(), $currentSearch = null + ) { + $this->_db = $dbname; + $this->_savedSearchList = $savedSearchList; + $this->_currentSearch = $currentSearch; + $this->_loadCriterias(); // Sets criteria parameters $this->_setSearchParams(); $this->_setCriteriaTablesAndColumns(); @@ -184,26 +247,7 @@ class PMA_DbQbe */ private function _setSearchParams() { - // sets column count - $criteriaColumnCount = PMA_ifSetOr( - $_REQUEST['criteriaColumnCount'], - 3, - 'numeric' - ); - $criteriaColumnAdd = PMA_ifSetOr( - $_REQUEST['criteriaColumnAdd'], - 0, - 'numeric' - ); - $this->_criteria_column_count = max( - $criteriaColumnCount + $criteriaColumnAdd, - 0 - ); - - // sets row count - $rows = PMA_ifSetOr($_REQUEST['rows'], 0, 'numeric'); - $criteriaRowAdd = PMA_ifSetOr($_REQUEST['criteriaRowAdd'], 0, 'numeric'); - $this->_criteria_row_count = max($rows + $criteriaRowAdd, 0); + $criteriaColumnCount = $this->_initializeCriteriasCount(); $this->_criteriaColumnInsert = PMA_ifSetOr( $_REQUEST['criteriaColumnInsert'], @@ -1130,20 +1174,10 @@ class PMA_DbQbe $unique_columns = $indexes['unique']; $index_columns = $indexes['index']; - // now we want to find the best. - if (isset($unique_columns) && count($unique_columns) > 0) { - $candidate_columns = $unique_columns; - $needsort = 1; - } elseif (isset($index_columns) && count($index_columns) > 0) { - $candidate_columns = $index_columns; - $needsort = 1; - } elseif (isset($where_clause_columns) && count($where_clause_columns) > 0) { - $candidate_columns = $where_clause_columns; - $needsort = 0; - } else { - $candidate_columns = $all_tables; - $needsort = 0; - } + list($candidate_columns, $needsort) + = $this->_getLeftJoinColumnCandidatesBest( + $all_tables, $where_clause_columns, $unique_columns, $index_columns + ); // If we came up with $unique_columns (very good) or $index_columns (still // good) as $candidate_columns we want to check if we have any 'Y' there @@ -1348,8 +1382,13 @@ class PMA_DbQbe */ public function getSelectionForm($cfgRelation) { - $html_output = '<form action="db_qbe.php" method="post">'; + $html_output = '<form action="db_qbe.php" method="post" id="formQBE">'; $html_output .= '<fieldset>'; + + if ($GLOBALS['cfgRelation']['savedsearcheswork']) { + $html_output .= $this->_getSavedSearchesField(); + } + $html_output .= '<table class="data" style="width: 100%;">'; // Get table's <tr> elements $html_output .= $this->_getColumnNamesRow(); @@ -1397,5 +1436,111 @@ class PMA_DbQbe $html_output .= '</form>'; return $html_output; } + + /** + * Get fields to display + * + * @return string + */ + private function _getSavedSearchesField() + { + $html_output = __('Saved searches : '); + $html_output .= '<select name="searchId" id="searchId">'; + $html_output .= '<option value="">New search</option>'; + + $currentSearch = $this->_getCurrentSearch(); + $currentSearchId = null; + $currentSearchName = null; + if (null != $currentSearch) { + $currentSearchId = $currentSearch->getId(); + $currentSearchName = $currentSearch->getSearchName(); + } + + foreach ($this->_savedSearchList as $id => $name) { + $html_output .= '<option value="' . htmlspecialchars($id) + . '" ' . ( + $id == $currentSearchId + ? 'selected="selected" ' + : '' + ) + . '>' + . htmlspecialchars($name) + . '</option>'; + } + $html_output .= '</select>'; + $html_output .= '<input type="text" name="searchName" id="searchName" ' + . 'value="' . $currentSearchName . '" />'; + $html_output .= '<input type="hidden" name="action" id="action" value="" />'; + $html_output .= '<input type="submit" name="saveSearch" id="saveSearch" ' + . 'value="' . __('Save search') . '" />'; + $html_output .= '<input type="submit" name="deleteSearch" id="deleteSearch" ' + . 'value="' . __('Delete search') . '" />'; + + return $html_output; + } + + /** + * Initialize _criteria_column_count + * + * @return int Previous number of columns + */ + private function _initializeCriteriasCount() + { + // sets column count + $criteriaColumnCount = PMA_ifSetOr( + $_REQUEST['criteriaColumnCount'], + 3, + 'numeric' + ); + $criteriaColumnAdd = PMA_ifSetOr( + $_REQUEST['criteriaColumnAdd'], + 0, + 'numeric' + ); + $this->_criteria_column_count = max( + $criteriaColumnCount + $criteriaColumnAdd, + 0 + ); + + // sets row count + $rows = PMA_ifSetOr($_REQUEST['rows'], 0, 'numeric'); + $criteriaRowAdd = PMA_ifSetOr($_REQUEST['criteriaRowAdd'], 0, 'numeric'); + $this->_criteria_row_count = max($rows + $criteriaRowAdd, 0); + + return $criteriaColumnCount; + } + + /** + * Get best + * + * @param array $all_tables All tables + * @param array $where_clause_columns Columns with where clause + * @param array $unique_columns Unique columns + * @param array $index_columns Indexed columns + * + * @return array + */ + private function _getLeftJoinColumnCandidatesBest( + $all_tables, $where_clause_columns, $unique_columns, $index_columns + ) { + // now we want to find the best. + if (isset($unique_columns) && count($unique_columns) > 0) { + $candidate_columns = $unique_columns; + $needsort = 1; + return array($candidate_columns, $needsort); + } elseif (isset($index_columns) && count($index_columns) > 0) { + $candidate_columns = $index_columns; + $needsort = 1; + return array($candidate_columns, $needsort); + } elseif (isset($where_clause_columns) && count($where_clause_columns) > 0) { + $candidate_columns = $where_clause_columns; + $needsort = 0; + return array($candidate_columns, $needsort); + } else { + $candidate_columns = $all_tables; + $needsort = 0; + return array($candidate_columns, $needsort); + } + } } ?> diff --git a/libraries/SavedSearches.php b/libraries/SavedSearches.php new file mode 100644 index 0000000000..63cca46f41 --- /dev/null +++ b/libraries/SavedSearches.php @@ -0,0 +1,397 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Saved searches managing + * + * @package PhpMyAdmin + */ + +if (! defined('PHPMYADMIN')) { + exit; +} + +/** + * Saved searches managing + * + * @package PhpMyAdmin + */ +class PMA_SavedSearches +{ + /** + * Global configuration + * @var array + */ + private $_config = null; + + /** + * Id + * @var int|null + */ + private $_id = null; + + /** + * Username + * @var string + */ + private $_username = null; + + /** + * DB name + * @var string + */ + private $_dbname = null; + + /** + * Saved search name + * @var string + */ + private $_searchName = null; + + /** + * Setter of id + * + * @param int|null $searchId Id of search + * + * @return static + */ + public function setId($searchId) + { + $searchId = (int)$searchId; + if (empty($searchId)) { + $searchId = null; + } + + $this->_id = $searchId; + return $this; + } + + /** + * Getter of id + * + * @return int|null + */ + public function getId() + { + return $this->_id; + } + + /** + * Setter of searchName + * + * @param string $searchName Saved search name + * + * @return static + */ + public function setSearchName($searchName) + { + $this->_searchName = $searchName; + return $this; + } + + /** + * Getter of searchName + * + * @return string + */ + public function getSearchName() + { + return $this->_searchName; + } + + /** + * Criterias + * @var array + */ + private $_criterias = null; + + /** + * Setter of config + * + * @param array $config Global configuration + * + * @return static + */ + public function setConfig($config) + { + $this->_config = $config; + return $this; + } + + /** + * Getter of config + * + * @return array + */ + public function getConfig() + { + return $this->_config; + } + + /** + * Setter for criterias + * + * @param array $criterias Criterias of saved searches + * @param bool $json Criterias are in JSON format + * + * @return static + */ + public function setCriterias($criterias, $json = false) + { + if (true === $json) { + $this->_criterias = json_decode($criterias, true); + return $this; + } + + $aListFieldsToGet = array( + 'criteriaColumn', + 'criteriaSort', + 'criteriaShow', + 'criteria', + 'criteriaAndOrRow', + 'criteriaAndOrColumn', + 'rows' + ); + + $data = array(); + + $data['criteriaColumnCount'] = count($criterias['criteriaColumn']); + + foreach ($aListFieldsToGet as $field) { + $data[$field] = $criterias[$field]; + } + + for ($i = 0; $i <= $data['rows']; $i++) { + $data['Or' . $i] = $criterias['Or' . $i]; + } + + $this->_criterias = $data; + return $this; + } + + /** + * Getter for criterias + * + * @return array + */ + public function getCriterias() + { + return $this->_criterias; + } + + /** + * Setter for username + * + * @param string $username Username + * + * @return static + */ + public function setUsername($username) + { + $this->_username = $username; + return $this; + } + + /** + * Getter for username + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + /** + * Setter for DB name + * + * @param string $dbname DB name + * + * @return static + */ + public function setDbname($dbname) + { + $this->_dbname = $dbname; + return $this; + } + + /** + * Getter for DB name + * + * @return string + */ + public function getDbname() + { + return $this->_dbname; + } + + /** + * Public constructor + * + * @param array $config Global configuration + */ + public function __construct($config) + { + $this->setConfig($config); + } + + /** + * Save the search + * + * @return boolean + */ + public function save() + { + if (null == $this->getUsername() + || null == $this->getDbname() + || null == $this->getSearchName() + || null == $this->getCriterias() + ) { + PMA_Util::mysqlDie(__('Missing information to save the search.')); + } + + $savedSearchesTbl + = PMA_Util::backquote($this->_config['cfgRelation']['db']) . "." + . PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']); + + //If it's an insert. + if (null === $this->getId()) { + $wheres = array( + "search_name = '" . PMA_Util::sqlAddSlashes($this->getSearchName()) + . "'" + ); + $existingSearches = $this->getList($wheres); + + if (!empty($existingSearches)) { + PMA_Util::mysqlDie(__('An entry with this name already exists.')); + } + + $sqlQuery = "INSERT INTO " . $savedSearchesTbl + . "(`username`, `db_name`, `search_name`, `search_data`)" + . " VALUES (" + . "'" . PMA_Util::sqlAddSlashes($this->getUsername()) . "'," + . "'" . PMA_Util::sqlAddSlashes($this->getDbname()) . "'," + . "'" . PMA_Util::sqlAddSlashes($this->getSearchName()) . "'," + . "'" . PMA_Util::sqlAddSlashes(json_encode($this->getCriterias())) + . "')"; + + $result = (bool)PMA_queryAsControlUser($sqlQuery); + if (!$result) { + return false; + } + + $this->setId($GLOBALS['dbi']->insertId()); + + return true; + } + + //Else, it's an update. + $wheres = array( + "id != " . $this->getId(), + "search_name = '" . PMA_Util::sqlAddSlashes($this->getSearchName()) . "'" + ); + $existingSearches = $this->getList($wheres); + + if (!empty($existingSearches)) { + PMA_Util::mysqlDie(__('An entry with this name already exists.')); + } + + $sqlQuery = "UPDATE " . $savedSearchesTbl + . "SET `search_name` = '" + . PMA_Util::sqlAddSlashes($this->getSearchName()) . "', " + . "`search_data` = '" + . PMA_Util::sqlAddSlashes(json_encode($this->getCriterias())) . "' " + . "WHERE id = " . $this->getId(); + return (bool)PMA_queryAsControlUser($sqlQuery); + } + + /** + * Delete the search + * + * @return boolean + */ + public function delete() + { + if (null == $this->getId()) { + PMA_Util::mysqlDie(__('Missing information to delete the search.')); + } + + $savedSearchesTbl + = PMA_Util::backquote($this->_config['cfgRelation']['db']) . "." + . PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']); + + $sqlQuery = "DELETE FROM " . $savedSearchesTbl + . "WHERE id = '" . PMA_Util::sqlAddSlashes($this->getId()) . "'"; + + return (bool)PMA_queryAsControlUser($sqlQuery); + } + + /** + * Load the current search from an id. + * + * @return bool Success + */ + public function load() + { + if (null == $this->getId()) { + PMA_Util::mysqlDie(__('Missing information to load the search.')); + } + + $savedSearchesTbl = PMA_Util::backquote($this->_config['cfgRelation']['db']) + . "." + . PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']); + $sqlQuery = "SELECT id, search_name, search_data " + . "FROM " . $savedSearchesTbl . " " + . "WHERE id = '" . PMA_Util::sqlAddSlashes($this->getId()) . "' "; + + $resList = PMA_queryAsControlUser($sqlQuery); + + if (false === ($oneResult = $GLOBALS['dbi']->fetchArray($resList))) { + PMA_Util::mysqlDie(__('Error while loading the search.')); + } + + $this->setSearchName($oneResult['search_name']) + ->setCriterias($oneResult['search_data'], true); + + return true; + } + + /** + * Get the list of saved search of a user on a DB + * + * @param array $wheres List of filters + * + * @return array|bool List of saved search or false on failure + */ + public function getList(array $wheres = array()) + { + if (null == $this->getUsername() + || null == $this->getDbname() + ) { + return false; + } + + $savedSearchesTbl = PMA_Util::backquote($this->_config['cfgRelation']['db']) + . "." + . PMA_Util::backquote($this->_config['cfgRelation']['savedsearches']); + $sqlQuery = "SELECT id, search_name " + . "FROM " . $savedSearchesTbl . " " + . "WHERE " + . "username = '" . PMA_Util::sqlAddSlashes($this->getUsername()) . "' " + . "AND db_name = '" . PMA_Util::sqlAddSlashes($this->getDbname()) . "' "; + + foreach ($wheres as $where) { + $sqlQuery .= "AND " . $where . " "; + } + + $sqlQuery .= "order by search_name ASC "; + + $resList = PMA_queryAsControlUser($sqlQuery); + + $list = array(); + while ($oneResult = $GLOBALS['dbi']->fetchArray($resList)) { + $list[$oneResult['id']] = $oneResult['search_name']; + } + + return $list; + } +}
\ No newline at end of file diff --git a/libraries/config.default.php b/libraries/config.default.php index a4694bf9e8..d04abdaf44 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -466,6 +466,15 @@ $cfg['Servers'][$i]['usergroups'] = ''; $cfg['Servers'][$i]['navigationhiding'] = ''; /** + * table to store information about saved searches from query-by-example on a db + * - leave blank to disable saved searches feature + * SUGGESTED: 'pma__savedsearches' + * + * @global string $cfg['Servers'][$i]['savedsearches'] + */ +$cfg['Servers'][$i]['savedsearches'] = ''; + +/** * Maximum number of records saved in $cfg['Servers'][$i]['table_uiprefs'] table. * * In case where tables in databases is modified (e.g. dropped or renamed), diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php index 611c1480b8..e979966799 100644 --- a/libraries/config/setup.forms.php +++ b/libraries/config/setup.forms.php @@ -79,6 +79,7 @@ $forms['Servers']['Server_pmadb'] = array('Servers' => array(1 => array( 'table_coords' => 'pma__table_coords', 'pdf_pages' => 'pma__pdf_pages', 'designer_coords' => 'pma__designer_coords', + 'saved_searches' => 'pma__savedsearches', 'MaxTableUiprefs' => 100))); $forms['Servers']['Server_tracking'] = array('Servers' => array(1 => array( 'tracking_version_auto_create', diff --git a/libraries/relation.lib.php b/libraries/relation.lib.php index 47f4bf7941..511337241d 100644 --- a/libraries/relation.lib.php +++ b/libraries/relation.lib.php @@ -395,6 +395,7 @@ function PMA_checkRelationsParam() $cfgRelation['menuswork'] = false; $cfgRelation['navwork'] = false; $cfgRelation['allworks'] = false; + $cfgRelation['savedsearcheswork'] = false; $cfgRelation['user'] = null; $cfgRelation['db'] = null; @@ -463,6 +464,8 @@ function PMA_checkRelationsParam() $cfgRelation['usergroups'] = $curr_table[0]; } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['navigationhiding']) { $cfgRelation['navigationhiding'] = $curr_table[0]; + } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['savedsearches']) { + $cfgRelation['savedsearches'] = $curr_table[0]; } } // end while $GLOBALS['dbi']->freeResult($tab_rs); @@ -521,6 +524,10 @@ function PMA_checkRelationsParam() $cfgRelation['navwork'] = true; } + if (isset($cfgRelation['savedsearches'])) { + $cfgRelation['savedsearcheswork'] = true; + } + if ($cfgRelation['relwork'] && $cfgRelation['displaywork'] && $cfgRelation['pdfwork'] && $cfgRelation['commwork'] && $cfgRelation['mimework'] && $cfgRelation['historywork'] @@ -528,6 +535,7 @@ function PMA_checkRelationsParam() && $cfgRelation['trackingwork'] && $cfgRelation['userconfigwork'] && $cfgRelation['bookmarkwork'] && $cfgRelation['designerwork'] && $cfgRelation['menuswork'] && $cfgRelation['navwork'] + && $cfgRelation['savedsearcheswork'] ) { $cfgRelation['allworks'] = true; } diff --git a/test/libraries/PMA_relation_cleanup_test.php b/test/libraries/PMA_relation_cleanup_test.php index 97d1099bf0..9b59f366b3 100644 --- a/test/libraries/PMA_relation_cleanup_test.php +++ b/test/libraries/PMA_relation_cleanup_test.php @@ -52,6 +52,7 @@ class PMA_Relation_Cleanup_Test extends PHPUnit_Framework_TestCase $GLOBALS['cfg']['Server']['users'] = 'users'; $GLOBALS['cfg']['Server']['usergroups'] = 'usergroups'; $GLOBALS['cfg']['Server']['navigationhiding'] = 'navigationhiding'; + $GLOBALS['cfg']['Server']['savedsearches'] = 'savedsearches'; $this->redefineRelation(); } |