Merge pull request #18430 from kamil-tekiela/Remove-QBE
Feature request: Remove QBE
This commit is contained in:
commit
412835eeb4
1
.github/workflows/test-selenium.yml
vendored
1
.github/workflows/test-selenium.yml
vendored
@ -65,7 +65,6 @@ jobs:
|
||||
- "Database/Events"
|
||||
- "Database/Operations"
|
||||
- "Database/Procedures"
|
||||
- "Database/QueryByExample"
|
||||
- "Database/Structure"
|
||||
- "Database/Triggers"
|
||||
- "Export"
|
||||
|
||||
@ -30,8 +30,6 @@ Currently phpMyAdmin can:
|
||||
* administer multiple servers
|
||||
* add, edit, and remove MySQL user accounts and privileges
|
||||
* check referential integrity in MyISAM tables
|
||||
* using Query-by-example (QBE), create complex queries automatically
|
||||
connecting required tables
|
||||
* create :term:`PDF` graphics of your
|
||||
database layout
|
||||
* search globally in a database or a subset of it
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
import $ from 'jquery';
|
||||
import { AJAX } from '../modules/ajax.ts';
|
||||
import { Functions } from '../modules/functions.ts';
|
||||
|
||||
/**
|
||||
* @fileoverview function used in QBE for DB
|
||||
* @name Database Operations
|
||||
*
|
||||
* @requires jQueryUI
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ajax event handlers here for /database/qbe
|
||||
*
|
||||
* Actions Ajaxified here:
|
||||
* Select saved search
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unbind all event handlers before tearing down a page
|
||||
*/
|
||||
AJAX.registerTeardown('database/qbe.js', function () {
|
||||
$(document).off('change', 'select[name^=criteriaColumn]');
|
||||
$(document).off('change', '#searchId');
|
||||
$(document).off('click', '#saveSearch');
|
||||
$(document).off('click', '#updateSearch');
|
||||
$(document).off('click', '#deleteSearch');
|
||||
});
|
||||
|
||||
AJAX.registerOnload('database/qbe.js', function () {
|
||||
Functions.getSqlEditor($('#textSqlquery'), {}, 'none');
|
||||
|
||||
$('#tblQbe').width($('#tblQbe').parent().width());
|
||||
$('#tblQbeFooters').width($('#tblQbeFooters').parent().width());
|
||||
$('#tblQbe').on('resize', function () {
|
||||
var newWidthTblQbe = $('#textSqlquery').next().width();
|
||||
$('#tblQbe').width(newWidthTblQbe);
|
||||
$('#tblQbeFooters').width(newWidthTblQbe);
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax handler to check the corresponding 'show' checkbox when column is selected
|
||||
*/
|
||||
$(document).on('change', 'select[name^=criteriaColumn]', function () {
|
||||
if ($(this).val()) {
|
||||
var index = (/\d+/).exec($(this).attr('name'));
|
||||
$('input[name=criteriaShow\\[' + index + '\\]]').prop('checked', true);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Select saved search'
|
||||
*/
|
||||
$(document).on('change', '#searchId', function () {
|
||||
$('#action').val('load');
|
||||
$('#formQBE').trigger('submit');
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Create bookmark'
|
||||
*/
|
||||
$(document).on('click', '#saveSearch', function () {
|
||||
$('#action').val('create');
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Update bookmark'
|
||||
*/
|
||||
$(document).on('click', '#updateSearch', function () {
|
||||
$('#action').val('update');
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax event handlers for 'Delete bookmark'
|
||||
*/
|
||||
$(document).on('click', '#deleteSearch', function () {
|
||||
var question = window.sprintf(window.Messages.strConfirmDeleteQBESearch, $('#searchId').find('option:selected').text());
|
||||
if (! window.confirm(question)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#action').val('delete');
|
||||
});
|
||||
|
||||
var windowwidth = $(window).width();
|
||||
$('.jsresponsive').css('max-width', (windowwidth - 35) + 'px');
|
||||
});
|
||||
@ -362,9 +362,6 @@ class Descriptions
|
||||
'Limits number of table preferences which are stored in database, the oldest '
|
||||
. 'records are automatically removed.',
|
||||
),
|
||||
'Servers_savedsearches_desc' => __(
|
||||
'Leave blank for no QBE saved searches support, suggested: [kbd]pma__savedsearches[/kbd].',
|
||||
),
|
||||
'Servers_export_templates_desc' => __(
|
||||
'Leave blank for no export template support, suggested: [kbd]pma__export_templates[/kbd].',
|
||||
),
|
||||
@ -884,7 +881,6 @@ class Descriptions
|
||||
'Servers_host_name' => __('Server hostname'),
|
||||
'Servers_LogoutURL_name' => __('Logout URL'),
|
||||
'Servers_MaxTableUiprefs_name' => __('Maximal number of table preferences to store'),
|
||||
'Servers_savedsearches_name' => __('QBE saved searches table'),
|
||||
'Servers_export_templates_name' => __('Export templates table'),
|
||||
'Servers_central_columns_name' => __('Central columns table'),
|
||||
'Servers_only_db_name' => __('Show only listed databases'),
|
||||
|
||||
@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Database;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\Database\Qbe;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\SavedSearchesException;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Operations;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SavedSearches;
|
||||
use PhpMyAdmin\Sql;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function stripos;
|
||||
|
||||
class QueryByExampleController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
private Relation $relation,
|
||||
private DatabaseInterface $dbi,
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$GLOBALS['goto'] ??= null;
|
||||
$GLOBALS['urlParams'] ??= null;
|
||||
$GLOBALS['errorUrl'] ??= null;
|
||||
|
||||
$savedQbeSearchesFeature = $this->relation->getRelationParameters()->savedQueryByExampleSearchesFeature;
|
||||
|
||||
$savedSearchList = [];
|
||||
$savedSearch = null;
|
||||
$this->addScriptFiles(['database/qbe.js']);
|
||||
if ($savedQbeSearchesFeature !== null) {
|
||||
//Get saved search list.
|
||||
$savedSearch = new SavedSearches();
|
||||
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
|
||||
->setDbname($GLOBALS['db']);
|
||||
|
||||
$searchId = $request->getParsedBodyParam('searchId');
|
||||
if (! empty($searchId)) {
|
||||
$savedSearch->setId($searchId);
|
||||
}
|
||||
|
||||
//Action field is sent.
|
||||
if ($request->hasBodyParam('action')) {
|
||||
$savedSearch->setSearchName($request->getParsedBodyParam('searchName'));
|
||||
$action = $request->getParsedBodyParam('action');
|
||||
if ($action === 'create') {
|
||||
try {
|
||||
$savedSearch->setId(null)
|
||||
->setCriterias($request->getParsedBody())
|
||||
->save($savedQbeSearchesFeature);
|
||||
} catch (SavedSearchesException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('fieldWithError', 'searchName');
|
||||
$this->response->addJSON('message', Message::error($exception->getMessage())->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
} elseif ($action === 'update') {
|
||||
try {
|
||||
$savedSearch->setCriterias($request->getParsedBody())
|
||||
->save($savedQbeSearchesFeature);
|
||||
} catch (SavedSearchesException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('fieldWithError', 'searchName');
|
||||
$this->response->addJSON('message', Message::error($exception->getMessage())->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
} elseif ($action === 'delete') {
|
||||
try {
|
||||
$savedSearch->delete($savedQbeSearchesFeature);
|
||||
} catch (SavedSearchesException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('fieldWithError', 'searchId');
|
||||
$this->response->addJSON('message', Message::error($exception->getMessage())->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//After deletion, reset search.
|
||||
$savedSearch = new SavedSearches();
|
||||
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
|
||||
->setDbname($GLOBALS['db']);
|
||||
$_POST = [];
|
||||
} elseif ($action === 'load') {
|
||||
if (empty($searchId)) {
|
||||
//when not loading a search, reset the object.
|
||||
$savedSearch = new SavedSearches();
|
||||
$savedSearch->setUsername($GLOBALS['cfg']['Server']['user'])
|
||||
->setDbname($GLOBALS['db']);
|
||||
$_POST = [];
|
||||
} else {
|
||||
try {
|
||||
$savedSearch->load($savedQbeSearchesFeature);
|
||||
} catch (SavedSearchesException $exception) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON('fieldWithError', 'searchId');
|
||||
$this->response->addJSON('message', Message::error($exception->getMessage())->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Else, it's an "update query"
|
||||
}
|
||||
|
||||
$savedSearchList = $savedSearch->getList($savedQbeSearchesFeature);
|
||||
}
|
||||
|
||||
/**
|
||||
* A query has been submitted -> (maybe) execute it
|
||||
*/
|
||||
$hasMessageToDisplay = false;
|
||||
if ($request->hasBodyParam('submit_sql') && ! empty($GLOBALS['sql_query'])) {
|
||||
if (stripos($GLOBALS['sql_query'], 'SELECT') !== 0) {
|
||||
$hasMessageToDisplay = true;
|
||||
} else {
|
||||
$GLOBALS['goto'] = Url::getFromRoute('/database/sql');
|
||||
|
||||
$sql = new Sql(
|
||||
$this->dbi,
|
||||
$this->relation,
|
||||
new RelationCleanup($this->dbi, $this->relation),
|
||||
new Operations($this->dbi, $this->relation),
|
||||
new Transformations(),
|
||||
$this->template,
|
||||
);
|
||||
|
||||
$this->response->addHTML($sql->executeQueryAndSendQueryResponse(
|
||||
null,
|
||||
false, // is_gotofile
|
||||
$request->getParsedBodyParam('db'), // db
|
||||
null, // table
|
||||
false, // find_real_end
|
||||
null, // sql_query_for_bookmark
|
||||
null, // extra_data
|
||||
null, // message_to_show
|
||||
null, // sql_data
|
||||
$GLOBALS['goto'], // goto
|
||||
null, // disp_query
|
||||
null, // disp_message
|
||||
$GLOBALS['sql_query'], // sql_query
|
||||
null, // complete_query
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkParameters(['db']);
|
||||
|
||||
$GLOBALS['errorUrl'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database');
|
||||
$GLOBALS['errorUrl'] .= Url::getCommon(['db' => $GLOBALS['db']], '&');
|
||||
|
||||
if (! $this->hasDatabase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$GLOBALS['urlParams']['goto'] = Url::getFromRoute('/database/qbe');
|
||||
|
||||
$databaseQbe = new Qbe(
|
||||
$this->relation,
|
||||
$this->template,
|
||||
$this->dbi,
|
||||
$GLOBALS['db'],
|
||||
$savedSearchList,
|
||||
$savedSearch,
|
||||
);
|
||||
|
||||
$this->render('database/qbe/index', [
|
||||
'url_params' => $GLOBALS['urlParams'],
|
||||
'has_message_to_display' => $hasMessageToDisplay,
|
||||
'selection_form_html' => $databaseQbe->getSelectionForm(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,6 @@ final class JavaScriptMessagesController
|
||||
'strDroppingForeignKey' => __('Dropping Foreign key.'),
|
||||
'strOperationTakesLongTime' => __('This operation could take a long time. Proceed anyway?'),
|
||||
'strDropUserGroupWarning' => __('Do you really want to delete user group "%s"?'),
|
||||
'strConfirmDeleteQBESearch' => __('Do you really want to delete the search "%s"?'),
|
||||
'strConfirmNavigation' => __('You have unsaved changes; are you sure you want to leave this page?'),
|
||||
'strConfirmRowChange' => __(
|
||||
'You are trying to reduce the number of rows, but have already entered'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SavedSearchesException extends Exception
|
||||
{
|
||||
}
|
||||
@ -351,7 +351,7 @@ class Menu
|
||||
$tabs['query']['text'] = __('Query');
|
||||
$tabs['query']['icon'] = 's_db';
|
||||
$tabs['query']['route'] = '/database/multi-table-query';
|
||||
$tabs['query']['active'] = $route === '/database/multi-table-query' || $route === '/database/qbe';
|
||||
$tabs['query']['active'] = $route === '/database/multi-table-query';
|
||||
if ($numTables == 0) {
|
||||
$tabs['query']['warning'] = __('Database seems to be empty!');
|
||||
}
|
||||
|
||||
@ -1,365 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Saved searches managing
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Features\SavedQueryByExampleSearchesFeature;
|
||||
use PhpMyAdmin\Dbal\Connection;
|
||||
use PhpMyAdmin\Exceptions\SavedSearchesException;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
use function intval;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function max;
|
||||
use function min;
|
||||
|
||||
/**
|
||||
* Saved searches managing
|
||||
*/
|
||||
class SavedSearches
|
||||
{
|
||||
/**
|
||||
* Id
|
||||
*/
|
||||
private int|null $id = null;
|
||||
|
||||
/**
|
||||
* Username
|
||||
*/
|
||||
private string $username = '';
|
||||
|
||||
/**
|
||||
* DB name
|
||||
*/
|
||||
private string $dbname = '';
|
||||
|
||||
/**
|
||||
* Saved search name
|
||||
*/
|
||||
private string $searchName = '';
|
||||
|
||||
/**
|
||||
* Criterias
|
||||
*/
|
||||
private array|null $criterias = null;
|
||||
|
||||
/**
|
||||
* Setter of id
|
||||
*
|
||||
* @param int|null $searchId Id of search
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setId(int|null $searchId): static
|
||||
{
|
||||
$searchId = (int) $searchId;
|
||||
if ($searchId === 0) {
|
||||
$searchId = null;
|
||||
}
|
||||
|
||||
$this->id = $searchId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of id
|
||||
*/
|
||||
public function getId(): int|null
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter of searchName
|
||||
*
|
||||
* @param string $searchName Saved search name
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setSearchName(string $searchName): static
|
||||
{
|
||||
$this->searchName = $searchName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter of searchName
|
||||
*/
|
||||
public function getSearchName(): string
|
||||
{
|
||||
return $this->searchName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for criterias
|
||||
*
|
||||
* @param mixed[]|string $criterias Criterias of saved searches
|
||||
* @param bool $json Criterias are in JSON format
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setCriterias(array|string $criterias, bool $json = false): static
|
||||
{
|
||||
if ($json && is_string($criterias)) {
|
||||
$this->criterias = json_decode($criterias, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$aListFieldsToGet = [
|
||||
'criteriaColumn',
|
||||
'criteriaSort',
|
||||
'criteriaShow',
|
||||
'criteria',
|
||||
'criteriaAndOrRow',
|
||||
'criteriaAndOrColumn',
|
||||
'rows',
|
||||
'TableList',
|
||||
];
|
||||
|
||||
$data = [];
|
||||
|
||||
$data['criteriaColumnCount'] = count($criterias['criteriaColumn']);
|
||||
|
||||
foreach ($aListFieldsToGet as $field) {
|
||||
if (! isset($criterias[$field])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[$field] = $criterias[$field];
|
||||
}
|
||||
|
||||
/* Limit amount of rows */
|
||||
if (! isset($data['rows'])) {
|
||||
$data['rows'] = 0;
|
||||
} else {
|
||||
$data['rows'] = min(
|
||||
max(0, intval($data['rows'])),
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
for ($i = 0; $i <= $data['rows']; $i++) {
|
||||
$data['Or' . $i] = $criterias['Or' . $i];
|
||||
}
|
||||
|
||||
$this->criterias = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for criterias
|
||||
*/
|
||||
public function getCriterias(): array|null
|
||||
{
|
||||
return $this->criterias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for username
|
||||
*
|
||||
* @param string $username Username
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setUsername(string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for username
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for DB name
|
||||
*
|
||||
* @param string $dbname DB name
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setDbname(string $dbname): static
|
||||
{
|
||||
$this->dbname = $dbname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for DB name
|
||||
*/
|
||||
public function getDbname(): string
|
||||
{
|
||||
return $this->dbname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the search
|
||||
*
|
||||
* @throws SavedSearchesException
|
||||
*/
|
||||
public function save(SavedQueryByExampleSearchesFeature $savedQueryByExampleSearchesFeature): bool
|
||||
{
|
||||
if ($this->getSearchName() == null) {
|
||||
throw new SavedSearchesException(__('Please provide a name for this bookmarked search.'));
|
||||
}
|
||||
|
||||
if (
|
||||
$this->getUsername() == null
|
||||
|| $this->getDbname() == null
|
||||
|| $this->getSearchName() == null
|
||||
|| $this->getCriterias() == null
|
||||
) {
|
||||
throw new SavedSearchesException(__('Missing information to save the bookmarked search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl = Util::backquote($savedQueryByExampleSearchesFeature->database) . '.'
|
||||
. Util::backquote($savedQueryByExampleSearchesFeature->savedSearches);
|
||||
|
||||
//If it's an insert.
|
||||
if ($this->getId() === null) {
|
||||
$wheres = [
|
||||
'search_name = ' . $GLOBALS['dbi']->quoteString($this->getSearchName(), Connection::TYPE_CONTROL),
|
||||
];
|
||||
$existingSearches = $this->getList($savedQueryByExampleSearchesFeature, $wheres);
|
||||
|
||||
if ($existingSearches !== []) {
|
||||
throw new SavedSearchesException(__('An entry with this name already exists.'));
|
||||
}
|
||||
|
||||
$sqlQuery = 'INSERT INTO ' . $savedSearchesTbl
|
||||
. '(`username`, `db_name`, `search_name`, `search_data`)'
|
||||
. ' VALUES ('
|
||||
. $GLOBALS['dbi']->quoteString($this->getUsername(), Connection::TYPE_CONTROL) . ','
|
||||
. $GLOBALS['dbi']->quoteString($this->getDbname(), Connection::TYPE_CONTROL) . ','
|
||||
. $GLOBALS['dbi']->quoteString($this->getSearchName(), Connection::TYPE_CONTROL) . ','
|
||||
. $GLOBALS['dbi']->quoteString(json_encode($this->getCriterias()), Connection::TYPE_CONTROL)
|
||||
. ')';
|
||||
|
||||
$GLOBALS['dbi']->queryAsControlUser($sqlQuery);
|
||||
|
||||
$this->setId($GLOBALS['dbi']->insertId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Else, it's an update.
|
||||
$wheres = ['id != ' . $this->getId(), 'search_name = ' . $GLOBALS['dbi']->quoteString($this->getSearchName())];
|
||||
$existingSearches = $this->getList($savedQueryByExampleSearchesFeature, $wheres);
|
||||
|
||||
if ($existingSearches !== []) {
|
||||
throw new SavedSearchesException(__('An entry with this name already exists.'));
|
||||
}
|
||||
|
||||
$sqlQuery = 'UPDATE ' . $savedSearchesTbl
|
||||
. 'SET `search_name` = '
|
||||
. $GLOBALS['dbi']->quoteString($this->getSearchName(), Connection::TYPE_CONTROL) . ', '
|
||||
. '`search_data` = '
|
||||
. $GLOBALS['dbi']->quoteString(json_encode($this->getCriterias()), Connection::TYPE_CONTROL) . ' '
|
||||
. 'WHERE id = ' . $this->getId();
|
||||
|
||||
return (bool) $GLOBALS['dbi']->queryAsControlUser($sqlQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the search
|
||||
*
|
||||
* @throws SavedSearchesException
|
||||
*/
|
||||
public function delete(SavedQueryByExampleSearchesFeature $savedQueryByExampleSearchesFeature): bool
|
||||
{
|
||||
if ($this->getId() == null) {
|
||||
throw new SavedSearchesException(__('Missing information to delete the search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl = Util::backquote($savedQueryByExampleSearchesFeature->database) . '.'
|
||||
. Util::backquote($savedQueryByExampleSearchesFeature->savedSearches);
|
||||
|
||||
$sqlQuery = 'DELETE FROM ' . $savedSearchesTbl
|
||||
. 'WHERE id = ' . $GLOBALS['dbi']->quoteString((string) $this->getId(), Connection::TYPE_CONTROL);
|
||||
|
||||
return (bool) $GLOBALS['dbi']->queryAsControlUser($sqlQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the current search from an id.
|
||||
*
|
||||
* @throws SavedSearchesException
|
||||
*/
|
||||
public function load(SavedQueryByExampleSearchesFeature $savedQueryByExampleSearchesFeature): bool
|
||||
{
|
||||
if ($this->getId() == null) {
|
||||
throw new SavedSearchesException(__('Missing information to load the search.'));
|
||||
}
|
||||
|
||||
$savedSearchesTbl = Util::backquote($savedQueryByExampleSearchesFeature->database)
|
||||
. '.'
|
||||
. Util::backquote($savedQueryByExampleSearchesFeature->savedSearches);
|
||||
$sqlQuery = 'SELECT id, search_name, search_data '
|
||||
. 'FROM ' . $savedSearchesTbl . ' '
|
||||
. 'WHERE id = ' . $GLOBALS['dbi']->quoteString((string) $this->getId(), Connection::TYPE_CONTROL);
|
||||
|
||||
$resList = $GLOBALS['dbi']->queryAsControlUser($sqlQuery);
|
||||
$oneResult = $resList->fetchAssoc();
|
||||
|
||||
if ($oneResult === []) {
|
||||
throw new SavedSearchesException(__('Error while loading the search.'));
|
||||
}
|
||||
|
||||
$this->setSearchName($oneResult['search_name'])
|
||||
->setCriterias($oneResult['search_data'], true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of saved searches of a user on a DB
|
||||
*
|
||||
* @param string[] $wheres List of filters
|
||||
*
|
||||
* @return mixed[] List of saved searches or empty array on failure
|
||||
*/
|
||||
public function getList(
|
||||
SavedQueryByExampleSearchesFeature $savedQueryByExampleSearchesFeature,
|
||||
array $wheres = [],
|
||||
): array {
|
||||
if ($this->getUsername() == null || $this->getDbname() == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$savedSearchesTbl = Util::backquote($savedQueryByExampleSearchesFeature->database)
|
||||
. '.'
|
||||
. Util::backquote($savedQueryByExampleSearchesFeature->savedSearches);
|
||||
$sqlQuery = 'SELECT id, search_name '
|
||||
. 'FROM ' . $savedSearchesTbl . ' '
|
||||
. 'WHERE '
|
||||
. 'username = ' . $GLOBALS['dbi']->quoteString($this->getUsername(), Connection::TYPE_CONTROL) . ' '
|
||||
. 'AND db_name = ' . $GLOBALS['dbi']->quoteString($this->getDbname(), Connection::TYPE_CONTROL) . ' ';
|
||||
|
||||
foreach ($wheres as $where) {
|
||||
$sqlQuery .= 'AND ' . $where . ' ';
|
||||
}
|
||||
|
||||
$sqlQuery .= 'order by search_name ASC ';
|
||||
|
||||
$resList = $GLOBALS['dbi']->queryAsControlUser($sqlQuery);
|
||||
|
||||
return $resList->fetchAllKeyPair();
|
||||
}
|
||||
}
|
||||
@ -78,7 +78,6 @@ return static function (RouteCollector $routes): void {
|
||||
$routes->post('/collation', Database\Operations\CollationController::class);
|
||||
});
|
||||
$routes->get('/privileges', Database\PrivilegesController::class);
|
||||
$routes->addRoute(['GET', 'POST'], '/qbe', Database\QueryByExampleController::class);
|
||||
$routes->addRoute(['GET', 'POST'], '/routines', Database\RoutinesController::class);
|
||||
$routes->addRoute(['GET', 'POST'], '/search', Database\SearchController::class);
|
||||
$routes->addGroup('/sql', static function (RouteCollector $routes): void {
|
||||
|
||||
@ -182,15 +182,6 @@ return [
|
||||
'$dbi' => '@dbi',
|
||||
],
|
||||
],
|
||||
Database\QueryByExampleController::class => [
|
||||
'class' => Database\QueryByExampleController::class,
|
||||
'arguments' => [
|
||||
'$response' => '@response',
|
||||
'$template' => '@template',
|
||||
'$relation' => '@relation',
|
||||
'$dbi' => '@dbi',
|
||||
],
|
||||
],
|
||||
Database\RoutinesController::class => [
|
||||
'class' => Database\RoutinesController::class,
|
||||
'arguments' => [
|
||||
|
||||
@ -1010,26 +1010,6 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Controllers/Database/OperationsController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$criterias of method PhpMyAdmin\\\\SavedSearches\\:\\:setCriterias\\(\\) expects array\\|string, array\\|object\\|null given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Controllers/Database/QueryByExampleController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$searchId of method PhpMyAdmin\\\\SavedSearches\\:\\:setId\\(\\) expects int\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Controllers/Database/QueryByExampleController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$searchName of method PhpMyAdmin\\\\SavedSearches\\:\\:setSearchName\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Controllers/Database/QueryByExampleController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$db of method PhpMyAdmin\\\\Sql\\:\\:executeQueryAndSendQueryResponse\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Controllers/Database/QueryByExampleController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$routine of method PhpMyAdmin\\\\Database\\\\Routines\\:\\:getRow\\(\\) expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
@ -3010,161 +2990,6 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Database/Events.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 6
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'foreign_field' on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'foreign_table' on mixed\\.$#"
|
||||
count: 5
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'index_list' on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'ref_index_list' on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'ref_table_name' on mixed\\.$#"
|
||||
count: 5
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'table_name' on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'table_schema' on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset mixed on mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^For loop initial assignment overwrites variable \\$columnIndex\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getLeftJoinColumnCandidates\\(\\) should return array but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getMasterTable\\(\\) should return string but returns mixed\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$haystack of function stripos expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$identifier of static method PhpMyAdmin\\\\Util\\:\\:backquote\\(\\) expects string\\|Stringable\\|null, mixed given\\.$#"
|
||||
count: 7
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function htmlspecialchars expects string, mixed given\\.$#"
|
||||
count: 3
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function mb_strtoupper expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function mb_substr expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$tableName of class PhpMyAdmin\\\\Table constructor expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$oneTable of method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:loadRelationsForTable\\(\\) expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:getChildReferences\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:getForeigners\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\DatabaseInterface\\:\\:getColumns\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$table of method PhpMyAdmin\\\\DatabaseInterface\\:\\:getTableIndexes\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$uniqueColumns of method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getLeftJoinColumnCandidatesBest\\(\\) expects array\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$whereClauseColumns of method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getMasterTable\\(\\) expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$indexColumns of method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getLeftJoinColumnCandidatesBest\\(\\) expects array\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$whereClauseTables of method PhpMyAdmin\\\\Database\\\\Qbe\\:\\:getMasterTable\\(\\) expects array, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Property PhpMyAdmin\\\\Database\\\\Qbe\\:\\:\\$criteriaColumnDelete type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Property PhpMyAdmin\\\\Database\\\\Qbe\\:\\:\\$criteriaColumnInsert type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Variable variables are not allowed\\.$#"
|
||||
count: 4
|
||||
path: libraries/classes/Database/Qbe.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset 'DTD_IDENTIFIER' on mixed\\.$#"
|
||||
count: 1
|
||||
@ -7410,41 +7235,6 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Sanitize.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\SavedSearches\\:\\:getCriterias\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Offset 'criteriaColumn' does not exist on array\\|string\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Offset non\\-falsy\\-string does not exist on array\\|string\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$value of function intval expects array\\|bool\\|float\\|int\\|resource\\|string\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Property PhpMyAdmin\\\\SavedSearches\\:\\:\\$criterias \\(array\\|null\\) does not accept mixed\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Property PhpMyAdmin\\\\SavedSearches\\:\\:\\$criterias type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/SavedSearches.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$name of class PhpMyAdmin\\\\Server\\\\Plugin constructor expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
@ -9320,16 +9110,6 @@ parameters:
|
||||
count: 1
|
||||
path: test/classes/Database/DesignerTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, mixed given\\.$#"
|
||||
count: 7
|
||||
path: test/classes/Database/QbeTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringNotContainsString\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: test/classes/Database/QbeTest.php
|
||||
|
||||
-
|
||||
message: "#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'PhpMyAdmin\\\\\\\\Dbal\\\\\\\\ResultInterface' and PhpMyAdmin\\\\Dbal\\\\ResultInterface will always evaluate to true\\.$#"
|
||||
count: 1
|
||||
|
||||
@ -1202,28 +1202,6 @@
|
||||
<code>__construct</code>
|
||||
</PossiblyUnusedMethod>
|
||||
</file>
|
||||
<file src="libraries/classes/Controllers/Database/QueryByExampleController.php">
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
</InvalidArrayOffset>
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$request->getParsedBodyParam('db')]]></code>
|
||||
<code><![CDATA[$request->getParsedBodyParam('searchName')]]></code>
|
||||
<code>$searchId</code>
|
||||
</MixedArgument>
|
||||
<MixedAssignment>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
<code>$action</code>
|
||||
<code>$searchId</code>
|
||||
</MixedAssignment>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$request->getParsedBody()]]></code>
|
||||
<code><![CDATA[$request->getParsedBody()]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyUnusedMethod>
|
||||
<code>__construct</code>
|
||||
</PossiblyUnusedMethod>
|
||||
</file>
|
||||
<file src="libraries/classes/Controllers/Database/RoutinesController.php">
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
@ -5245,180 +5223,6 @@
|
||||
<code>isSuccess</code>
|
||||
</PossiblyNullReference>
|
||||
</file>
|
||||
<file src="libraries/classes/Database/Qbe.php">
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$this->criteriaTables]]></code>
|
||||
<code><![CDATA[$this->criteriaTables]]></code>
|
||||
<code><![CDATA[$this->criteriaTables]]></code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<MixedArgument>
|
||||
<code>$column</code>
|
||||
<code>$columns[$columnIndex]</code>
|
||||
<code><![CDATA[$eachColumn['Field']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_field']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code>$indexColumns</code>
|
||||
<code>$name</code>
|
||||
<code>$oneField</code>
|
||||
<code><![CDATA[$oneKey['ref_index_list'][$index]]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code>$oneTable</code>
|
||||
<code>$oneTable</code>
|
||||
<code>$oneTable</code>
|
||||
<code>$table</code>
|
||||
<code>$table</code>
|
||||
<code>$table</code>
|
||||
<code><![CDATA[$this->criteria[$columnIndex]]]></code>
|
||||
<code><![CDATA[$this->criteria[$columnIndex]]]></code>
|
||||
<code><![CDATA[$this->formAliases[$columnIndex]]]></code>
|
||||
<code><![CDATA[$this->formAndOrCols[$lastOrWhere]]]></code>
|
||||
<code><![CDATA[$this->formAndOrCols[$lastWhere]]]></code>
|
||||
<code><![CDATA[$this->formCriterions[$newColumnCount]]]></code>
|
||||
<code>$tmpCriteria</code>
|
||||
<code>$uniqueColumns</code>
|
||||
<code>$value</code>
|
||||
<code>$whereClauseColumns</code>
|
||||
<code>$whereClauseTables</code>
|
||||
</MixedArgument>
|
||||
<MixedArgumentTypeCoercion>
|
||||
<code>$selectClauses</code>
|
||||
<code>$table</code>
|
||||
<code>$table</code>
|
||||
</MixedArgumentTypeCoercion>
|
||||
<MixedArrayAccess>
|
||||
<code><![CDATA[$foreigner['foreign_field']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code><![CDATA[$foreigner['foreign_table']]]></code>
|
||||
<code><![CDATA[$oneKey['index_list']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_index_list']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_index_list'][$index]]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code><![CDATA[$oneKey['ref_table_name']]]></code>
|
||||
<code><![CDATA[$reference['table_name']]]></code>
|
||||
<code><![CDATA[$reference['table_schema']]]></code>
|
||||
</MixedArrayAccess>
|
||||
<MixedArrayOffset>
|
||||
<code>$checkedTables[$table]</code>
|
||||
<code>$finalized[$foreignTable]</code>
|
||||
<code>$finalized[$foreignTable]</code>
|
||||
<code>$finalized[$foreignTable]</code>
|
||||
<code>$finalized[$masterTable]</code>
|
||||
<code>$finalized[$masterTable]</code>
|
||||
<code>$finalized[$masterTable]</code>
|
||||
<code><![CDATA[$foreignTables[$foreigner['foreign_table']]]]></code>
|
||||
<code><![CDATA[$foreignTables[$oneKey['ref_table_name']]]]></code>
|
||||
<code><![CDATA[$oneKey['ref_index_list'][$index]]]></code>
|
||||
<code>$relations[$masterTable]</code>
|
||||
<code><![CDATA[$relations[$oneTable][$foreigner['foreign_table']]]]></code>
|
||||
<code><![CDATA[$relations[$oneTable][$oneKey['ref_table_name']]]]></code>
|
||||
<code><![CDATA[$this->formAndOrCols[$lastWhere]]]></code>
|
||||
<code>$tsize[$table]</code>
|
||||
</MixedArrayOffset>
|
||||
<MixedArrayTypeCoercion>
|
||||
<code>$tsize[$table]</code>
|
||||
</MixedArrayTypeCoercion>
|
||||
<MixedAssignment>
|
||||
<code><![CDATA[$GLOBALS[${'cur' . $or}][$newColumnCount]]]></code>
|
||||
<code>$clause</code>
|
||||
<code>$clause</code>
|
||||
<code>$column</code>
|
||||
<code>$columnReferences</code>
|
||||
<code>$finalized[$foreignTable]</code>
|
||||
<code>$finalized[$masterTable]</code>
|
||||
<code>$foreignData</code>
|
||||
<code>$foreignTable</code>
|
||||
<code><![CDATA[$foreignTables[$foreigner['foreign_table']]]]></code>
|
||||
<code><![CDATA[$foreignTables[$oneKey['ref_table_name']]]]></code>
|
||||
<code>$foreigner</code>
|
||||
<code>$foreigner</code>
|
||||
<code>$index</code>
|
||||
<code>$indexColumns</code>
|
||||
<code>$isWhere</code>
|
||||
<code>$masterTable</code>
|
||||
<code>$name</code>
|
||||
<code>$oneField</code>
|
||||
<code>$oneKey</code>
|
||||
<code>$oneKey</code>
|
||||
<code>$oneTable</code>
|
||||
<code>$oneTable</code>
|
||||
<code>$oneTable</code>
|
||||
<code>$reference</code>
|
||||
<code>$result</code>
|
||||
<code>$select</code>
|
||||
<code>$selectClauses[]</code>
|
||||
<code>$table</code>
|
||||
<code>$table</code>
|
||||
<code>$table</code>
|
||||
<code>$tmpCriteria</code>
|
||||
<code>$uniqueColumns</code>
|
||||
<code>$value</code>
|
||||
<code>$whereClauseColumns</code>
|
||||
<code>$whereClauseTables</code>
|
||||
</MixedAssignment>
|
||||
<MixedInferredReturnType>
|
||||
<code>mixed[]</code>
|
||||
<code>string</code>
|
||||
</MixedInferredReturnType>
|
||||
<MixedOperand>
|
||||
<code>$clause</code>
|
||||
<code>$columns[$columnIndex]</code>
|
||||
<code>$select</code>
|
||||
<code><![CDATA[$selected['and'] ?? '']]></code>
|
||||
<code><![CDATA[$selected['or'] ?? '']]></code>
|
||||
<code>$sort[$columnIndex]</code>
|
||||
<code><![CDATA[$this->formAndOrRows[$rowIndex]]]></code>
|
||||
<code><![CDATA[$this->formColumns[$columnIndex]]]></code>
|
||||
<code><![CDATA[$this->formColumns[$columnIndex]]]></code>
|
||||
<code><![CDATA[$this->formCriterions[$columnIndex]]]></code>
|
||||
</MixedOperand>
|
||||
<MixedReturnStatement>
|
||||
<code>$candidateColumns</code>
|
||||
<code>$result</code>
|
||||
<code>$result</code>
|
||||
<code>reset($candidateColumns)</code>
|
||||
<code>reset($candidateColumns)</code>
|
||||
</MixedReturnStatement>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$_POST['criteriaColumn'][$colInd]]]></code>
|
||||
<code><![CDATA[$_POST['criteriaColumn'][$columnIndex]]]></code>
|
||||
<code>$selected</code>
|
||||
<code>$tmpAlias</code>
|
||||
<code>$tmpOr</code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyInvalidCast>
|
||||
<code><![CDATA[$_POST['criteriaColumn'][$colInd]]]></code>
|
||||
<code>$selected</code>
|
||||
</PossiblyInvalidCast>
|
||||
<PossiblyInvalidOperand>
|
||||
<code><![CDATA[$_POST['Or' . $rowIndex][$columnIndex]]]></code>
|
||||
</PossiblyInvalidOperand>
|
||||
<PossiblyInvalidPropertyAssignmentValue>
|
||||
<code><![CDATA[$_POST['criteria'] ?? array_fill(0, $criteriaColumnCount, '')]]></code>
|
||||
<code><![CDATA[$_POST['criteriaAndOrColumn'] ?? array_fill(0, $criteriaColumnCount, '')]]></code>
|
||||
<code><![CDATA[$_POST['criteriaAndOrRow'] ?? array_fill(0, $criteriaColumnCount, '')]]></code>
|
||||
<code><![CDATA[$_POST['criteriaRowDelete'] ?? array_fill(0, $criteriaColumnCount, '')]]></code>
|
||||
<code><![CDATA[$_POST['criteriaRowInsert'] ?? array_fill(0, $criteriaColumnCount, '')]]></code>
|
||||
<code><![CDATA[$_POST['prev_criteria'] ?? []]]></code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<PossiblyNullArgument>
|
||||
<code>$table</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullOperand>
|
||||
<code><![CDATA[$index['Column_name']]]></code>
|
||||
</PossiblyNullOperand>
|
||||
<RedundantPropertyInitializationCheck>
|
||||
<code><![CDATA[isset($this->formAndOrRows)]]></code>
|
||||
</RedundantPropertyInitializationCheck>
|
||||
<RiskyCast>
|
||||
<code><![CDATA[$this->formSortOrders[$newColumnCount] = $_POST['criteriaSortOrder'][$colInd]]]></code>
|
||||
</RiskyCast>
|
||||
</file>
|
||||
<file src="libraries/classes/Database/Routines.php">
|
||||
<DeprecatedMethod>
|
||||
<code>escapeString</code>
|
||||
@ -11918,33 +11722,6 @@
|
||||
<code><![CDATA[isset($_COOKIE[$key]) && ! is_string($_COOKIE[$key])]]></code>
|
||||
</TypeDoesNotContainType>
|
||||
</file>
|
||||
<file src="libraries/classes/SavedSearches.php">
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$criterias['criteriaColumn']]]></code>
|
||||
</MixedArgument>
|
||||
<MixedAssignment>
|
||||
<code>$data[$field]</code>
|
||||
<code><![CDATA[$data['Or' . $i]]]></code>
|
||||
<code><![CDATA[$this->criterias]]></code>
|
||||
</MixedAssignment>
|
||||
<PossiblyInvalidArrayOffset>
|
||||
<code><![CDATA[$criterias['Or' . $i]]]></code>
|
||||
<code><![CDATA[$criterias['criteriaColumn']]]></code>
|
||||
</PossiblyInvalidArrayOffset>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$oneResult['search_data']]]></code>
|
||||
<code><![CDATA[$oneResult['search_name']]]></code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullOperand>
|
||||
<code><![CDATA[$this->getId()]]></code>
|
||||
<code><![CDATA[$this->getId()]]></code>
|
||||
</PossiblyNullOperand>
|
||||
<PossiblyUnusedReturnValue>
|
||||
<code>bool</code>
|
||||
<code>bool</code>
|
||||
<code>bool</code>
|
||||
</PossiblyUnusedReturnValue>
|
||||
</file>
|
||||
<file src="libraries/classes/Server/Plugin.php">
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$state['authVersion'] ?? null]]></code>
|
||||
@ -14342,20 +14119,6 @@
|
||||
<code>$result</code>
|
||||
</MixedAssignment>
|
||||
</file>
|
||||
<file src="test/classes/Database/QbeTest.php">
|
||||
<MixedArgument>
|
||||
<code>$actual</code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortRow', [])]]></code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortRow', [])]]></code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortRow', [])]]></code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1, 'ASC'])]]></code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1])]]></code>
|
||||
<code><![CDATA[$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1])]]></code>
|
||||
</MixedArgument>
|
||||
<MixedAssignment>
|
||||
<code>$actual</code>
|
||||
</MixedAssignment>
|
||||
</file>
|
||||
<file src="test/classes/Database/RoutinesTest.php">
|
||||
<MixedAssignment>
|
||||
<code>$_POST[$key]</code>
|
||||
|
||||
@ -797,11 +797,6 @@ textarea {
|
||||
border-top: 0.1em solid silver;
|
||||
}
|
||||
|
||||
#qbe_div_table_list,
|
||||
#qbe_div_sql_query {
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.sqlvalidate {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
|
||||
@ -1047,11 +1047,6 @@ li {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#qbe_div_table_list,
|
||||
#qbe_div_sql_query {
|
||||
float: left;
|
||||
}
|
||||
|
||||
kbd {
|
||||
color: $body-color;
|
||||
background-color: transparent;
|
||||
|
||||
@ -784,11 +784,6 @@ textarea {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#qbe_div_table_list,
|
||||
#qbe_div_sql_query {
|
||||
float: left;
|
||||
}
|
||||
|
||||
kbd {
|
||||
color: $main-color;
|
||||
background-color: transparent;
|
||||
|
||||
@ -977,11 +977,6 @@ textarea {
|
||||
border-top: 0.1em solid silver;
|
||||
}
|
||||
|
||||
#qbe_div_table_list,
|
||||
#qbe_div_sql_query {
|
||||
float: left;
|
||||
}
|
||||
|
||||
kbd {
|
||||
color: $main-color;
|
||||
background-color: transparent;
|
||||
|
||||
@ -1,17 +1,3 @@
|
||||
<ul class="nav nav-pills m-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active disableAjax" href="{{ url('/database/multi-table-query', {'db': db}) }}">
|
||||
{% trans 'Multi-table query' %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disableAjax" href="{{ url('/database/qbe', {'db': db}) }}">
|
||||
{% trans 'Query by example' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-sm btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#queryWindow" aria-expanded="true" aria-controls="queryWindow">
|
||||
{% trans 'Query window' %}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
<td class="text-center">
|
||||
<select name="criteriaColumn[{{ column_number }}]" size="1">
|
||||
<option value=""> </option>
|
||||
{% for column in column_names %}
|
||||
<option value="{{ column }}"
|
||||
{%- if column is same as(selected) %} selected="selected"{% endif %}>
|
||||
{{- column -}}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
@ -1,23 +0,0 @@
|
||||
<ul class="nav nav-pills m-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disableAjax" href="{{ url('/database/multi-table-query', url_params) }}">
|
||||
{% trans 'Multi-table query' %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active disableAjax" href="{{ url('/database/qbe', url_params) }}">
|
||||
{% trans 'Query by example' %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% apply format('<a href="' ~ url('/database/designer', url_params|merge({query: true})) ~ '">', '</a>')|notice %}
|
||||
{% trans 'Switch to %svisual builder%s' %}
|
||||
{% endapply %}
|
||||
|
||||
{% if has_message_to_display %}
|
||||
{{ 'You have to choose at least one column to display!'|trans|error }}
|
||||
{% endif %}
|
||||
|
||||
{{ selection_form_html|raw }}
|
||||
@ -1,28 +0,0 @@
|
||||
<td class="value text-nowrap">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tr>
|
||||
<td class="value text-nowrap p-0">
|
||||
<small>{% trans 'Ins:' %}</small>
|
||||
<input type="checkbox" name="criteriaRowInsert[{{ row_index }}]" aria-label="{% trans 'Insert' %}">
|
||||
</td>
|
||||
<td class="value p-0">
|
||||
<strong>{% trans 'And:' %}</strong>
|
||||
</td>
|
||||
<td class="p-0">
|
||||
<input type="radio" name="criteriaAndOrRow[{{ row_index }}]" value="and"{{ checked_options.and ? ' checked' }} aria-label="{% trans 'And' %}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="value text-nowrap p-0">
|
||||
<small>{% trans 'Del:' %}</small>
|
||||
<input type="checkbox" name="criteriaRowDelete[{{ row_index }}]" aria-label="{% trans 'Delete' %}">
|
||||
</td>
|
||||
<td class="value p-0">
|
||||
<strong>{% trans 'Or:' %}</strong>
|
||||
</td>
|
||||
<td class="p-0">
|
||||
<input type="radio" name="criteriaAndOrRow[{{ row_index }}]" value="or"{{ checked_options.or ? ' checked' }} aria-label="{% trans 'Or' %}">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
@ -1,118 +0,0 @@
|
||||
<form action="{{ url('/database/qbe') }}" method="post" id="formQBE" class="lock-page">
|
||||
{{ get_hidden_inputs(url_params) }}
|
||||
|
||||
<div class="w-100">
|
||||
<fieldset class="pma-fieldset">
|
||||
|
||||
{{ saved_searches_field|raw }}
|
||||
|
||||
<div class="table-responsive jsresponsive">
|
||||
<table class="table table-borderless table-sm w-auto">
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Column:' %}</th>
|
||||
{{ column_names_row|raw }}
|
||||
</tr>
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Alias:' %}</th>
|
||||
{{ column_alias_row|raw }}
|
||||
</tr>
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Show:' %}</th>
|
||||
{{ show_row|raw }}
|
||||
</tr>
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Sort:' %}</th>
|
||||
{{ sort_row|raw }}
|
||||
</tr>
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Sort order:' %}</th>
|
||||
{{ sort_order|raw }}
|
||||
</tr>
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Criteria:' %}</th>
|
||||
{{ criteria_input_box_row|raw }}
|
||||
</tr>
|
||||
|
||||
{{ ins_del_and_or_criteria_rows|raw }}
|
||||
|
||||
<tr class="noclick">
|
||||
<th>{% trans 'Modify:' %}</th>
|
||||
{{ modify_columns_row|raw }}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<fieldset class="pma-fieldset tblFooters">
|
||||
<div class="float-start">
|
||||
<label for="criteriaRowAddSelect">{% trans 'Add/Delete criteria rows:' %}</label>
|
||||
<select size="1" name="criteriaRowAdd" id="criteriaRowAddSelect">
|
||||
<option value="-3">-3</option>
|
||||
<option value="-2">-2</option>
|
||||
<option value="-1">-1</option>
|
||||
<option value="0" selected>0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="float-start">
|
||||
<label for="criteriaColumnAddSelect">{% trans 'Add/Delete columns:' %}</label>
|
||||
<select size="1" name="criteriaColumnAdd" id="criteriaColumnAddSelect">
|
||||
<option value="-3">-3</option>
|
||||
<option value="-2">-2</option>
|
||||
<option value="-1">-1</option>
|
||||
<option value="0" selected>0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="float-start">
|
||||
<input class="btn btn-secondary" type="submit" name="modify" value="{% trans 'Update query' %}">
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="float-start w-100">
|
||||
<fieldset class="pma-fieldset">
|
||||
<legend>{% trans 'Use tables' %}</legend>
|
||||
|
||||
<select class="resize-vertical" name="TableList[]" id="listTable" size="{{ criteria_tables|length > 30 ? '15' : '7' }}" aria-label="{% trans 'Use tables' %}" multiple>
|
||||
{% for table, selected in criteria_tables %}
|
||||
<option value="{{ table }}"{{ selected|raw }}>{{ table }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="pma-fieldset tblFooters">
|
||||
<input class="btn btn-secondary" type="submit" name="modify" value="{% trans 'Update query' %}">
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form action="{{ url('/database/qbe') }}" method="post" class="lock-page">
|
||||
{{ get_hidden_inputs(db) }}
|
||||
<input type="hidden" name="submit_sql" value="1">
|
||||
|
||||
<div class="float-start w-50">
|
||||
<fieldset class="pma-fieldset" id="tblQbe">
|
||||
<legend>{{ 'SQL query on database <b>%s</b>:'|trans|format(db_link)|raw }}</legend>
|
||||
|
||||
<textarea cols="80" name="sql_query" id="textSqlquery" rows="{{ criteria_tables|length > 30 ? '15' : '7' }}" dir="ltr" aria-label="{% trans 'SQL query' %}">
|
||||
{{- sql_query -}}
|
||||
</textarea>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="pma-fieldset tblFooters" id="tblQbeFooters">
|
||||
<input class="btn btn-primary" type="submit" value="{% trans 'Submit query' %}">
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,10 +0,0 @@
|
||||
<td class="text-center">
|
||||
<select name="criteriaSortOrder[{{ column_number }}]">
|
||||
<option value="1000"> </option>
|
||||
{% for i in 1..total_column_count %}
|
||||
<option value="{{ i }}"{{ i == sort_order ? ' selected="selected"' }}>
|
||||
{{ i }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
@ -1,9 +0,0 @@
|
||||
<td class="text-center">
|
||||
<select style="width:{{ real_width }}" name="criteriaSort[{{ column_number }}]" size="1">
|
||||
<option value=""> </option>
|
||||
<option value="ASC"
|
||||
{{- selected == 'ASC' ? ' selected="selected"' }}>{% trans 'Ascending' %}</option>
|
||||
<option value="DESC"
|
||||
{{- selected == 'DESC' ? ' selected="selected"' }}>{% trans 'Descending' %}</option>
|
||||
</select>
|
||||
</td>
|
||||
@ -1,269 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Database;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Database\Qbe;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
|
||||
/** @covers \PhpMyAdmin\Database\Qbe */
|
||||
class QbeTest extends AbstractTestCase
|
||||
{
|
||||
public function testGetSortSelectCell(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertStringContainsString(
|
||||
'style="width:12ex" name="criteriaSort[1]"',
|
||||
$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1]),
|
||||
);
|
||||
$this->assertStringNotContainsString(
|
||||
'selected="selected"',
|
||||
$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1]),
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
'value="ASC" selected="selected">',
|
||||
$this->callFunction($object, Qbe::class, 'getSortSelectCell', [1, 'ASC']),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetSortRow(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertStringContainsString(
|
||||
'name="criteriaSort[0]"',
|
||||
$this->callFunction($object, Qbe::class, 'getSortRow', []),
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
'name="criteriaSort[1]"',
|
||||
$this->callFunction($object, Qbe::class, 'getSortRow', []),
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
'name="criteriaSort[2]"',
|
||||
$this->callFunction($object, Qbe::class, 'getSortRow', []),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetShowRow(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
'<td class="text-center"><input type'
|
||||
. '="checkbox" name="criteriaShow[0]"></td><td class="text-center">'
|
||||
. '<input type="checkbox" name="criteriaShow[1]"></td><td '
|
||||
. 'class="text-center"><input type="checkbox" name="criteriaShow[2]">'
|
||||
. '</td>',
|
||||
$this->callFunction($object, Qbe::class, 'getShowRow', []),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCriteriaInputboxRow(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
'<td class="text-center">'
|
||||
. '<input type="hidden" name="prev_criteria[0]" value="">'
|
||||
. '<input type="text" name="criteria[0]" value="" class="textfield" '
|
||||
. 'style="width: 12ex" size="20"></td><td class="text-center">'
|
||||
. '<input type="hidden" name="prev_criteria[1]" value="">'
|
||||
. '<input type="text" name="criteria[1]" value="" class="textfield" '
|
||||
. 'style="width: 12ex" size="20"></td><td class="text-center">'
|
||||
. '<input type="hidden" name="prev_criteria[2]" value="">'
|
||||
. '<input type="text" name="criteria[2]" value="" class="textfield" '
|
||||
. 'style="width: 12ex" size="20"></td>',
|
||||
$this->callFunction($object, Qbe::class, 'getCriteriaInputboxRow', []),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetAndOrColCell(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
'<td class="text-center"><strong>Or:</strong><input type="radio" '
|
||||
. 'name="criteriaAndOrColumn[1]" value="or"> <strong>And:'
|
||||
. '</strong><input type="radio" name="criteriaAndOrColumn[1]" value='
|
||||
. '"and"><br>Ins<input type="checkbox" name="criteriaColumnInsert'
|
||||
. '[1]"> Del<input type="checkbox" '
|
||||
. 'name="criteriaColumnDelete[1]"></td>',
|
||||
$this->callFunction($object, Qbe::class, 'getAndOrColCell', [1]),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetModifyColumnsRow(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
'<td class="text-center"><strong>'
|
||||
. 'Or:</strong><input type="radio" name="criteriaAndOrColumn[0]" value'
|
||||
. '="or"> <strong>And:</strong><input type="radio" name='
|
||||
. '"criteriaAndOrColumn[0]" value="and" checked="checked"><br>Ins'
|
||||
. '<input type="checkbox" name="criteriaColumnInsert[0]"> '
|
||||
. 'Del<input type="checkbox" name="criteriaColumnDelete[0]"></td><td '
|
||||
. 'class="text-center"><strong>Or:</strong><input type="radio" name="'
|
||||
. 'criteriaAndOrColumn[1]" value="or"> <strong>And:'
|
||||
. '</strong><input type="radio" name="criteriaAndOrColumn[1]" value='
|
||||
. '"and" checked="checked"><br>Ins<input type="checkbox" name='
|
||||
. '"criteriaColumnInsert[1]"> Del<input type="checkbox" '
|
||||
. 'name="criteriaColumnDelete[1]"></td><td class="text-center"><br>Ins'
|
||||
. '<input type="checkbox" name="criteriaColumnInsert[2]"> '
|
||||
. 'Del<input type="checkbox" name="criteriaColumnDelete[2]"></td>',
|
||||
$this->callFunction($object, Qbe::class, 'getModifyColumnsRow', []),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetInputboxRow(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
'<td class="text-center"><input type="text" name="Or2[0]" value="" class='
|
||||
. '"textfield" style="width: 12ex" size="20"></td><td class="text-center">'
|
||||
. '<input type="text" name="Or2[1]" value="" class="textfield" '
|
||||
. 'style="width: 12ex" size="20"></td><td class="text-center"><input '
|
||||
. 'type="text" name="Or2[2]" value="" class="textfield" style="width: '
|
||||
. '12ex" size="20"></td>',
|
||||
$this->callFunction($object, Qbe::class, 'getInputboxRow', [2]),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetInsDelAndOrCriteriaRows(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$actual = $this->callFunction($object, Qbe::class, 'getInsDelAndOrCriteriaRows', [2, 3]);
|
||||
|
||||
$this->assertStringContainsString('<tr class="noclick">', $actual);
|
||||
$this->assertStringContainsString(
|
||||
'<td class="text-center"><input type="text" '
|
||||
. 'name="Or0[0]" value="" class="textfield" style="width: 12ex" '
|
||||
. 'size="20"></td><td class="text-center"><input type="text" name="Or0[1]" '
|
||||
. 'value="" class="textfield" style="width: 12ex" size="20"></td><td '
|
||||
. 'class="text-center"><input type="text" name="Or0[2]" value="" class='
|
||||
. '"textfield" style="width: 12ex" size="20"></td></tr>',
|
||||
$actual,
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetSelectClause(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals('', $this->callFunction($object, Qbe::class, 'getSelectClause', []));
|
||||
}
|
||||
|
||||
public function testGetWhereClause(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals('', $this->callFunction($object, Qbe::class, 'getWhereClause', []));
|
||||
}
|
||||
|
||||
public function testGetOrderByClause(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals('', $this->callFunction($object, Qbe::class, 'getOrderByClause', []));
|
||||
}
|
||||
|
||||
public function testGetIndexes(): void
|
||||
{
|
||||
$dbiDummy = $this->createDbiDummy();
|
||||
$dbiDummy->addResult('SHOW INDEXES FROM `pma_test`.```table1```', []);
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface($dbiDummy);
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
['unique' => [], 'index' => []],
|
||||
$this->callFunction(
|
||||
$object,
|
||||
Qbe::class,
|
||||
'getIndexes',
|
||||
[['`table1`', 'table2'], ['column1', 'column2', 'column3'], ['column2']],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetLeftJoinColumnCandidates(): void
|
||||
{
|
||||
$dbiDummy = $this->createDbiDummy();
|
||||
$dbiDummy->addSelectDb('pma_test');
|
||||
$dbiDummy->addResult('SHOW INDEXES FROM `pma_test`.```table1```', []);
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface($dbiDummy);
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
[0 => 'column2'],
|
||||
$this->callFunction(
|
||||
$object,
|
||||
Qbe::class,
|
||||
'getLeftJoinColumnCandidates',
|
||||
[['`table1`', 'table2'], ['column1', 'column2', 'column3'], ['column2']],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetMasterTable(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$this->callFunction(
|
||||
$object,
|
||||
Qbe::class,
|
||||
'getMasterTable',
|
||||
[['table1', 'table2'], ['column1', 'column2', 'column3'], ['column2'], ['qbe_test']],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetWhereClauseTablesAndColumns(): void
|
||||
{
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface();
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$_POST['criteriaColumn'] = ['table1.id', 'table1.value', 'table1.name', 'table1.deleted'];
|
||||
$this->assertEquals(
|
||||
['where_clause_tables' => [], 'where_clause_columns' => []],
|
||||
$this->callFunction($object, Qbe::class, 'getWhereClauseTablesAndColumns', []),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetFromClause(): void
|
||||
{
|
||||
$GLOBALS['db'] = 'pma_test';
|
||||
$dbiDummy = $this->createDbiDummy();
|
||||
$createTableStatement = 'CREATE TABLE `table1` (`id` int(11) NOT NULL,`value` int(11) NOT NULL,'
|
||||
. 'PRIMARY KEY (`id`,`value`),KEY `value` (`value`)) ENGINE=InnoDB DEFAULT CHARSET=latin1';
|
||||
$dbiDummy->addResult('SHOW CREATE TABLE `pma_test`.`table1`', [[$createTableStatement]]);
|
||||
$dbiDummy->addSelectDb('pma_test');
|
||||
$dbiDummy->addResult('SHOW CREATE TABLE `pma_test`.`table1`', [[$createTableStatement]]);
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface($dbiDummy);
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$_POST['criteriaColumn'] = ['table1.id', 'table1.value', 'table1.name', 'table1.deleted'];
|
||||
$this->assertEquals('`table1`', $this->callFunction($object, Qbe::class, 'getFromClause', [['`table1`.`id`']]));
|
||||
}
|
||||
|
||||
public function testGetSQLQuery(): void
|
||||
{
|
||||
$GLOBALS['db'] = 'pma_test';
|
||||
$dbiDummy = $this->createDbiDummy();
|
||||
$createTableStatement = 'CREATE TABLE `table1` (`id` int(11) NOT NULL,`value` int(11) NOT NULL,'
|
||||
. 'PRIMARY KEY (`id`,`value`),KEY `value` (`value`)) ENGINE=InnoDB DEFAULT CHARSET=latin1';
|
||||
$dbiDummy->addResult('SHOW CREATE TABLE `pma_test`.`table1`', [[$createTableStatement]]);
|
||||
$dbiDummy->addSelectDb('pma_test');
|
||||
$dbiDummy->addResult('SHOW CREATE TABLE `pma_test`.`table1`', [[$createTableStatement]]);
|
||||
$GLOBALS['dbi'] = $this->createDatabaseInterface($dbiDummy);
|
||||
$object = new Qbe(new Relation($GLOBALS['dbi']), new Template(), $GLOBALS['dbi'], 'pma_test');
|
||||
$_POST['criteriaColumn'] = ['table1.id', 'table1.value', 'table1.name', 'table1.deleted'];
|
||||
$this->assertEquals(
|
||||
'FROM `table1`' . "\n",
|
||||
$this->callFunction($object, Qbe::class, 'getSQLQuery', [['`table1`.`id`']]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Selenium\Database;
|
||||
|
||||
use PhpMyAdmin\Tests\Selenium\TestBase;
|
||||
|
||||
use function trim;
|
||||
|
||||
/** @coversNothing */
|
||||
class QueryByExampleTest extends TestBase
|
||||
{
|
||||
/**
|
||||
* Setup the browser environment to run the selenium test case
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->dbQuery(
|
||||
'USE `' . $this->databaseName . '`;'
|
||||
. 'CREATE TABLE `test_table` ('
|
||||
. ' `id` int(11) NOT NULL AUTO_INCREMENT,'
|
||||
. ' `val` int(11) NOT NULL,'
|
||||
. ' PRIMARY KEY (`id`)'
|
||||
. ');'
|
||||
. 'INSERT INTO `test_table` (val) VALUES (2), (6), (5), (3), (4), (4), (5);',
|
||||
);
|
||||
|
||||
$this->login();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test typing a SQL query on Server SQL page and submitting it
|
||||
*/
|
||||
public function testQueryByExample(): void
|
||||
{
|
||||
$this->navigateDatabase($this->databaseName);
|
||||
|
||||
$this->waitForElement('partialLinkText', 'Query')->click();
|
||||
$this->waitAjax();
|
||||
|
||||
$this->waitForElement('partialLinkText', 'Query by example')->click();
|
||||
$this->waitAjax();
|
||||
|
||||
/* Select Columns to be used in the query */
|
||||
$this->selectByValue(
|
||||
$this->waitForElement('name', 'criteriaColumn[0]'),
|
||||
'`test_table`.`id`',
|
||||
);
|
||||
|
||||
$this->selectByValue(
|
||||
$this->waitForElement('name', 'criteriaColumn[1]'),
|
||||
'`test_table`.`val`',
|
||||
);
|
||||
|
||||
/* Set aliases for the columns */
|
||||
$this->waitForElement('name', 'criteriaAlias[0]')->sendKeys('ID');
|
||||
$this->waitForElement('name', 'criteriaAlias[1]')->sendKeys('VAL');
|
||||
|
||||
/* Set Sort orders */
|
||||
$this->selectByLabel(
|
||||
$this->waitForElement('name', 'criteriaSort[0]'),
|
||||
'Descending',
|
||||
);
|
||||
|
||||
$this->selectByLabel(
|
||||
$this->waitForElement('name', 'criteriaSort[1]'),
|
||||
'Ascending',
|
||||
);
|
||||
|
||||
/* Select sort order amongst columns */
|
||||
$this->selectByValue(
|
||||
$this->waitForElement('name', 'criteriaSortOrder[0]'),
|
||||
'2',
|
||||
);
|
||||
|
||||
$this->selectByValue(
|
||||
$this->waitForElement('name', 'criteriaSortOrder[1]'),
|
||||
'1',
|
||||
);
|
||||
|
||||
/* Set criteria conditions */
|
||||
$this->waitForElement('name', 'criteria[0]')->sendKeys('> 1');
|
||||
$this->waitForElement('name', 'criteria[1]')->sendKeys('< 6');
|
||||
|
||||
/* Change operator to AND */
|
||||
/*
|
||||
//TODO: Needs to be re-done
|
||||
$radioElements = $this->elements(
|
||||
$this->using('css selector')->sendKeys('input[name="criteriaAndOrColumn[0]"]')
|
||||
);
|
||||
if (count($radioElements) > 2) {
|
||||
$radioElements[1]->click();
|
||||
}
|
||||
*/
|
||||
|
||||
/* Update Query in the editor */
|
||||
$updateQueryButton = $this->byCssSelector('.tblFooters > input[name=modify]');
|
||||
$this->scrollToElement($updateQueryButton);
|
||||
$updateQueryButton->click();
|
||||
$this->waitAjax();
|
||||
|
||||
$expected = 'SELECT `test_table`.`id` AS `ID`, `test_table`.`val` AS `VAL`'
|
||||
. "\nFROM `test_table`"
|
||||
. "\nWHERE ((`test_table`.`id` > 1) AND (`test_table`.`val` < 6))"
|
||||
. "\nORDER BY `test_table`.`val` ASC, `test_table`.`id` DESC";
|
||||
$actual = trim((string) $this->waitForElement('id', 'textSqlquery')->getAttribute('value'));
|
||||
|
||||
/* Compare generated query */
|
||||
$this->assertEquals($expected, $actual);
|
||||
|
||||
/* Submit the query */
|
||||
$submitButton = $this->waitForElement('cssSelector', '#tblQbeFooters > input[type=submit]');
|
||||
$this->scrollToElement($submitButton);
|
||||
$submitButton->click();
|
||||
$this->waitAjax();
|
||||
|
||||
$this->waitForElement('cssSelector', 'table.table_results');
|
||||
|
||||
/* Assert Row 1 */
|
||||
$this->assertEquals(
|
||||
4,
|
||||
$this->getCellByTableClass('table_results', 1, 5),
|
||||
);
|
||||
$this->assertEquals(
|
||||
3,
|
||||
$this->getCellByTableClass('table_results', 1, 6),
|
||||
);
|
||||
|
||||
/* Assert Row 2 */
|
||||
$this->assertEquals(
|
||||
6,
|
||||
$this->getCellByTableClass('table_results', 2, 5),
|
||||
);
|
||||
$this->assertEquals(
|
||||
4,
|
||||
$this->getCellByTableClass('table_results', 2, 6),
|
||||
);
|
||||
|
||||
/* Assert Row 3 */
|
||||
$this->assertEquals(
|
||||
5,
|
||||
$this->getCellByTableClass('table_results', 3, 5),
|
||||
);
|
||||
$this->assertEquals(
|
||||
4,
|
||||
$this->getCellByTableClass('table_results', 3, 6),
|
||||
);
|
||||
|
||||
/* Assert Row 4 */
|
||||
$this->assertEquals(
|
||||
7,
|
||||
$this->getCellByTableClass('table_results', 4, 5),
|
||||
);
|
||||
$this->assertEquals(
|
||||
5,
|
||||
$this->getCellByTableClass('table_results', 4, 6),
|
||||
);
|
||||
|
||||
/* Assert Row 5 */
|
||||
$this->assertEquals(
|
||||
3,
|
||||
$this->getCellByTableClass('table_results', 5, 5),
|
||||
);
|
||||
$this->assertEquals(
|
||||
5,
|
||||
$this->getCellByTableClass('table_results', 5, 6),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,6 @@ module.exports = [
|
||||
'database/events': rootPath + '/js/src/database/events.ts',
|
||||
'database/multi_table_query': rootPath + '/js/src/database/multi_table_query.ts',
|
||||
'database/operations': rootPath + '/js/src/database/operations.ts',
|
||||
'database/qbe': rootPath + '/js/src/database/qbe.ts',
|
||||
'database/query_generator': rootPath + '/js/src/database/query_generator.ts',
|
||||
'database/routines': rootPath + '/js/src/database/routines.ts',
|
||||
'database/search': rootPath + '/js/src/database/search.ts',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user