_db = $db; // Sets criteria parameters $this->_setSearchParams(); } /** * Sets search parameters * * @return void */ private function _setSearchParams() { $this->_tables_names_only = $GLOBALS['dbi']->getTables($this->_db); $this->_searchTypes = array( '1' => __('at least one of the words'), '2' => __('all words'), '3' => __('the exact phrase'), '4' => __('as regular expression'), ); if (empty($_REQUEST['criteriaSearchType']) || ! is_string($_REQUEST['criteriaSearchType']) || ! array_key_exists( $_REQUEST['criteriaSearchType'], $this->_searchTypes ) ) { $this->_criteriaSearchType = 1; unset($_REQUEST['submit_search']); } else { $this->_criteriaSearchType = (int) $_REQUEST['criteriaSearchType']; $this->_searchTypeDescription = $this->_searchTypes[$_REQUEST['criteriaSearchType']]; } if (empty($_REQUEST['criteriaSearchString']) || ! is_string($_REQUEST['criteriaSearchString']) ) { $this->_criteriaSearchString = ''; unset($_REQUEST['submit_search']); } else { $this->_criteriaSearchString = $_REQUEST['criteriaSearchString']; } $this->_criteriaTables = array(); if (empty($_REQUEST['criteriaTables']) || ! is_array($_REQUEST['criteriaTables']) ) { unset($_REQUEST['submit_search']); } else { $this->_criteriaTables = array_intersect( $_REQUEST['criteriaTables'], $this->_tables_names_only ); } if (empty($_REQUEST['criteriaColumnName']) || ! is_string($_REQUEST['criteriaColumnName']) ) { unset($this->_criteriaColumnName); } else { $this->_criteriaColumnName = Util::sqlAddSlashes( $_REQUEST['criteriaColumnName'], true ); } } /** * Builds the SQL search query * * @param string $table The table name * * @return array 3 SQL queries (for count, display and delete results) * * @todo can we make use of fulltextsearch IN BOOLEAN MODE for this? * PMA_backquote * DatabaseInterface::freeResult * DatabaseInterface::fetchAssoc * $GLOBALS['db'] * explode * count * strlen */ private function _getSearchSqls($table) { // Statement types $sqlstr_select = 'SELECT'; $sqlstr_delete = 'DELETE'; // Table to use $sqlstr_from = ' FROM ' . Util::backquote($GLOBALS['db']) . '.' . Util::backquote($table); // Gets where clause for the query $where_clause = $this->_getWhereClause($table); // Builds complete queries $sql = array(); $sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from . $where_clause; // here, I think we need to still use the COUNT clause, even for // VIEWs, anyway we have a WHERE clause that should limit results $sql['select_count'] = $sqlstr_select . ' COUNT(*) AS `count`' . $sqlstr_from . $where_clause; $sql['delete'] = $sqlstr_delete . $sqlstr_from . $where_clause; return $sql; } /** * Provides where clause for building SQL query * * @param string $table The table name * * @return string The generated where clause */ private function _getWhereClause($table) { // Columns to select $allColumns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table); $likeClauses = array(); // Based on search type, decide like/regex & '%'/'' $like_or_regex = (($this->_criteriaSearchType == 4) ? 'REGEXP' : 'LIKE'); $automatic_wildcard = (($this->_criteriaSearchType < 3) ? '%' : ''); // For "as regular expression" (search option 4), LIKE won't be used // Usage example: If user is searching for a literal $ in a regexp search, // he should enter \$ as the value. $criteriaSearchStringEscaped = Util::sqlAddSlashes( $this->_criteriaSearchString, ($this->_criteriaSearchType == 4 ? false : true) ); // Extract search words or pattern $search_words = (($this->_criteriaSearchType > 2) ? array($criteriaSearchStringEscaped) : explode(' ', $criteriaSearchStringEscaped)); foreach ($search_words as $search_word) { // Eliminates empty values if (mb_strlen($search_word) === 0) { continue; } $likeClausesPerColumn = array(); // for each column in the table foreach ($allColumns as $column) { if (! isset($this->_criteriaColumnName) || mb_strlen($this->_criteriaColumnName) == 0 || $column['Field'] == $this->_criteriaColumnName ) { $column = 'CONVERT(' . Util::backquote($column['Field']) . ' USING utf8)'; $likeClausesPerColumn[] = $column . ' ' . $like_or_regex . ' ' . "'" . $automatic_wildcard . $search_word . $automatic_wildcard . "'"; } } // end for if (count($likeClausesPerColumn) > 0) { $likeClauses[] = implode(' OR ', $likeClausesPerColumn); } } // end for // Use 'OR' if 'at least one word' is to be searched, else use 'AND' $implode_str = ($this->_criteriaSearchType == 1 ? ' OR ' : ' AND '); if (empty($likeClauses)) { // this could happen when the "inside column" does not exist // in any selected tables $where_clause = ' WHERE FALSE'; } else { $where_clause = ' WHERE (' . implode(') ' . $implode_str . ' (', $likeClauses) . ')'; } return $where_clause; } /** * Displays database search results * * @return string HTML for search results */ public function getSearchResults() { $html_output = ''; // Displays search string $html_output .= '
' . '' . ''; $num_search_result_total = 0; $odd_row = true; // For each table selected as search criteria foreach ($this->_criteriaTables as $each_table) { // Gets the SQL statements $newsearchsqls = $this->_getSearchSqls($each_table); // Executes the "COUNT" statement $res_cnt = $GLOBALS['dbi']->fetchValue($newsearchsqls['select_count']); $num_search_result_total += $res_cnt; // Gets the result row's HTML for a table $html_output .= $this->_getResultsRow( $each_table, $newsearchsqls, $odd_row, $res_cnt ); $odd_row = ! $odd_row; } // end for $html_output .= '
' . sprintf( __('Search results for "%s" %s:'), htmlspecialchars($this->_criteriaSearchString), $this->_searchTypeDescription ) . '
'; // Displays total number of matches if (count($this->_criteriaTables) > 1) { $html_output .= '

'; $html_output .= sprintf( _ngettext( 'Total: %s match', 'Total: %s matches', $num_search_result_total ), $num_search_result_total ); $html_output .= '

'; } return $html_output; } /** * Provides search results row with browse/delete links. * (for a table) * * @param string $each_table One of the tables on which search was performed * @param array $newsearchsqls Contains SQL queries * @param bool $odd_row For displaying contrasting table rows * @param integer $res_cnt Number of results found * * @return string HTML row */ private function _getResultsRow($each_table, $newsearchsqls, $odd_row, $res_cnt) { $this_url_params = array( 'db' => $GLOBALS['db'], 'table' => $each_table, 'goto' => 'db_sql.php', 'pos' => 0, 'is_js_confirmed' => 0, ); // Start forming search results row $html_output = ''; // Displays results count for a table $html_output .= ''; $html_output .= sprintf( _ngettext( '%1$s match in %2$s', '%1$s matches in %2$s', $res_cnt ), $res_cnt, htmlspecialchars($each_table) ); $html_output .= ''; // Displays browse/delete link if result count > 0 if ($res_cnt > 0) { $this_url_params['sql_query'] = $newsearchsqls['select_columns']; $browse_result_path = 'sql.php' . PMA_URL_getCommon($this_url_params); $html_output .= '' . __('Browse') . ''; $this_url_params['sql_query'] = $newsearchsqls['delete']; $delete_result_path = 'sql.php' . PMA_URL_getCommon($this_url_params); $html_output .= '' . __('Delete') . ''; } else { $html_output .= ' ' . ' '; }// end if else $html_output .= ''; return $html_output; } /** * Provides the main search form's html * * @return string HTML for selection form */ public function getSelectionForm() { $html_output = ''; $html_output .= '
'; $html_output .= PMA_URL_getHiddenInputs($GLOBALS['db']); $html_output .= '
'; // set legend caption $html_output .= '' . __('Search in database') . ''; $html_output .= ''; // inputbox for search phrase $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; // choices for types of search $html_output .= ''; $html_output .= ''; $html_output .= ''; // displays table names as select options $html_output .= ''; $html_output .= ''; $html_output .= ''; // Displays 'select all' and 'unselect all' links $alter_select = '' . __('Select all') . '  / '; $alter_select .= '' . __('Unselect all') . ''; $html_output .= ''; // Inputbox for column name entry $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= ''; $html_output .= '
' . __('Words or values to search for (wildcard: "%"):') . ''; $html_output .= '
' . __('Find:') . ''; $choices = array( '1' => __('at least one of the words') . Util::showHint( __('Words are separated by a space character (" ").') ), '2' => __('all words') . Util::showHint( __('Words are separated by a space character (" ").') ), '3' => __('the exact phrase'), '4' => __('as regular expression') . ' ' . Util::showMySQLDocu('Regexp') ); // 4th parameter set to true to add line breaks // 5th parameter set to false to avoid htmlspecialchars() escaping // in the label since we have some HTML in some labels $html_output .= Util::getRadioFields( 'criteriaSearchType', $choices, $this->_criteriaSearchType, true, false ); $html_output .= '
' . __('Inside tables:') . ''; $html_output .= ''; $html_output .= '
' . $alter_select . '
' . __('Inside column:') . '
'; $html_output .= '
'; $html_output .= '
'; $html_output .= ''; $html_output .= '
'; $html_output .= '
'; $html_output .= '
' . '
'; return $html_output; } /** * Provides div tags for browsing search results and sql query form. * * @return string div tags */ public function getResultDivs() { $html_output = ''; $html_output .= '
'; $html_output .= ''; $html_output .= '
'; // div for browsing results $html_output .= '
'; $html_output .= ''; $html_output .= '
'; $html_output .= '
'; $html_output .= '
'; $html_output .= ''; $html_output .= '
'; $html_output .= ''; $html_output .= ''; return $html_output; } }