123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173 |
- <?php
- /* vim: set expandtab sw=4 ts=4 sts=4: */
- /**
- * Holds the PhpMyAdmin\Controllers\Table\TableSearchController
- *
- * @package PhpMyAdmin\Controllers
- */
- namespace PhpMyAdmin\Controllers\Table;
- use PhpMyAdmin\Controllers\TableController;
- use PhpMyAdmin\Core;
- use PhpMyAdmin\DatabaseInterface;
- use PhpMyAdmin\Relation;
- use PhpMyAdmin\Sql;
- use PhpMyAdmin\Template;
- use PhpMyAdmin\Util;
- /**
- * Class TableSearchController
- *
- * @package PhpMyAdmin\Controllers
- */
- class TableSearchController extends TableController
- {
- /**
- * Normal search or Zoom search
- *
- * @access private
- * @var string
- */
- private $_searchType;
- /**
- * Names of columns
- *
- * @access private
- * @var array
- */
- private $_columnNames;
- /**
- * Types of columns
- *
- * @access private
- * @var array
- */
- private $_columnTypes;
- /**
- * Collations of columns
- *
- * @access private
- * @var array
- */
- private $_columnCollations;
- /**
- * Null Flags of columns
- *
- * @access private
- * @var array
- */
- private $_columnNullFlags;
- /**
- * Whether a geometry column is present
- *
- * @access private
- * @var boolean
- */
- private $_geomColumnFlag;
- /**
- * Foreign Keys
- *
- * @access private
- * @var array
- */
- private $_foreigners;
- /**
- * Connection charset
- *
- * @access private
- * @var string
- */
- private $_connectionCharSet;
- protected $url_query;
- /**
- * @var Relation $relation
- */
- private $relation;
- /**
- * Constructor
- *
- * @param string $searchType Search type
- * @param string $url_query URL query
- */
- public function __construct(
- $response,
- $dbi,
- $db,
- $table,
- $searchType,
- $url_query
- ) {
- parent::__construct($response, $dbi, $db, $table);
- $this->url_query = $url_query;
- $this->_searchType = $searchType;
- $this->_columnNames = array();
- $this->_columnNullFlags = array();
- $this->_columnTypes = array();
- $this->_columnCollations = array();
- $this->_geomColumnFlag = false;
- $this->_foreigners = array();
- $this->relation = new Relation();
- // Loads table's information
- $this->_loadTableInfo();
- $this->_connectionCharSet = $this->dbi->fetchValue(
- "SELECT @@character_set_connection"
- );
- }
- /**
- * Gets all the columns of a table along with their types, collations
- * and whether null or not.
- *
- * @return void
- */
- private function _loadTableInfo()
- {
- // Gets the list and number of columns
- $columns = $this->dbi->getColumns(
- $this->db, $this->table, null, true
- );
- // Get details about the geometry functions
- $geom_types = Util::getGISDatatypes();
- foreach ($columns as $row) {
- // set column name
- $this->_columnNames[] = $row['Field'];
- $type = $row['Type'];
- // check whether table contains geometric columns
- if (in_array($type, $geom_types)) {
- $this->_geomColumnFlag = true;
- }
- // reformat mysql query output
- if (strncasecmp($type, 'set', 3) == 0
- || strncasecmp($type, 'enum', 4) == 0
- ) {
- $type = str_replace(',', ', ', $type);
- } else {
- // strip the "BINARY" attribute, except if we find "BINARY(" because
- // this would be a BINARY or VARBINARY column type
- if (! preg_match('@BINARY[\(]@i', $type)) {
- $type = preg_replace('@BINARY@i', '', $type);
- }
- $type = preg_replace('@ZEROFILL@i', '', $type);
- $type = preg_replace('@UNSIGNED@i', '', $type);
- $type = mb_strtolower($type);
- }
- if (empty($type)) {
- $type = ' ';
- }
- $this->_columnTypes[] = $type;
- $this->_columnNullFlags[] = $row['Null'];
- $this->_columnCollations[]
- = ! empty($row['Collation']) && $row['Collation'] != 'NULL'
- ? $row['Collation']
- : '';
- } // end for
- // Retrieve foreign keys
- $this->_foreigners = $this->relation->getForeigners($this->db, $this->table);
- }
- /**
- * Index action
- *
- * @return void
- */
- public function indexAction()
- {
- switch ($this->_searchType) {
- case 'replace':
- if (isset($_POST['find'])) {
- $this->findAction();
- return;
- }
- $this->response
- ->getHeader()
- ->getScripts()
- ->addFile('tbl_find_replace.js');
- if (isset($_POST['replace'])) {
- $this->replaceAction();
- }
- // Displays the find and replace form
- $this->displaySelectionFormAction();
- break;
- case 'normal':
- $this->response->getHeader()
- ->getScripts()
- ->addFiles(
- array(
- 'makegrid.js',
- 'sql.js',
- 'tbl_select.js',
- 'tbl_change.js',
- 'vendor/jquery/jquery.uitablefilter.js',
- 'gis_data_editor.js',
- )
- );
- if (isset($_POST['range_search'])) {
- $this->rangeSearchAction();
- return;
- }
- /**
- * No selection criteria received -> display the selection form
- */
- if (!isset($_POST['columnsToDisplay'])
- && !isset($_POST['displayAllColumns'])
- ) {
- $this->displaySelectionFormAction();
- } else {
- $this->doSelectionAction();
- }
- break;
- case 'zoom':
- $this->response->getHeader()
- ->getScripts()
- ->addFiles(
- array(
- 'makegrid.js',
- 'sql.js',
- 'vendor/jqplot/jquery.jqplot.js',
- 'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
- 'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
- 'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
- 'vendor/jqplot/plugins/jqplot.highlighter.js',
- 'vendor/jqplot/plugins/jqplot.cursor.js',
- 'tbl_zoom_plot_jqplot.js',
- 'tbl_change.js',
- )
- );
- /**
- * Handle AJAX request for data row on point select
- *
- * @var boolean Object containing parameters for the POST request
- */
- if (isset($_POST['get_data_row'])
- && $_POST['get_data_row'] == true
- ) {
- $this->getDataRowAction();
- return;
- }
- /**
- * Handle AJAX request for changing field information
- * (value,collation,operators,field values) in input form
- *
- * @var boolean Object containing parameters for the POST request
- */
- if (isset($_POST['change_tbl_info'])
- && $_POST['change_tbl_info'] == true
- ) {
- $this->changeTableInfoAction();
- return;
- }
- //Set default datalabel if not selected
- if (!isset($_POST['zoom_submit']) || $_POST['dataLabel'] == '') {
- $dataLabel = $this->relation->getDisplayField($this->db, $this->table);
- } else {
- $dataLabel = $_POST['dataLabel'];
- }
- // Displays the zoom search form
- $this->displaySelectionFormAction($dataLabel);
- /*
- * Handle the input criteria and generate the query result
- * Form for displaying query results
- */
- if (isset($_POST['zoom_submit'])
- && $_POST['criteriaColumnNames'][0] != 'pma_null'
- && $_POST['criteriaColumnNames'][1] != 'pma_null'
- && $_POST['criteriaColumnNames'][0] != $_POST['criteriaColumnNames'][1]
- ) {
- if (! isset($goto)) {
- $goto = Util::getScriptNameForOption(
- $GLOBALS['cfg']['DefaultTabTable'], 'table'
- );
- }
- $this->zoomSubmitAction($dataLabel, $goto);
- }
- break;
- }
- }
- /**
- * Zoom submit action
- *
- * @param string $dataLabel Data label
- * @param string $goto Goto
- *
- * @return void
- */
- public function zoomSubmitAction($dataLabel, $goto)
- {
- //Query generation part
- $sql_query = $this->_buildSqlQuery();
- $sql_query .= ' LIMIT ' . $_POST['maxPlotLimit'];
- //Query execution part
- $result = $this->dbi->query(
- $sql_query . ";",
- DatabaseInterface::CONNECT_USER,
- DatabaseInterface::QUERY_STORE
- );
- $fields_meta = $this->dbi->getFieldsMeta($result);
- $data = array();
- while ($row = $this->dbi->fetchAssoc($result)) {
- //Need a row with indexes as 0,1,2 for the getUniqueCondition
- // hence using a temporary array
- $tmpRow = array();
- foreach ($row as $val) {
- $tmpRow[] = $val;
- }
- //Get unique condition on each row (will be needed for row update)
- $uniqueCondition = Util::getUniqueCondition(
- $result, // handle
- count($this->_columnNames), // fields_cnt
- $fields_meta, // fields_meta
- $tmpRow, // row
- true, // force_unique
- false, // restrict_to_table
- null // analyzed_sql_results
- );
- //Append it to row array as where_clause
- $row['where_clause'] = $uniqueCondition[0];
- $row['where_clause_sign'] = Core::signSqlQuery($uniqueCondition[0]);
- $tmpData = array(
- $_POST['criteriaColumnNames'][0] =>
- $row[$_POST['criteriaColumnNames'][0]],
- $_POST['criteriaColumnNames'][1] =>
- $row[$_POST['criteriaColumnNames'][1]],
- 'where_clause' => $uniqueCondition[0],
- 'where_clause_sign' => Core::signSqlQuery($uniqueCondition[0])
- );
- $tmpData[$dataLabel] = ($dataLabel) ? $row[$dataLabel] : '';
- $data[] = $tmpData;
- }
- unset($tmpData);
- //Displays form for point data and scatter plot
- $titles = array(
- 'Browse' => Util::getIcon(
- 'b_browse',
- __('Browse foreign values')
- )
- );
- $this->response->addHTML(
- Template::get('table/search/zoom_result_form')->render([
- 'db' => $this->db,
- 'table' => $this->table,
- 'column_names' => $this->_columnNames,
- 'foreigners' => $this->_foreigners,
- 'column_null_flags' => $this->_columnNullFlags,
- 'column_types' => $this->_columnTypes,
- 'titles' => $titles,
- 'goto' => $goto,
- 'data' => $data,
- 'data_json' => json_encode($data),
- 'zoom_submit' => isset($_POST['zoom_submit']),
- 'foreign_max_limit' => $GLOBALS['cfg']['ForeignKeyMaxLimit'],
- ])
- );
- }
- /**
- * Change table info action
- *
- * @return void
- */
- public function changeTableInfoAction()
- {
- $field = $_POST['field'];
- if ($field == 'pma_null') {
- $this->response->addJSON('field_type', '');
- $this->response->addJSON('field_collation', '');
- $this->response->addJSON('field_operators', '');
- $this->response->addJSON('field_value', '');
- return;
- }
- $key = array_search($field, $this->_columnNames);
- $search_index
- = ((isset($_POST['it']) && is_numeric($_POST['it']))
- ? intval($_POST['it']) : 0);
- $properties = $this->getColumnProperties($search_index, $key);
- $this->response->addJSON(
- 'field_type', htmlspecialchars($properties['type'])
- );
- $this->response->addJSON('field_collation', $properties['collation']);
- $this->response->addJSON('field_operators', $properties['func']);
- $this->response->addJSON('field_value', $properties['value']);
- }
- /**
- * Get data row action
- *
- * @return void
- */
- public function getDataRowAction()
- {
- if (! Core::checkSqlQuerySignature($_POST['where_clause'], $_POST['where_clause_sign'])) {
- return;
- }
- $extra_data = array();
- $row_info_query = 'SELECT * FROM ' . Util::backquote($_POST['db']) . '.'
- . Util::backquote($_POST['table']) . ' WHERE ' . $_POST['where_clause'];
- $result = $this->dbi->query(
- $row_info_query . ";",
- DatabaseInterface::CONNECT_USER,
- DatabaseInterface::QUERY_STORE
- );
- $fields_meta = $this->dbi->getFieldsMeta($result);
- while ($row = $this->dbi->fetchAssoc($result)) {
- // for bit fields we need to convert them to printable form
- $i = 0;
- foreach ($row as $col => $val) {
- if ($fields_meta[$i]->type == 'bit') {
- $row[$col] = Util::printableBitValue(
- $val, $fields_meta[$i]->length
- );
- }
- $i++;
- }
- $extra_data['row_info'] = $row;
- }
- $this->response->addJSON($extra_data);
- }
- /**
- * Do selection action
- *
- * @return void
- */
- public function doSelectionAction()
- {
- /**
- * Selection criteria have been submitted -> do the work
- */
- $sql_query = $this->_buildSqlQuery();
- /**
- * Add this to ensure following procedures included running correctly.
- */
- $db = $this->db;
- $sql = new Sql();
- $sql->executeQueryAndSendQueryResponse(
- null, // analyzed_sql_results
- false, // is_gotofile
- $this->db, // db
- $this->table, // table
- null, // find_real_end
- null, // sql_query_for_bookmark
- null, // extra_data
- null, // message_to_show
- null, // message
- null, // sql_data
- $GLOBALS['goto'], // goto
- $GLOBALS['pmaThemeImage'], // pmaThemeImage
- null, // disp_query
- null, // disp_message
- null, // query_type
- $sql_query, // sql_query
- null, // selectedTables
- null // complete_query
- );
- }
- /**
- * Display selection form action
- *
- * @param string $dataLabel Data label
- *
- * @return void
- */
- public function displaySelectionFormAction($dataLabel = null)
- {
- $this->url_query .= '&goto=tbl_select.php&back=tbl_select.php';
- if (! isset($goto)) {
- $goto = Util::getScriptNameForOption(
- $GLOBALS['cfg']['DefaultTabTable'], 'table'
- );
- }
- // Displays the table search form
- $this->response->addHTML(
- Template::get('secondary_tabs')
- ->render(
- array(
- 'url_params' => array(
- 'db' => $this->db,
- 'table' => $this->table,
- ),
- 'sub_tabs' => $this->_getSubTabs(),
- )
- )
- );
- $this->response->addHTML(
- Template::get('table/search/selection_form')->render(array(
- 'search_type' => $this->_searchType,
- 'db' => $this->db,
- 'table' => $this->table,
- 'goto' => $goto,
- 'self' => $this,
- 'geom_column_flag' => $this->_geomColumnFlag,
- 'column_names' => $this->_columnNames,
- 'column_types' => $this->_columnTypes,
- 'column_collations' => $this->_columnCollations,
- 'data_label' => $dataLabel,
- 'criteria_column_names' => isset($_POST['criteriaColumnNames']) ? $_POST['criteriaColumnNames'] : null,
- 'criteria_column_types' => isset($_POST['criteriaColumnTypes']) ? $_POST['criteriaColumnTypes'] : null,
- 'sql_types' => $GLOBALS['dbi']->types,
- 'max_rows' => intval($GLOBALS['cfg']['MaxRows']),
- 'max_plot_limit' => ((! empty($_POST['maxPlotLimit']))
- ? intval($_POST['maxPlotLimit'])
- : intval($GLOBALS['cfg']['maxRowPlotLimit'])),
- ))
- );
- }
- /**
- * Range search action
- *
- * @return void
- */
- public function rangeSearchAction()
- {
- $min_max = $this->getColumnMinMax($_POST['column']);
- $this->response->addJSON('column_data', $min_max);
- }
- /**
- * Find action
- *
- * @return void
- */
- public function findAction()
- {
- $useRegex = array_key_exists('useRegex', $_POST)
- && $_POST['useRegex'] == 'on';
- $preview = $this->getReplacePreview(
- $_POST['columnIndex'],
- $_POST['find'],
- $_POST['replaceWith'],
- $useRegex,
- $this->_connectionCharSet
- );
- $this->response->addJSON('preview', $preview);
- }
- /**
- * Replace action
- *
- * @return void
- */
- public function replaceAction()
- {
- $this->replace(
- $_POST['columnIndex'],
- $_POST['findString'],
- $_POST['replaceWith'],
- $_POST['useRegex'],
- $this->_connectionCharSet
- );
- $this->response->addHTML(
- Util::getMessage(
- __('Your SQL query has been executed successfully.'),
- null, 'success'
- )
- );
- }
- /**
- * Returns HTML for previewing strings found and their replacements
- *
- * @param int $columnIndex index of the column
- * @param string $find string to find in the column
- * @param string $replaceWith string to replace with
- * @param boolean $useRegex to use Regex replace or not
- * @param string $charSet character set of the connection
- *
- * @return string HTML for previewing strings found and their replacements
- */
- function getReplacePreview(
- $columnIndex, $find, $replaceWith, $useRegex, $charSet
- ) {
- $column = $this->_columnNames[$columnIndex];
- if ($useRegex) {
- $result = $this->_getRegexReplaceRows(
- $columnIndex, $find, $replaceWith, $charSet
- );
- } else {
- $sql_query = "SELECT "
- . Util::backquote($column) . ","
- . " REPLACE("
- . Util::backquote($column) . ", '" . $find . "', '"
- . $replaceWith
- . "'),"
- . " COUNT(*)"
- . " FROM " . Util::backquote($this->db)
- . "." . Util::backquote($this->table)
- . " WHERE " . Util::backquote($column)
- . " LIKE '%" . $find . "%' COLLATE " . $charSet . "_bin"; // here we
- // change the collation of the 2nd operand to a case sensitive
- // binary collation to make sure that the comparison
- // is case sensitive
- $sql_query .= " GROUP BY " . Util::backquote($column)
- . " ORDER BY " . Util::backquote($column) . " ASC";
- $result = $this->dbi->fetchResult($sql_query, 0);
- }
- return Template::get('table/search/replace_preview')->render(
- array(
- 'db' => $this->db,
- 'table' => $this->table,
- 'column_index' => $columnIndex,
- 'find' => $find,
- 'replace_with' => $replaceWith,
- 'use_regex' => $useRegex,
- 'result' => $result
- )
- );
- }
- /**
- * Finds and returns Regex pattern and their replacements
- *
- * @param int $columnIndex index of the column
- * @param string $find string to find in the column
- * @param string $replaceWith string to replace with
- * @param string $charSet character set of the connection
- *
- * @return array Array containing original values, replaced values and count
- */
- private function _getRegexReplaceRows(
- $columnIndex, $find, $replaceWith, $charSet
- ) {
- $column = $this->_columnNames[$columnIndex];
- $sql_query = "SELECT "
- . Util::backquote($column) . ","
- . " 1," // to add an extra column that will have replaced value
- . " COUNT(*)"
- . " FROM " . Util::backquote($this->db)
- . "." . Util::backquote($this->table)
- . " WHERE " . Util::backquote($column)
- . " RLIKE '" . $GLOBALS['dbi']->escapeString($find) . "' COLLATE "
- . $charSet . "_bin"; // here we
- // change the collation of the 2nd operand to a case sensitive
- // binary collation to make sure that the comparison is case sensitive
- $sql_query .= " GROUP BY " . Util::backquote($column)
- . " ORDER BY " . Util::backquote($column) . " ASC";
- $result = $this->dbi->fetchResult($sql_query, 0);
- if (is_array($result)) {
- /* Iterate over possible delimiters to get one */
- $delimiters = array('/', '@', '#', '~', '!', '$', '%', '^', '&', '_');
- $found = false;
- for ($i = 0, $l = count($delimiters); $i < $l; $i++) {
- if (strpos($find, $delimiters[$i]) === false) {
- $found = true;
- break;
- }
- }
- if (! $found) {
- return false;
- }
- $find = $delimiters[$i] . $find . $delimiters[$i];
- foreach ($result as $index=>$row) {
- $result[$index][1] = preg_replace(
- $find,
- $replaceWith,
- $row[0]
- );
- }
- }
- return $result;
- }
- /**
- * Replaces a given string in a column with a give replacement
- *
- * @param int $columnIndex index of the column
- * @param string $find string to find in the column
- * @param string $replaceWith string to replace with
- * @param boolean $useRegex to use Regex replace or not
- * @param string $charSet character set of the connection
- *
- * @return void
- */
- public function replace($columnIndex, $find, $replaceWith, $useRegex,
- $charSet
- ) {
- $column = $this->_columnNames[$columnIndex];
- if ($useRegex) {
- $toReplace = $this->_getRegexReplaceRows(
- $columnIndex, $find, $replaceWith, $charSet
- );
- $sql_query = "UPDATE " . Util::backquote($this->table)
- . " SET " . Util::backquote($column) . " = CASE";
- if (is_array($toReplace)) {
- foreach ($toReplace as $row) {
- $sql_query .= "\n WHEN " . Util::backquote($column)
- . " = '" . $GLOBALS['dbi']->escapeString($row[0])
- . "' THEN '" . $GLOBALS['dbi']->escapeString($row[1]) . "'";
- }
- }
- $sql_query .= " END"
- . " WHERE " . Util::backquote($column)
- . " RLIKE '" . $GLOBALS['dbi']->escapeString($find) . "' COLLATE "
- . $charSet . "_bin"; // here we
- // change the collation of the 2nd operand to a case sensitive
- // binary collation to make sure that the comparison
- // is case sensitive
- } else {
- $sql_query = "UPDATE " . Util::backquote($this->table)
- . " SET " . Util::backquote($column) . " ="
- . " REPLACE("
- . Util::backquote($column) . ", '" . $find . "', '"
- . $replaceWith
- . "')"
- . " WHERE " . Util::backquote($column)
- . " LIKE '%" . $find . "%' COLLATE " . $charSet . "_bin"; // here we
- // change the collation of the 2nd operand to a case sensitive
- // binary collation to make sure that the comparison
- // is case sensitive
- }
- $this->dbi->query(
- $sql_query,
- DatabaseInterface::CONNECT_USER,
- DatabaseInterface::QUERY_STORE
- );
- $GLOBALS['sql_query'] = $sql_query;
- }
- /**
- * Finds minimum and maximum value of a given column.
- *
- * @param string $column Column name
- *
- * @return array
- */
- public function getColumnMinMax($column)
- {
- $sql_query = 'SELECT MIN(' . Util::backquote($column) . ') AS `min`, '
- . 'MAX(' . Util::backquote($column) . ') AS `max` '
- . 'FROM ' . Util::backquote($this->db) . '.'
- . Util::backquote($this->table);
- $result = $this->dbi->fetchSingleRow($sql_query);
- return $result;
- }
- /**
- * Returns an array with necessary configurations to create
- * sub-tabs in the table_select page.
- *
- * @return array Array containing configuration (icon, text, link, id, args)
- * of sub-tabs
- */
- private function _getSubTabs()
- {
- $subtabs = array();
- $subtabs['search']['icon'] = 'b_search';
- $subtabs['search']['text'] = __('Table search');
- $subtabs['search']['link'] = 'tbl_select.php';
- $subtabs['search']['id'] = 'tbl_search_id';
- $subtabs['search']['args']['pos'] = 0;
- $subtabs['zoom']['icon'] = 'b_select';
- $subtabs['zoom']['link'] = 'tbl_zoom_select.php';
- $subtabs['zoom']['text'] = __('Zoom search');
- $subtabs['zoom']['id'] = 'zoom_search_id';
- $subtabs['replace']['icon'] = 'b_find_replace';
- $subtabs['replace']['link'] = 'tbl_find_replace.php';
- $subtabs['replace']['text'] = __('Find and replace');
- $subtabs['replace']['id'] = 'find_replace_id';
- return $subtabs;
- }
- /**
- * Builds the sql search query from the post parameters
- *
- * @return string the generated SQL query
- */
- private function _buildSqlQuery()
- {
- $sql_query = 'SELECT ';
- // If only distinct values are needed
- $is_distinct = (isset($_POST['distinct'])) ? 'true' : 'false';
- if ($is_distinct == 'true') {
- $sql_query .= 'DISTINCT ';
- }
- // if all column names were selected to display, we do a 'SELECT *'
- // (more efficient and this helps prevent a problem in IE
- // if one of the rows is edited and we come back to the Select results)
- if (isset($_POST['zoom_submit']) || ! empty($_POST['displayAllColumns'])) {
- $sql_query .= '* ';
- } else {
- $sql_query .= implode(
- ', ',
- Util::backquote($_POST['columnsToDisplay'])
- );
- } // end if
- $sql_query .= ' FROM '
- . Util::backquote($_POST['table']);
- $whereClause = $this->_generateWhereClause();
- $sql_query .= $whereClause;
- // if the search results are to be ordered
- if (isset($_POST['orderByColumn']) && $_POST['orderByColumn'] != '--nil--') {
- $sql_query .= ' ORDER BY '
- . Util::backquote($_POST['orderByColumn'])
- . ' ' . $_POST['order'];
- } // end if
- return $sql_query;
- }
- /**
- * Provides a column's type, collation, operators list, and criteria value
- * to display in table search form
- *
- * @param integer $search_index Row number in table search form
- * @param integer $column_index Column index in ColumnNames array
- *
- * @return array Array containing column's properties
- */
- public function getColumnProperties($search_index, $column_index)
- {
- $selected_operator = (isset($_POST['criteriaColumnOperators'][$search_index])
- ? $_POST['criteriaColumnOperators'][$search_index] : '');
- $entered_value = (isset($_POST['criteriaValues'])
- ? $_POST['criteriaValues'] : '');
- $titles = array(
- 'Browse' => Util::getIcon(
- 'b_browse', __('Browse foreign values')
- )
- );
- //Gets column's type and collation
- $type = $this->_columnTypes[$column_index];
- $collation = $this->_columnCollations[$column_index];
- //Gets column's comparison operators depending on column type
- $typeOperators = $GLOBALS['dbi']->types->getTypeOperatorsHtml(
- preg_replace('@\(.*@s', '', $this->_columnTypes[$column_index]),
- $this->_columnNullFlags[$column_index], $selected_operator
- );
- $func = Template::get('table/search/column_comparison_operators')->render(
- array(
- 'search_index' => $search_index,
- 'type_operators' => $typeOperators
- )
- );
- //Gets link to browse foreign data(if any) and criteria inputbox
- $foreignData = $this->relation->getForeignData(
- $this->_foreigners, $this->_columnNames[$column_index], false, '', ''
- );
- $value = Template::get('table/search/input_box')->render(
- array(
- 'str' => '',
- 'column_type' => (string) $type,
- 'column_id' => 'fieldID_',
- 'in_zoom_search_edit' => false,
- 'foreigners' => $this->_foreigners,
- 'column_name' => $this->_columnNames[$column_index],
- 'column_name_hash' => md5($this->_columnNames[$column_index]),
- 'foreign_data' => $foreignData,
- 'table' => $this->table,
- 'column_index' => $search_index,
- 'foreign_max_limit' => $GLOBALS['cfg']['ForeignKeyMaxLimit'],
- 'criteria_values' => $entered_value,
- 'db' => $this->db,
- 'titles' => $titles,
- 'in_fbs' => true
- )
- );
- return array(
- 'type' => $type,
- 'collation' => $collation,
- 'func' => $func,
- 'value' => $value
- );
- }
- /**
- * Generates the where clause for the SQL search query to be executed
- *
- * @return string the generated where clause
- */
- private function _generateWhereClause()
- {
- if (isset($_POST['customWhereClause'])
- && trim($_POST['customWhereClause']) != ''
- ) {
- return ' WHERE ' . $_POST['customWhereClause'];
- }
- // If there are no search criteria set or no unary criteria operators,
- // return
- if (! isset($_POST['criteriaValues'])
- && ! isset($_POST['criteriaColumnOperators'])
- && ! isset($_POST['geom_func'])
- ) {
- return '';
- }
- // else continue to form the where clause from column criteria values
- $fullWhereClause = array();
- foreach ($_POST['criteriaColumnOperators'] as $column_index => $operator) {
- $unaryFlag = $GLOBALS['dbi']->types->isUnaryOperator($operator);
- $tmp_geom_func = isset($_POST['geom_func'][$column_index])
- ? $_POST['geom_func'][$column_index] : null;
- $whereClause = $this->_getWhereClause(
- $_POST['criteriaValues'][$column_index],
- $_POST['criteriaColumnNames'][$column_index],
- $_POST['criteriaColumnTypes'][$column_index],
- $operator,
- $unaryFlag,
- $tmp_geom_func
- );
- if ($whereClause) {
- $fullWhereClause[] = $whereClause;
- }
- } // end foreach
- if (!empty($fullWhereClause)) {
- return ' WHERE ' . implode(' AND ', $fullWhereClause);
- }
- return '';
- }
- /**
- * Return the where clause in case column's type is ENUM.
- *
- * @param mixed $criteriaValues Search criteria input
- * @param string $func_type Search function/operator
- *
- * @return string part of where clause.
- */
- private function _getEnumWhereClause($criteriaValues, $func_type)
- {
- if (! is_array($criteriaValues)) {
- $criteriaValues = explode(',', $criteriaValues);
- }
- $enum_selected_count = count($criteriaValues);
- if ($func_type == '=' && $enum_selected_count > 1) {
- $func_type = 'IN';
- $parens_open = '(';
- $parens_close = ')';
- } elseif ($func_type == '!=' && $enum_selected_count > 1) {
- $func_type = 'NOT IN';
- $parens_open = '(';
- $parens_close = ')';
- } else {
- $parens_open = '';
- $parens_close = '';
- }
- $enum_where = '\''
- . $GLOBALS['dbi']->escapeString($criteriaValues[0]) . '\'';
- for ($e = 1; $e < $enum_selected_count; $e++) {
- $enum_where .= ', \''
- . $GLOBALS['dbi']->escapeString($criteriaValues[$e]) . '\'';
- }
- return ' ' . $func_type . ' ' . $parens_open
- . $enum_where . $parens_close;
- }
- /**
- * Return the where clause for a geometrical column.
- *
- * @param mixed $criteriaValues Search criteria input
- * @param string $names Name of the column on which search is submitted
- * @param string $func_type Search function/operator
- * @param string $types Type of the field
- * @param bool $geom_func Whether geometry functions should be applied
- *
- * @return string part of where clause.
- */
- private function _getGeomWhereClause($criteriaValues, $names,
- $func_type, $types, $geom_func = null
- ) {
- $geom_unary_functions = array(
- 'IsEmpty' => 1,
- 'IsSimple' => 1,
- 'IsRing' => 1,
- 'IsClosed' => 1,
- );
- $where = '';
- // Get details about the geometry functions
- $geom_funcs = Util::getGISFunctions($types, true, false);
- // If the function takes multiple parameters
- if(strpos($func_type, "IS NULL") !== false || strpos($func_type, "IS NOT NULL") !== false) {
- $where = Util::backquote($names) . " " . $func_type;
- return $where;
- } elseif ($geom_funcs[$geom_func]['params'] > 1) {
- // create gis data from the criteria input
- $gis_data = Util::createGISData($criteriaValues, $this->dbi->getVersion());
- $where = $geom_func . '(' . Util::backquote($names)
- . ', ' . $gis_data . ')';
- return $where;
- }
- // New output type is the output type of the function being applied
- $type = $geom_funcs[$geom_func]['type'];
- $geom_function_applied = $geom_func
- . '(' . Util::backquote($names) . ')';
- // If the where clause is something like 'IsEmpty(`spatial_col_name`)'
- if (isset($geom_unary_functions[$geom_func])
- && trim($criteriaValues) == ''
- ) {
- $where = $geom_function_applied;
- } elseif (in_array($type, Util::getGISDatatypes())
- && ! empty($criteriaValues)
- ) {
- // create gis data from the criteria input
- $gis_data = Util::createGISData($criteriaValues, $this->dbi->getVersion());
- $where = $geom_function_applied . " " . $func_type . " " . $gis_data;
- } elseif (strlen($criteriaValues) > 0) {
- $where = $geom_function_applied . " "
- . $func_type . " '" . $criteriaValues . "'";
- }
- return $where;
- }
- /**
- * Return the where clause for query generation based on the inputs provided.
- *
- * @param mixed $criteriaValues Search criteria input
- * @param string $names Name of the column on which search is submitted
- * @param string $types Type of the field
- * @param string $func_type Search function/operator
- * @param bool $unaryFlag Whether operator unary or not
- * @param bool $geom_func Whether geometry functions should be applied
- *
- * @return string generated where clause.
- */
- private function _getWhereClause($criteriaValues, $names, $types,
- $func_type, $unaryFlag, $geom_func = null
- ) {
- // If geometry function is set
- if (! empty($geom_func)) {
- return $this->_getGeomWhereClause(
- $criteriaValues, $names, $func_type, $types, $geom_func
- );
- }
- $backquoted_name = Util::backquote($names);
- $where = '';
- if ($unaryFlag) {
- $where = $backquoted_name . ' ' . $func_type;
- } elseif (strncasecmp($types, 'enum', 4) == 0 && (! empty($criteriaValues) || $criteriaValues[0] === '0')) {
- $where = $backquoted_name;
- $where .= $this->_getEnumWhereClause($criteriaValues, $func_type);
- } elseif ($criteriaValues != '') {
- // For these types we quote the value. Even if it's another type
- // (like INT), for a LIKE we always quote the value. MySQL converts
- // strings to numbers and numbers to strings as necessary
- // during the comparison
- if (preg_match('@char|binary|blob|text|set|date|time|year@i', $types)
- || mb_strpos(' ' . $func_type, 'LIKE')
- ) {
- $quot = '\'';
- } else {
- $quot = '';
- }
- // LIKE %...%
- if ($func_type == 'LIKE %...%') {
- $func_type = 'LIKE';
- $criteriaValues = '%' . $criteriaValues . '%';
- }
- if ($func_type == 'REGEXP ^...$') {
- $func_type = 'REGEXP';
- $criteriaValues = '^' . $criteriaValues . '$';
- }
- if ('IN (...)' != $func_type
- && 'NOT IN (...)' != $func_type
- && 'BETWEEN' != $func_type
- && 'NOT BETWEEN' != $func_type
- ) {
- return $backquoted_name . ' ' . $func_type . ' ' . $quot
- . $GLOBALS['dbi']->escapeString($criteriaValues) . $quot;
- }
- $func_type = str_replace(' (...)', '', $func_type);
- //Don't explode if this is already an array
- //(Case for (NOT) IN/BETWEEN.)
- if (is_array($criteriaValues)) {
- $values = $criteriaValues;
- } else {
- $values = explode(',', $criteriaValues);
- }
- // quote values one by one
- $emptyKey = false;
- foreach ($values as $key => &$value) {
- if ('' === $value) {
- $emptyKey = $key;
- $value = 'NULL';
- continue;
- }
- $value = $quot . $GLOBALS['dbi']->escapeString(trim($value))
- . $quot;
- }
- if ('BETWEEN' == $func_type || 'NOT BETWEEN' == $func_type) {
- $where = $backquoted_name . ' ' . $func_type . ' '
- . (isset($values[0]) ? $values[0] : '')
- . ' AND ' . (isset($values[1]) ? $values[1] : '');
- } else { //[NOT] IN
- if (false !== $emptyKey) {
- unset($values[$emptyKey]);
- }
- $wheres = array();
- if (!empty($values)) {
- $wheres[] = $backquoted_name . ' ' . $func_type
- . ' (' . implode(',', $values) . ')';
- }
- if (false !== $emptyKey) {
- $wheres[] = $backquoted_name . ' IS NULL';
- }
- $where = implode(' OR ', $wheres);
- if (1 < count($wheres)) {
- $where = '(' . $where . ')';
- }
- }
- } // end if
- return $where;
- }
- }
|