From 10b937511db681ea01e3173d5fdfd588ce742468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Mon, 23 May 2022 19:49:54 -0300 Subject: [PATCH] Replace `$analyzedSqlResults` array with `StatementInfo` object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MaurĂ­cio Meneghini Fauth --- .../Database/QueryByExampleController.php | 2 +- .../Controllers/Import/ImportController.php | 30 +- .../classes/Controllers/Sql/SqlController.php | 13 +- .../Controllers/Table/SearchController.php | 2 +- .../Table/Structure/BrowseController.php | 6 +- .../classes/Database/MultiTableQuery.php | 2 +- libraries/classes/Display/Results.php | 297 ++++++++--------- libraries/classes/ParseAnalyze.php | 30 +- libraries/classes/Sql.php | 308 ++++++++---------- libraries/classes/StatementInfo.php | 232 +++++++++++++ phpstan-baseline.neon | 212 +----------- phpstan.neon.dist | 1 + psalm-baseline.xml | 245 +++----------- psalm.xml | 2 + test/classes/Display/ResultsTest.php | 118 +++---- test/classes/ParseAnalyzeTest.php | 59 ++++ test/classes/SqlTest.php | 57 ++-- test/classes/StatementInfoTest.php | 159 +++++++++ test/stubs/Query.stub | 53 +++ 19 files changed, 938 insertions(+), 890 deletions(-) create mode 100644 libraries/classes/StatementInfo.php create mode 100644 test/classes/ParseAnalyzeTest.php create mode 100644 test/classes/StatementInfoTest.php create mode 100644 test/stubs/Query.stub diff --git a/libraries/classes/Controllers/Database/QueryByExampleController.php b/libraries/classes/Controllers/Database/QueryByExampleController.php index f21400923d..a0833bf4f5 100644 --- a/libraries/classes/Controllers/Database/QueryByExampleController.php +++ b/libraries/classes/Controllers/Database/QueryByExampleController.php @@ -126,7 +126,7 @@ class QueryByExampleController extends AbstractController ); $this->response->addHTML($sql->executeQueryAndSendQueryResponse( - null, // analyzed_sql_results + null, false, // is_gotofile $_POST['db'], // db null, // table diff --git a/libraries/classes/Controllers/Import/ImportController.php b/libraries/classes/Controllers/Import/ImportController.php index 760fc87d92..1fe5588347 100644 --- a/libraries/classes/Controllers/Import/ImportController.php +++ b/libraries/classes/Controllers/Import/ImportController.php @@ -715,14 +715,13 @@ final class ImportController extends AbstractController // can choke on it so avoid parsing) $sqlLength = mb_strlen($GLOBALS['sql_query']); if ($sqlLength <= $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) { - [ - $analyzed_sql_results, - $GLOBALS['db'], - $table_from_sql, - ] = ParseAnalyze::sqlQuery($GLOBALS['sql_query'], $GLOBALS['db']); + [$statementInfo, $GLOBALS['db'], $table_from_sql] = ParseAnalyze::sqlQuery( + $GLOBALS['sql_query'], + $GLOBALS['db'] + ); - $GLOBALS['reload'] = $analyzed_sql_results['reload']; - $GLOBALS['offset'] = $analyzed_sql_results['offset']; + $GLOBALS['reload'] = $statementInfo->reload; + $GLOBALS['offset'] = $statementInfo->offset; if ($GLOBALS['table'] != $table_from_sql && ! empty($table_from_sql)) { $GLOBALS['table'] = $table_from_sql; @@ -747,19 +746,18 @@ final class ImportController extends AbstractController foreach ($queriesToBeExecuted as $GLOBALS['sql_query']) { // parse sql query - [ - $analyzed_sql_results, - $GLOBALS['db'], - $table_from_sql, - ] = ParseAnalyze::sqlQuery($GLOBALS['sql_query'], $GLOBALS['db']); + [$statementInfo, $GLOBALS['db'], $table_from_sql] = ParseAnalyze::sqlQuery( + $GLOBALS['sql_query'], + $GLOBALS['db'] + ); - $GLOBALS['offset'] = $analyzed_sql_results['offset']; - $GLOBALS['reload'] = $analyzed_sql_results['reload']; + $GLOBALS['offset'] = $statementInfo->offset; + $GLOBALS['reload'] = $statementInfo->reload; // Check if User is allowed to issue a 'DROP DATABASE' Statement if ( $this->sql->hasNoRightsToDropDatabase( - $analyzed_sql_results, + $statementInfo, $GLOBALS['cfg']['AllowUserDropDatabase'], $this->dbi->isSuperUser() ) @@ -779,7 +777,7 @@ final class ImportController extends AbstractController } $html_output .= $this->sql->executeQueryAndGetQueryResponse( - $analyzed_sql_results, // analyzed_sql_results + $statementInfo, false, // is_gotofile $GLOBALS['db'], // db $GLOBALS['table'], // table diff --git a/libraries/classes/Controllers/Sql/SqlController.php b/libraries/classes/Controllers/Sql/SqlController.php index f04f98e854..3bd61e06b0 100644 --- a/libraries/classes/Controllers/Sql/SqlController.php +++ b/libraries/classes/Controllers/Sql/SqlController.php @@ -145,11 +145,10 @@ class SqlController extends AbstractController /** * Parse and analyze the query */ - [ - $analyzed_sql_results, - $GLOBALS['db'], - $GLOBALS['table_from_sql'], - ] = ParseAnalyze::sqlQuery($GLOBALS['sql_query'], $GLOBALS['db']); + [$statementInfo, $GLOBALS['db'], $GLOBALS['table_from_sql']] = ParseAnalyze::sqlQuery( + $GLOBALS['sql_query'], + $GLOBALS['db'] + ); if ($GLOBALS['table'] != $GLOBALS['table_from_sql'] && ! empty($GLOBALS['table_from_sql'])) { $GLOBALS['table'] = $GLOBALS['table_from_sql']; @@ -164,7 +163,7 @@ class SqlController extends AbstractController */ if ( $this->sql->hasNoRightsToDropDatabase( - $analyzed_sql_results, + $statementInfo, $GLOBALS['cfg']['AllowUserDropDatabase'], $this->dbi->isSuperUser() ) @@ -206,7 +205,7 @@ class SqlController extends AbstractController } $this->response->addHTML($this->sql->executeQueryAndSendQueryResponse( - $analyzed_sql_results, + $statementInfo, $GLOBALS['is_gotofile'], $GLOBALS['db'], $GLOBALS['table'], diff --git a/libraries/classes/Controllers/Table/SearchController.php b/libraries/classes/Controllers/Table/SearchController.php index db316574a4..079f679015 100644 --- a/libraries/classes/Controllers/Table/SearchController.php +++ b/libraries/classes/Controllers/Table/SearchController.php @@ -259,7 +259,7 @@ class SearchController extends AbstractController ); $this->response->addHTML($sql->executeQueryAndSendQueryResponse( - null, // analyzed_sql_results + null, false, // is_gotofile $GLOBALS['db'], // db $GLOBALS['table'], // table diff --git a/libraries/classes/Controllers/Table/Structure/BrowseController.php b/libraries/classes/Controllers/Table/Structure/BrowseController.php index 4648965350..7666d84b54 100644 --- a/libraries/classes/Controllers/Table/Structure/BrowseController.php +++ b/libraries/classes/Controllers/Table/Structure/BrowseController.php @@ -60,13 +60,13 @@ final class BrowseController extends AbstractController ); // Parse and analyze the query - [$analyzed_sql_results, $GLOBALS['db']] = ParseAnalyze::sqlQuery($sql_query, $GLOBALS['db']); + [$statementInfo, $GLOBALS['db']] = ParseAnalyze::sqlQuery($sql_query, $GLOBALS['db']); $this->response->addHTML( $this->sql->executeQueryAndGetQueryResponse( - $analyzed_sql_results ?? '', + $statementInfo, false, // is_gotofile - (string) $GLOBALS['db'], // db + $GLOBALS['db'], // db $GLOBALS['table'], // table null, // find_real_end null, // sql_query_for_bookmark diff --git a/libraries/classes/Database/MultiTableQuery.php b/libraries/classes/Database/MultiTableQuery.php index 60ccd937ed..5bf67a81de 100644 --- a/libraries/classes/Database/MultiTableQuery.php +++ b/libraries/classes/Database/MultiTableQuery.php @@ -122,7 +122,7 @@ class MultiTableQuery ); return $sql->executeQueryAndSendQueryResponse( - null, // analyzed_sql_results + null, false, // is_gotofile $db, // db null, // table diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index f768df180c..3c446494f1 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -24,6 +24,7 @@ use PhpMyAdmin\Sql; use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\SqlParser\Statements\SelectStatement; use PhpMyAdmin\SqlParser\Utils\Query; +use PhpMyAdmin\StatementInfo; use PhpMyAdmin\Table; use PhpMyAdmin\Template; use PhpMyAdmin\Theme; @@ -643,19 +644,17 @@ class Results * "SELECT * FROM ..." * * @see getTableHeaders(), getColumnParams() - * - * @param array $analyzedSqlResults analyzed sql results */ - private function isSelect(array $analyzedSqlResults): bool + private function isSelect(StatementInfo $statementInfo): bool { return ! ($this->properties['is_count'] || $this->properties['is_export'] || $this->properties['is_func'] || $this->properties['is_analyse']) - && ! empty($analyzedSqlResults['select_from']) - && ! empty($analyzedSqlResults['statement']->from) - && (count($analyzedSqlResults['statement']->from) === 1) - && ! empty($analyzedSqlResults['statement']->from[0]->table); + && ! empty($statementInfo->selectFrom) + && ! empty($statementInfo->statement->from) + && (count($statementInfo->statement->from) === 1) + && ! empty($statementInfo->statement->from[0]->table); } /** @@ -778,7 +777,6 @@ class Results * * @see getTableHeaders() * - * @param array $analyzedSqlResults analyzed sql results * @param array $sortExpression sort expression * @param array $sortExpressionNoDirection sort expression * without direction @@ -791,7 +789,7 @@ class Results */ private function getTableHeadersForColumns( bool $hasSortLink, - array $analyzedSqlResults, + StatementInfo $statementInfo, array $sortExpression, array $sortExpressionNoDirection, array $sortDirection, @@ -812,9 +810,9 @@ class Results // Prepare Display column comments if enabled // ($GLOBALS['cfg']['ShowBrowseComments']). - $commentsMap = $this->getTableCommentsArray($analyzedSqlResults); + $commentsMap = $this->getTableCommentsArray($statementInfo); - [$colOrder, $colVisib] = $this->getColumnParams($analyzedSqlResults); + [$colOrder, $colVisib] = $this->getColumnParams($statementInfo); // optimize: avoid calling a method on each iteration $numberOfColumns = $this->properties['fields_cnt']; @@ -893,7 +891,6 @@ class Results * * @see getTable() * - * @param array $analyzedSqlResults analyzed sql results * @param string $unsortedSqlQuery the unsorted sql query * @param array $sortExpression sort expression * @param array $sortExpressionNoDirection sort expression without direction @@ -911,7 +908,7 @@ class Results */ private function getTableHeaders( DisplayParts $displayParts, - array $analyzedSqlResults, + StatementInfo $statementInfo, $unsortedSqlQuery, array $sortExpression = [], array $sortExpressionNoDirection = [], @@ -924,7 +921,7 @@ class Results $displayParams = $this->properties['display_params']; // Output data needed for column reordering and show/hide column - $columnOrder = $this->getDataForResettingColumnOrder($analyzedSqlResults); + $columnOrder = $this->getDataForResettingColumnOrder($statementInfo); $displayParams['emptypre'] = 0; $displayParams['emptyafter'] = 0; @@ -954,12 +951,12 @@ class Results // See if we have to highlight any header fields of a WHERE query. // Uses SQL-Parser results. - $this->setHighlightedColumnGlobalField($analyzedSqlResults); + $this->setHighlightedColumnGlobalField($statementInfo); // Get the headers for all of the columns $tableHeadersForColumns = $this->getTableHeadersForColumns( $displayParts->hasSortLink, - $analyzedSqlResults, + $statementInfo, $sortExpression, $sortExpressionNoDirection, $sortDirection, @@ -1138,18 +1135,16 @@ class Results * * @see getTableHeaders() * - * @param array $analyzedSqlResults analyzed sql results - * * @return array table comments */ - private function getTableCommentsArray(array $analyzedSqlResults) + private function getTableCommentsArray(StatementInfo $statementInfo) { - if (! $GLOBALS['cfg']['ShowBrowseComments'] || empty($analyzedSqlResults['statement']->from)) { + if (! $GLOBALS['cfg']['ShowBrowseComments'] || empty($statementInfo->statement->from)) { return []; } $ret = []; - foreach ($analyzedSqlResults['statement']->from as $field) { + foreach ($statementInfo->statement->from as $field) { if (empty($field->table)) { continue; } @@ -1167,15 +1162,13 @@ class Results * Set global array for store highlighted header fields * * @see getTableHeaders() - * - * @param array $analyzedSqlResults analyzed sql results */ - private function setHighlightedColumnGlobalField(array $analyzedSqlResults): void + private function setHighlightedColumnGlobalField(StatementInfo $statementInfo): void { $highlightColumns = []; - if (! empty($analyzedSqlResults['statement']->where)) { - foreach ($analyzedSqlResults['statement']->where as $expr) { + if (! empty($statementInfo->statement->where)) { + foreach ($statementInfo->statement->where as $expr) { foreach ($expr->identifiers as $identifier) { $highlightColumns[$identifier] = 'true'; } @@ -1190,17 +1183,15 @@ class Results * * @see getTableHeaders() * - * @param array $analyzedSqlResults analyzed sql results - * * @return array */ - private function getDataForResettingColumnOrder(array $analyzedSqlResults): array + private function getDataForResettingColumnOrder(StatementInfo $statementInfo): array { - if (! $this->isSelect($analyzedSqlResults)) { + if (! $this->isSelect($statementInfo)) { return []; } - [$columnOrder, $columnVisibility] = $this->getColumnParams($analyzedSqlResults); + [$columnOrder, $columnVisibility] = $this->getColumnParams($statementInfo); $tableCreateTime = ''; $table = new Table($this->properties['table'], $this->properties['db']); @@ -1936,11 +1927,10 @@ class Results * * @see getTable() * - * @param ResultInterface $dtResult the link id associated to the query - * which results have to be displayed - * @param array $map the list of relations - * @param array $analyzedSqlResults analyzed sql results - * @param bool $isLimitedDisplay with limited operations or not + * @param ResultInterface $dtResult the link id associated to the query + * which results have to be displayed + * @param array $map the list of relations + * @param bool $isLimitedDisplay with limited operations or not * * @return string html content * @@ -1950,7 +1940,7 @@ class Results ResultInterface $dtResult, DisplayParts $displayParts, array $map, - array $analyzedSqlResults, + StatementInfo $statementInfo, $isLimitedDisplay = false ) { // Mostly because of browser transformations, to make the row-data accessible in a plugin. @@ -1961,7 +1951,7 @@ class Results // query without conditions to shorten URLs when needed, 200 is just // guess, it should depend on remaining URL length - $urlSqlQuery = $this->getUrlSqlQuery($analyzedSqlResults); + $urlSqlQuery = $this->getUrlSqlQuery($statementInfo); $displayParams = $this->properties['display_params']; @@ -1982,7 +1972,7 @@ class Results } // prepare to get the column order, if available - [$colOrder, $colVisib] = $this->getColumnParams($analyzedSqlResults); + [$colOrder, $colVisib] = $this->getColumnParams($statementInfo); // Correction University of Virginia 19991216 in the while below // Previous code assumed that all tables have keys, specifically that @@ -2044,10 +2034,10 @@ class Results $expressions = []; if ( - isset($analyzedSqlResults['statement']) - && $analyzedSqlResults['statement'] instanceof SelectStatement + isset($statementInfo->statement) + && $statementInfo->statement instanceof SelectStatement ) { - $expressions = $analyzedSqlResults['statement']->expr; + $expressions = $statementInfo->statement->expr; } // Results from a "SELECT" statement -> builds the @@ -2154,7 +2144,7 @@ class Results $gridEditConfig, $colVisib, $urlSqlQuery, - $analyzedSqlResults + $statementInfo ); // 3. Displays the modify/delete links on the right if required @@ -2265,17 +2255,16 @@ class Results * * @see getTableBody() * - * @param array $row current row data - * @param int $rowNumber the index of current row - * @param array|false $colOrder the column order false when - * a property not found false - * when a property not found - * @param array $map the list of relations - * @param bool|array|string $colVisib column is visible(false); - * column isn't visible(string - * array) - * @param string $urlSqlQuery the analyzed sql query - * @param array $analyzedSqlResults analyzed sql results + * @param array $row current row data + * @param int $rowNumber the index of current row + * @param array|false $colOrder the column order false when + * a property not found false + * when a property not found + * @param array $map the list of relations + * @param bool|array|string $colVisib column is visible(false); + * column isn't visible(string + * array) + * @param string $urlSqlQuery the analyzed sql query * @psalm-param 'double-click'|'click'|'disabled' $gridEditConfig * * @return string html content @@ -2288,7 +2277,7 @@ class Results string $gridEditConfig, $colVisib, $urlSqlQuery, - array $analyzedSqlResults + StatementInfo $statementInfo ) { $rowValuesHtml = ''; @@ -2430,10 +2419,10 @@ class Results $expressions = []; if ( - isset($analyzedSqlResults['statement']) - && $analyzedSqlResults['statement'] instanceof SelectStatement + isset($statementInfo->statement) + && $statementInfo->statement instanceof SelectStatement ) { - $expressions = $analyzedSqlResults['statement']->expr; + $expressions = $statementInfo->statement->expr; } /** @@ -2490,7 +2479,7 @@ class Results $conditionField, $meta, $map, - $analyzedSqlResults, + $statementInfo, $transformationPlugin, $transformOptions ); @@ -2510,7 +2499,7 @@ class Results $conditionField, $transformationPlugin, $transformOptions, - $analyzedSqlResults + $statementInfo ); } else { // n o t n u m e r i c @@ -2524,7 +2513,7 @@ class Results $conditionField, $transformationPlugin, $transformOptions, - $analyzedSqlResults + $statementInfo ); } @@ -2620,23 +2609,26 @@ class Results * * @see getTableBody() * - * @param array $analyzedSqlResults analyzed sql results - * * @return string analyzed sql query */ - private function getUrlSqlQuery(array $analyzedSqlResults) + private function getUrlSqlQuery(StatementInfo $statementInfo) { - if (($analyzedSqlResults['querytype'] !== 'SELECT') || (mb_strlen($this->properties['sql_query']) < 200)) { + if ( + $statementInfo->queryType !== 'SELECT' + || mb_strlen($this->properties['sql_query']) < 200 + || $statementInfo->statement === null + || $statementInfo->parser === null + ) { return $this->properties['sql_query']; } $query = 'SELECT ' . Query::getClause( - $analyzedSqlResults['statement'], - $analyzedSqlResults['parser']->list, + $statementInfo->statement, + $statementInfo->parser->list, 'SELECT' ); - $fromClause = Query::getClause($analyzedSqlResults['statement'], $analyzedSqlResults['parser']->list, 'FROM'); + $fromClause = Query::getClause($statementInfo->statement, $statementInfo->parser->list, 'FROM'); if ($fromClause !== '') { $query .= ' FROM ' . $fromClause; @@ -2650,13 +2642,11 @@ class Results * * @see getTableBody() * - * @param array $analyzedSqlResults analyzed sql results - * * @return mixed[] 2 element array - $col_order, $col_visib */ - private function getColumnParams(array $analyzedSqlResults): array + private function getColumnParams(StatementInfo $statementInfo): array { - if ($this->isSelect($analyzedSqlResults)) { + if ($this->isSelect($statementInfo)) { $pmatable = new Table($this->properties['table'], $this->properties['db']); $colOrder = $pmatable->getUiProp(Table::PROP_COLUMN_ORDER); $fieldsCount = $this->properties['fields_cnt']; @@ -2930,13 +2920,12 @@ class Results * * @see getTableBody() * - * @param string|null $column the column's value - * @param string $class the html class for column - * @param bool $conditionField the column should highlighted or not - * @param FieldMetadata $meta the meta-information about this field - * @param array $map the list of relations - * @param array $analyzedSqlResults the analyzed query - * @param array $transformOptions the transformation parameters + * @param string|null $column the column's value + * @param string $class the html class for column + * @param bool $conditionField the column should highlighted or not + * @param FieldMetadata $meta the meta-information about this field + * @param array $map the list of relations + * @param array $transformOptions the transformation parameters * * @return string the prepared cell, html content */ @@ -2946,7 +2935,7 @@ class Results bool $conditionField, FieldMetadata $meta, array $map, - array $analyzedSqlResults, + StatementInfo $statementInfo, ?TransformationsPlugin $transformationPlugin, array $transformOptions ) { @@ -2963,7 +2952,7 @@ class Results return $this->getRowData( $class, $conditionField, - $analyzedSqlResults, + $statementInfo, $meta, $map, $column, @@ -2980,14 +2969,13 @@ class Results * * @see getTableBody() * - * @param string|null $column the relevant column in data row - * @param string $class the html class for column - * @param FieldMetadata $meta the meta-information about this field - * @param array $map the list of relations - * @param array $urlParams the parameters for generate url - * @param bool $conditionField the column should highlighted or not - * @param array $transformOptions the transformation parameters - * @param array $analyzedSqlResults the analyzed query + * @param string|null $column the relevant column in data row + * @param string $class the html class for column + * @param FieldMetadata $meta the meta-information about this field + * @param array $map the list of relations + * @param array $urlParams the parameters for generate url + * @param bool $conditionField the column should highlighted or not + * @param array $transformOptions the transformation parameters * * @return string the prepared data cell, html content */ @@ -3000,7 +2988,7 @@ class Results bool $conditionField, ?TransformationsPlugin $transformationPlugin, $transformOptions, - array $analyzedSqlResults + StatementInfo $statementInfo ) { if ($column === null) { return $this->buildNullDisplay($class, $conditionField, $meta); @@ -3039,7 +3027,7 @@ class Results return $this->getRowData( $class, $conditionField, - $analyzedSqlResults, + $statementInfo, $meta, $map, $wktval, @@ -3067,7 +3055,7 @@ class Results return $this->getRowData( $class, $conditionField, - $analyzedSqlResults, + $statementInfo, $meta, $map, $wkbval, @@ -3097,14 +3085,13 @@ class Results * * @see getTableBody() * - * @param string|null $column the relevant column in data row - * @param string $class the html class for column - * @param FieldMetadata $meta the meta-information about the field - * @param array $map the list of relations - * @param array $urlParams the parameters for generate url - * @param bool $conditionField the column should highlighted or not - * @param array $transformOptions the transformation parameters - * @param array $analyzedSqlResults the analyzed query + * @param string|null $column the relevant column in data row + * @param string $class the html class for column + * @param FieldMetadata $meta the meta-information about the field + * @param array $map the list of relations + * @param array $urlParams the parameters for generate url + * @param bool $conditionField the column should highlighted or not + * @param array $transformOptions the transformation parameters * * @return string the prepared data cell, html content */ @@ -3117,7 +3104,7 @@ class Results bool $conditionField, ?TransformationsPlugin $transformationPlugin, $transformOptions, - array $analyzedSqlResults + StatementInfo $statementInfo ) { $originalLength = 0; @@ -3224,7 +3211,7 @@ class Results return $this->getRowData( $class, $conditionField, - $analyzedSqlResults, + $statementInfo, $meta, $map, $column, @@ -3376,23 +3363,22 @@ class Results /** * Prepare a table of results returned by a SQL query. * - * @param ResultInterface $dtResult the link id associated to the query - * which results have to be displayed - * @param array $analyzedSqlResults analyzed sql results - * @param bool $isLimitedDisplay With limited operations or not + * @param ResultInterface $dtResult the link id associated to the query + * which results have to be displayed + * @param bool $isLimitedDisplay With limited operations or not * * @return string Generated HTML content for resulted table */ public function getTable( ResultInterface $dtResult, DisplayParts $displayParts, - array $analyzedSqlResults, + StatementInfo $statementInfo, $isLimitedDisplay = false ) { // The statement this table is built for. - if (isset($analyzedSqlResults['statement'])) { + if (isset($statementInfo->statement)) { /** @var SelectStatement $statement */ - $statement = $analyzedSqlResults['statement']; + $statement = $statementInfo->statement; } else { $statement = null; } @@ -3410,7 +3396,7 @@ class Results $isInnodb = (isset($showTable['Type']) && $showTable['Type'] === self::TABLE_TYPE_INNO_DB); - if ($isInnodb && Sql::isJustBrowsing($analyzedSqlResults, true)) { + if ($isInnodb && Sql::isJustBrowsing($statementInfo, true)) { $preCount = '~'; $afterCount = Generator::showHint( Sanitize::sanitizeMessage( @@ -3471,7 +3457,7 @@ class Results if ($displayParts->hasNavigationBar) { $message = $this->setMessageInformation( $sortedColumnMessage, - $analyzedSqlResults, + $statementInfo, $total, $posNext, $preCount, @@ -3488,7 +3474,7 @@ class Results } // 2.3 Prepare the navigation bars - if ($this->properties['table'] === '' && $analyzedSqlResults['querytype'] === 'SELECT') { + if ($this->properties['table'] === '' && $statementInfo->queryType === 'SELECT') { // table does not always contain a real table name, // for example in MySQL 5.0.x, the query SHOW STATUS // returns STATUS as a table name @@ -3498,16 +3484,20 @@ class Results $unsortedSqlQuery = ''; $sortByKeyData = []; // can the result be sorted? - if ($displayParts->hasSortLink && isset($analyzedSqlResults['statement'])) { + if ( + $displayParts->hasSortLink + && $statementInfo->statement !== null + && $statementInfo->parser !== null + ) { $unsortedSqlQuery = Query::replaceClause( - $analyzedSqlResults['statement'], - $analyzedSqlResults['parser']->list, + $statementInfo->statement, + $statementInfo->parser->list, 'ORDER BY', '' ); // Data is sorted by indexes only if there is only one table. - if ($this->isSelect($analyzedSqlResults)) { + if ($this->isSelect($statementInfo)) { $sortByKeyData = $this->getSortByKeyDropDown( $sortExpression, $unsortedSqlQuery @@ -3547,7 +3537,7 @@ class Results // 3. ----- Prepare the results table ----- $headers = $this->getTableHeaders( $displayParts, - $analyzedSqlResults, + $statementInfo, $unsortedSqlQuery, $sortExpression, $sortExpressionNoDirection, @@ -3555,17 +3545,17 @@ class Results $isLimitedDisplay ); - $body = $this->getTableBody($dtResult, $displayParts, $map, $analyzedSqlResults, $isLimitedDisplay); + $body = $this->getTableBody($dtResult, $displayParts, $map, $statementInfo, $isLimitedDisplay); $this->properties['display_params'] = null; // 4. ----- Prepares the link for multi-fields edit and delete - $bulkLinks = $this->getBulkLinks($dtResult, $analyzedSqlResults, $displayParts->deleteLink); + $bulkLinks = $this->getBulkLinks($dtResult, $statementInfo, $displayParts->deleteLink); // 5. ----- Prepare "Query results operations" $operations = []; if (($printView === null || $printView != '1') && ! $isLimitedDisplay) { - $operations = $this->getResultsOperations($displayParts->hasPrintLink, $analyzedSqlResults); + $operations = $this->getResultsOperations($displayParts->hasPrintLink, $statementInfo); } $relationParameters = $this->relation->getRelationParameters(); @@ -3727,7 +3717,6 @@ class Results * @see getTable() * * @param string $sortedColumnMessage the message for sorted column - * @param array $analyzedSqlResults the analyzed query * @param int $total the total number of rows returned by * the SQL query without any * programmatically appended LIMIT clause @@ -3739,7 +3728,7 @@ class Results */ private function setMessageInformation( string $sortedColumnMessage, - array $analyzedSqlResults, + StatementInfo $statementInfo, $total, $posNext, string $preCount, @@ -3747,9 +3736,9 @@ class Results ) { $unlimNumRows = $this->properties['unlim_num_rows']; // To use in isset() - if (! empty($analyzedSqlResults['statement']->limit)) { - $firstShownRec = $analyzedSqlResults['statement']->limit->offset; - $rowCount = $analyzedSqlResults['statement']->limit->rowCount; + if (! empty($statementInfo->statement->limit)) { + $firstShownRec = $statementInfo->statement->limit->offset; + $rowCount = $statementInfo->statement->limit->rowCount; if ($rowCount < $total) { $lastShownRec = $firstShownRec + $rowCount - 1; @@ -3883,16 +3872,15 @@ class Results * * @see getTable() * - * @param ResultInterface $dtResult the link id associated to the query which - * results have to be displayed - * @param array $analyzedSqlResults analyzed sql results + * @param ResultInterface $dtResult the link id associated to the query which + * results have to be displayed * @psalm-param DisplayParts::NO_DELETE|DisplayParts::DELETE_ROW|DisplayParts::KILL_PROCESS $deleteLink * * @psalm-return array{has_export_button:bool, clause_is_unique:mixed}|array */ private function getBulkLinks( ResultInterface $dtResult, - array $analyzedSqlResults, + StatementInfo $statementInfo, int $deleteLink ): array { if ($deleteLink !== DisplayParts::DELETE_ROW) { @@ -3905,8 +3893,8 @@ class Results $expressions = []; - if (isset($analyzedSqlResults['statement']) && $analyzedSqlResults['statement'] instanceof SelectStatement) { - $expressions = $analyzedSqlResults['statement']->expr; + if (isset($statementInfo->statement) && $statementInfo->statement instanceof SelectStatement) { + $expressions = $statementInfo->statement->expr; } /** @@ -3926,7 +3914,7 @@ class Results $dtResult->seek(0); return [ - 'has_export_button' => $analyzedSqlResults['querytype'] === 'SELECT', + 'has_export_button' => $statementInfo->queryType === 'SELECT', 'clause_is_unique' => $clauseIsUnique, ]; } @@ -3936,8 +3924,6 @@ class Results * * @see getTable() * - * @param array $analyzedSqlResults analyzed sql results - * * @psalm-return array{ * has_export_link: bool, * has_geometry: bool, @@ -3956,7 +3942,7 @@ class Results */ private function getResultsOperations( bool $hasPrintLink, - array $analyzedSqlResults + StatementInfo $statementInfo ): array { $urlParams = [ 'db' => $this->properties['db'], @@ -3974,16 +3960,16 @@ class Results // (most probably PROCEDURE ANALYSE()) it makes no sense to // display the Export link). if ( - ($analyzedSqlResults['querytype'] === self::QUERY_TYPE_SELECT) - && empty($analyzedSqlResults['procedure']) + ($statementInfo->queryType === self::QUERY_TYPE_SELECT) + && empty($statementInfo->isProcedure) ) { - if (count($analyzedSqlResults['select_tables']) === 1) { + if (count($statementInfo->selectTables) === 1) { $urlParams['single_table'] = 'true'; } // In case this query doesn't involve any tables, // implies only raw query is to be exported - if (! $analyzedSqlResults['select_tables']) { + if (! $statementInfo->selectTables) { $urlParams['raw_query'] = 'true'; } @@ -4011,10 +3997,10 @@ class Results } return [ - 'has_procedure' => ! empty($analyzedSqlResults['procedure']), + 'has_procedure' => ! empty($statementInfo->isProcedure), 'has_geometry' => $geometryFound, 'has_print_link' => $hasPrintLink, - 'has_export_link' => $analyzedSqlResults['querytype'] === self::QUERY_TYPE_SELECT, + 'has_export_link' => $statementInfo->queryType === self::QUERY_TYPE_SELECT, 'url_params' => $urlParams, ]; } @@ -4153,25 +4139,24 @@ class Results * @see getDataCellForNumericColumns(), getDataCellForGeometryColumns(), * getDataCellForNonNumericColumns(), * - * @param string $class css classes for the td element - * @param bool $conditionField whether the column is a part of the where clause - * @param array $analyzedSqlResults the analyzed query - * @param FieldMetadata $meta the meta-information about the field - * @param array $map the list of relations - * @param string $data data - * @param string $displayedData data that will be displayed (maybe be chunked) - * @param string $nowrap 'nowrap' if the content should not be wrapped - * @param string $whereComparison data for the where clause - * @param array $transformOptions options for transformation - * @param bool $isFieldTruncated whether the field is truncated - * @param string $originalLength of a truncated column, or '' + * @param string $class css classes for the td element + * @param bool $conditionField whether the column is a part of the where clause + * @param FieldMetadata $meta the meta-information about the field + * @param array $map the list of relations + * @param string $data data + * @param string $displayedData data that will be displayed (maybe be chunked) + * @param string $nowrap 'nowrap' if the content should not be wrapped + * @param string $whereComparison data for the where clause + * @param array $transformOptions options for transformation + * @param bool $isFieldTruncated whether the field is truncated + * @param string $originalLength of a truncated column, or '' * * @return string formatted data */ private function getRowData( string $class, bool $conditionField, - array $analyzedSqlResults, + StatementInfo $statementInfo, FieldMetadata $meta, array $map, $data, @@ -4195,8 +4180,8 @@ class Results $transformationPlugin !== null ); - if (! empty($analyzedSqlResults['statement']->expr)) { - foreach ($analyzedSqlResults['statement']->expr as $expr) { + if (! empty($statementInfo->statement->expr)) { + foreach ($statementInfo->statement->expr as $expr) { if (empty($expr->alias) || empty($expr->column)) { continue; } diff --git a/libraries/classes/ParseAnalyze.php b/libraries/classes/ParseAnalyze.php index 1a01f885ac..083ceae893 100644 --- a/libraries/classes/ParseAnalyze.php +++ b/libraries/classes/ParseAnalyze.php @@ -23,26 +23,26 @@ class ParseAnalyze * @param string $sqlQuery the query to parse * @param string $db the current database * - * @return array + * @return array + * @psalm-return array{StatementInfo, string, string} */ - public static function sqlQuery($sqlQuery, $db) + public static function sqlQuery(string $sqlQuery, string $db): array { // @todo: move to returned results (also in all the calling chain) $GLOBALS['unparsed_sql'] = $sqlQuery; - // Get details about the SQL query. - $analyzedSqlResults = Query::getAll($sqlQuery); + $info = Query::getAll($sqlQuery); $table = ''; // If the targeted table (and database) are different than the ones that is // currently browsed, edit `$db` and `$table` to match them so other elements // (page headers, links, navigation panel) can be updated properly. - if (! empty($analyzedSqlResults['select_tables'])) { + if (! empty($info['select_tables'])) { // Previous table and database name is stored to check if it changed. $previousDb = $db; - if (count($analyzedSqlResults['select_tables']) > 1) { + if (count($info['select_tables']) > 1) { /** * @todo if there are more than one table name in the Select: @@ -52,25 +52,21 @@ class ParseAnalyze */ $table = ''; } else { - $table = $analyzedSqlResults['select_tables'][0][0]; - if (! empty($analyzedSqlResults['select_tables'][0][1])) { - $db = $analyzedSqlResults['select_tables'][0][1]; + $table = $info['select_tables'][0][0] ?? ''; + if (isset($info['select_tables'][0][1])) { + $db = $info['select_tables'][0][1]; } } - // There is no point checking if a reload is required if we already decided + // There is no point checking if a reloading is required if we already decided // to reload. Also, no reload is required for AJAX requests. $response = ResponseRenderer::getInstance(); - if (empty($analyzedSqlResults['reload']) && ! $response->isAjax()) { + if (empty($info['reload']) && ! $response->isAjax()) { // NOTE: Database names are case-insensitive. - $analyzedSqlResults['reload'] = strcasecmp($db, $previousDb) != 0; + $info['reload'] = strcasecmp($db, $previousDb) !== 0; } } - return [ - $analyzedSqlResults, - $db, - $table, - ]; + return [StatementInfo::fromArray($info), $db, $table]; } } diff --git a/libraries/classes/Sql.php b/libraries/classes/Sql.php index e1d5e1dc6f..6e1cd870df 100644 --- a/libraries/classes/Sql.php +++ b/libraries/classes/Sql.php @@ -82,24 +82,27 @@ class Sql /** * Handle remembered sorting order, only for single table query * - * @param string $db database name - * @param string $table table name - * @param array $analyzedSqlResults the analyzed query results - * @param string $fullSqlQuery SQL query + * @param string $db database name + * @param string $table table name + * @param string $fullSqlQuery SQL query */ private function handleSortOrder( $db, $table, - array &$analyzedSqlResults, + StatementInfo $statementInfo, &$fullSqlQuery - ): void { + ): StatementInfo { + if ($statementInfo->statement === null || $statementInfo->parser === null) { + return $statementInfo; + } + $tableObject = new Table($table, $db); - if (empty($analyzedSqlResults['order'])) { + if (empty($statementInfo->order)) { // Retrieving the name of the column we should sort after. $sortCol = $tableObject->getUiProp(Table::PROP_SORTED_COLUMN); if (empty($sortCol)) { - return; + return $statementInfo; } // Remove the name of the table from the retrieved field name. @@ -111,38 +114,42 @@ class Sql // Create the new query. $fullSqlQuery = Query::replaceClause( - $analyzedSqlResults['statement'], - $analyzedSqlResults['parser']->list, + $statementInfo->statement, + $statementInfo->parser->list, 'ORDER BY ' . $sortCol ); // TODO: Avoid reparsing the query. - $analyzedSqlResults = Query::getAll($fullSqlQuery); + $statementInfo = StatementInfo::fromArray(Query::getAll($fullSqlQuery)); } else { // Store the remembered table into session. $tableObject->setUiProp( Table::PROP_SORTED_COLUMN, Query::getClause( - $analyzedSqlResults['statement'], - $analyzedSqlResults['parser']->list, + $statementInfo->statement, + $statementInfo->parser->list, 'ORDER BY' ) ); } + + return $statementInfo; } /** * Append limit clause to SQL query * - * @param array $analyzedSqlResults the analyzed query results - * * @return string limit clause appended SQL query */ - private function getSqlWithLimitClause(array $analyzedSqlResults) + private function getSqlWithLimitClause(StatementInfo $statementInfo) { + if ($statementInfo->statement === null || $statementInfo->parser === null) { + return ''; + } + return Query::replaceClause( - $analyzedSqlResults['statement'], - $analyzedSqlResults['parser']->list, + $statementInfo->statement, + $statementInfo->parser->list, 'LIMIT ' . $_SESSION['tmpval']['pos'] . ', ' . $_SESSION['tmpval']['max_rows'] ); @@ -341,103 +348,86 @@ class Sql } /** - * Function to check whether to remember the sorting order or not - * - * @param array $analyzedSqlResults the analyzed query and other variables set - * after analyzing the query + * Function to check whether to remember the sorting order or not. */ - private function isRememberSortingOrder(array $analyzedSqlResults): bool + private function isRememberSortingOrder(StatementInfo $statementInfo): bool { - return isset($analyzedSqlResults['select_expr'], $analyzedSqlResults['select_tables']) - && $GLOBALS['cfg']['RememberSorting'] - && ! ($analyzedSqlResults['is_count'] - || $analyzedSqlResults['is_export'] - || $analyzedSqlResults['is_func'] - || $analyzedSqlResults['is_analyse']) - && $analyzedSqlResults['select_from'] - && (empty($analyzedSqlResults['select_expr']) - || ((count($analyzedSqlResults['select_expr']) === 1) - && ($analyzedSqlResults['select_expr'][0] === '*'))) - && count($analyzedSqlResults['select_tables']) === 1; + return $GLOBALS['cfg']['RememberSorting'] + && ! ($statementInfo->isCount + || $statementInfo->isExport + || $statementInfo->isFunction + || $statementInfo->isAnalyse) + && $statementInfo->selectFrom + && (empty($statementInfo->selectExpression) + || ((count($statementInfo->selectExpression) === 1) + && ($statementInfo->selectExpression[0] === '*'))) + && count($statementInfo->selectTables) === 1; } /** - * Function to check whether the LIMIT clause should be appended or not - * - * @param array $analyzedSqlResults the analyzed query and other variables set - * after analyzing the query + * Function to check whether the LIMIT clause should be appended or not. */ - private function isAppendLimitClause(array $analyzedSqlResults): bool + private function isAppendLimitClause(StatementInfo $statementInfo): bool { // Assigning LIMIT clause to an syntactically-wrong query // is not needed. Also we would want to show the true query // and the true error message to the query executor - return (isset($analyzedSqlResults['parser']) - && count($analyzedSqlResults['parser']->errors) === 0) + return (isset($statementInfo->parser) + && count($statementInfo->parser->errors) === 0) && ($_SESSION['tmpval']['max_rows'] !== 'all') - && ! ($analyzedSqlResults['is_export'] - || $analyzedSqlResults['is_analyse']) - && ($analyzedSqlResults['select_from'] - || $analyzedSqlResults['is_subquery']) - && empty($analyzedSqlResults['limit']); + && ! ($statementInfo->isExport + || $statementInfo->isAnalyse) + && ($statementInfo->selectFrom + || $statementInfo->isSubquery) + && empty($statementInfo->limit); } /** * Function to check whether this query is for just browsing * - * @param array $analyzedSqlResults the analyzed query and other variables set - * after analyzing the query - * @param bool|null $findRealEnd whether the real end should be found + * @param bool|null $findRealEnd whether the real end should be found */ - public static function isJustBrowsing(array $analyzedSqlResults, ?bool $findRealEnd): bool + public static function isJustBrowsing(StatementInfo $statementInfo, ?bool $findRealEnd): bool { - return ! $analyzedSqlResults['is_group'] - && ! $analyzedSqlResults['is_func'] - && empty($analyzedSqlResults['union']) - && empty($analyzedSqlResults['distinct']) - && $analyzedSqlResults['select_from'] - && (count($analyzedSqlResults['select_tables']) === 1) - && (empty($analyzedSqlResults['statement']->where) - || (count($analyzedSqlResults['statement']->where) === 1 - && $analyzedSqlResults['statement']->where[0]->expr === '1')) - && empty($analyzedSqlResults['group']) + return ! $statementInfo->isGroup + && ! $statementInfo->isFunction + && empty($statementInfo->union) + && empty($statementInfo->distinct) + && $statementInfo->selectFrom + && (count($statementInfo->selectTables) === 1) + && (empty($statementInfo->statement->where) + || (count($statementInfo->statement->where) === 1 + && $statementInfo->statement->where[0]->expr === '1')) + && empty($statementInfo->group) && ! isset($findRealEnd) - && ! $analyzedSqlResults['is_subquery'] - && ! $analyzedSqlResults['join'] - && empty($analyzedSqlResults['having']); + && ! $statementInfo->isSubquery + && ! $statementInfo->join + && empty($statementInfo->having); } /** - * Function to check whether the related transformation information should be deleted - * - * @param array $analyzedSqlResults the analyzed query and other variables set - * after analyzing the query + * Function to check whether the related transformation information should be deleted. */ - private function isDeleteTransformationInfo(array $analyzedSqlResults): bool + private function isDeleteTransformationInfo(StatementInfo $statementInfo): bool { - return ! empty($analyzedSqlResults['querytype']) - && (($analyzedSqlResults['querytype'] === 'ALTER') - || ($analyzedSqlResults['querytype'] === 'DROP')); + return ! empty($statementInfo->queryType) + && (($statementInfo->queryType === 'ALTER') + || ($statementInfo->queryType === 'DROP')); } /** * Function to check whether the user has rights to drop the database * - * @param array $analyzedSqlResults the analyzed query and other variables set - * after analyzing the query - * @param bool $allowUserDropDatabase whether the user is allowed to drop db - * @param bool $isSuperUser whether this user is a superuser + * @param bool $allowUserDropDatabase whether the user is allowed to drop db + * @param bool $isSuperUser whether this user is a superuser */ public function hasNoRightsToDropDatabase( - array $analyzedSqlResults, + StatementInfo $statementInfo, $allowUserDropDatabase, $isSuperUser ): bool { - return ! $allowUserDropDatabase - && isset($analyzedSqlResults['drop_database']) - && $analyzedSqlResults['drop_database'] - && ! $isSuperUser; + return ! $allowUserDropDatabase && $statementInfo->dropDatabase && ! $isSuperUser; } /** @@ -675,11 +665,10 @@ class Sql * Function to count the total number of rows for the same 'SELECT' query without * the 'LIMIT' clause that may have been programmatically added * - * @param int|string $numRows number of rows affected/changed by the query - * @param bool $justBrowsing whether just browsing or not - * @param string $db the current database - * @param string $table the current table - * @param array $analyzedSqlResults the analyzed query and other variables set after analyzing the query + * @param int|string $numRows number of rows affected/changed by the query + * @param bool $justBrowsing whether just browsing or not + * @param string $db the current database + * @param string $table the current table * @psalm-param int|numeric-string $numRows * * @return int|string unlimited number of rows @@ -690,14 +679,14 @@ class Sql bool $justBrowsing, string $db, string $table, - array $analyzedSqlResults + StatementInfo $statementInfo ) { /* Shortcut for not analyzed/empty query */ - if ($analyzedSqlResults === []) { + if ($statementInfo->statement === null || $statementInfo->parser === null) { return 0; } - if (! $this->isAppendLimitClause($analyzedSqlResults)) { + if (! $this->isAppendLimitClause($statementInfo)) { // if we did not append a limit, set this to get a correct // "Showing rows..." message // $_SESSION['tmpval']['max_rows'] = 'all'; @@ -707,7 +696,7 @@ class Sql // result are less than max_rows to display, there is no need // to count total rows for that query again $unlimNumRows = $_SESSION['tmpval']['pos'] + $numRows; - } elseif ($analyzedSqlResults['querytype'] === 'SELECT' || $analyzedSqlResults['is_subquery']) { + } elseif ($statementInfo->queryType === 'SELECT' || $statementInfo->isSubquery) { // c o u n t q u e r y // If we are "just browsing", there is only one table (and no join), @@ -736,8 +725,8 @@ class Sql ->countRecords(true); } } else { - $statement = $analyzedSqlResults['statement']; - $tokenList = $analyzedSqlResults['parser']->list; + $statement = $statementInfo->statement; + $tokenList = $statementInfo->parser->list; $replaces = [ // Remove ORDER BY to decrease unnecessary sorting time [ @@ -770,7 +759,6 @@ class Sql /** * Function to handle all aspects relating to executing the query * - * @param array $analyzedSqlResults analyzed sql results * @param string $fullSqlQuery full sql query * @param bool $isGotoFile whether to go to a file * @param string $db current database @@ -788,7 +776,7 @@ class Sql * } */ private function executeTheQuery( - array $analyzedSqlResults, + StatementInfo $statementInfo, $fullSqlQuery, $isGotoFile, string $db, @@ -847,13 +835,13 @@ class Sql // Gets the number of rows affected/returned // (This must be done immediately after the query because // mysql_affected_rows() reports about the last query done) - $numRows = $this->getNumberOfRowsAffectedOrChanged($analyzedSqlResults['is_affected'], $result); + $numRows = $this->getNumberOfRowsAffectedOrChanged($statementInfo->isAffected, $result); $profilingResults = Profiling::getInformation($this->dbi); - $justBrowsing = self::isJustBrowsing($analyzedSqlResults, $findRealEnd ?? null); + $justBrowsing = self::isJustBrowsing($statementInfo, $findRealEnd ?? null); - $unlimNumRows = $this->countQueryResults($numRows, $justBrowsing, $db, $table ?? '', $analyzedSqlResults); + $unlimNumRows = $this->countQueryResults($numRows, $justBrowsing, $db, $table ?? '', $statementInfo); $this->cleanupRelations($db, $table ?? '', $_POST['dropped_column'] ?? null, ! empty($_POST['purge'])); @@ -887,17 +875,16 @@ class Sql /** * Delete related transformation information * - * @param string $db current database - * @param string $table current table - * @param array $analyzedSqlResults analyzed sql results + * @param string $db current database + * @param string $table current table */ - private function deleteTransformationInfo(string $db, string $table, array $analyzedSqlResults): void + private function deleteTransformationInfo(string $db, string $table, StatementInfo $statementInfo): void { - if (! isset($analyzedSqlResults['statement'])) { + if (! isset($statementInfo->statement)) { return; } - $statement = $analyzedSqlResults['statement']; + $statement = $statementInfo->statement; if ($statement instanceof AlterStatement) { if ( ! empty($statement->altered[0]) @@ -914,19 +901,18 @@ class Sql /** * Function to get the message for the no rows returned case * - * @param string|null $messageToShow message to show - * @param array $analyzedSqlResults analyzed sql results - * @param int|string $numRows number of rows + * @param string|null $messageToShow message to show + * @param int|string $numRows number of rows */ private function getMessageForNoRowsReturned( ?string $messageToShow, - array $analyzedSqlResults, + StatementInfo $statementInfo, $numRows ): Message { - if ($analyzedSqlResults['querytype'] === 'DELETE"') { + if ($statementInfo->queryType === 'DELETE"') { $message = Message::getMessageForDeletedRows($numRows); - } elseif ($analyzedSqlResults['is_insert']) { - if ($analyzedSqlResults['querytype'] === 'REPLACE') { + } elseif ($statementInfo->isInsert) { + if ($statementInfo->queryType === 'REPLACE') { // For REPLACE we get DELETED + INSERTED row count, // so we have to call it affected $message = Message::getMessageForAffectedRows($numRows); @@ -946,7 +932,7 @@ class Sql $inserted->addParam($insertId + $numRows - 1); $message->addMessage($inserted); } - } elseif ($analyzedSqlResults['is_affected']) { + } elseif ($statementInfo->isAffected) { $message = Message::getMessageForAffectedRows($numRows); // Ok, here is an explanation for the !$is_select. @@ -956,7 +942,7 @@ class Sql // fact that $message_to_show is sent for every case. // The $message_to_show containing a success message and sent with // the form should not have priority over errors - } elseif ($messageToShow && $analyzedSqlResults['querytype'] !== 'SELECT') { + } elseif ($messageToShow && $statementInfo->queryType !== 'SELECT') { $message = Message::rawSuccess(htmlspecialchars($messageToShow)); } elseif (! empty($GLOBALS['show_as_php'])) { $message = Message::success(__('Showing as PHP code')); @@ -996,7 +982,6 @@ class Sql * 6-> When searching using the SEARCH tab which returns zero results * 7-> When changing the structure of the table except change operation * - * @param array $analyzedSqlResults analyzed sql results * @param string $db current database * @param string|null $table current table * @param string|null $messageToShow message to show @@ -1012,7 +997,7 @@ class Sql * @return string html */ private function getQueryResponseForNoResultsReturned( - array $analyzedSqlResults, + StatementInfo $statementInfo, string $db, ?string $table, ?string $messageToShow, @@ -1024,14 +1009,14 @@ class Sql $sqlQuery, ?string $completeQuery ): string { - if ($this->isDeleteTransformationInfo($analyzedSqlResults)) { - $this->deleteTransformationInfo($db, $table ?? '', $analyzedSqlResults); + if ($this->isDeleteTransformationInfo($statementInfo)) { + $this->deleteTransformationInfo($db, $table ?? '', $statementInfo); } if (isset($extraData['error'])) { $message = Message::rawError($extraData['error']); } else { - $message = $this->getMessageForNoRowsReturned($messageToShow, $analyzedSqlResults, $numRows); + $message = $this->getMessageForNoRowsReturned($messageToShow, $statementInfo, $numRows); } $queryMessage = Generator::getMessage($message, $GLOBALS['sql_query'], 'success'); @@ -1056,7 +1041,7 @@ class Sql $response = ResponseRenderer::getInstance(); $response->addJSON($extraData ?? []); - if (empty($analyzedSqlResults['is_select']) || isset($extraData['error'])) { + if (empty($statementInfo->isSelect) || isset($extraData['error'])) { return $queryMessage; } @@ -1078,7 +1063,7 @@ class Sql $numRows, null, $result, - $analyzedSqlResults, + $statementInfo, true ); @@ -1120,7 +1105,7 @@ class Sql 'db' => $db, 'table' => $table, 'sql_query' => $sqlQuery, - 'is_procedure' => ! empty($analyzedSqlResults['procedure']), + 'is_procedure' => ! empty($statementInfo->isProcedure), ]); } @@ -1170,7 +1155,6 @@ class Sql * @param int|string $numRows number of rows * @param array|null $showTable table definitions * @param ResultInterface|false|null $result result of the executed query - * @param array $analyzedSqlResults analyzed sql results * @param bool $isLimitedDisplay Show only limited operations or not * @psalm-param int|numeric-string $unlimNumRows * @psalm-param int|numeric-string $numRows @@ -1183,14 +1167,14 @@ class Sql $numRows, ?array $showTable, $result, - array $analyzedSqlResults, + StatementInfo $statementInfo, $isLimitedDisplay = false ): string { $printView = isset($_POST['printview']) && $_POST['printview'] == '1' ? '1' : null; $tableHtml = ''; $isBrowseDistinct = ! empty($_POST['is_browse_distinct']); - if ($analyzedSqlResults['is_procedure']) { + if ($statementInfo->isProcedure) { do { if ($result === null) { $result = $this->dbi->storeResult(); @@ -1210,17 +1194,17 @@ class Sql $displayResultsObject->setProperties( $numRows, $fieldsMeta, - $analyzedSqlResults['is_count'], - $analyzedSqlResults['is_export'], - $analyzedSqlResults['is_func'], - $analyzedSqlResults['is_analyse'], + $statementInfo->isCount, + $statementInfo->isExport, + $statementInfo->isFunction, + $statementInfo->isAnalyse, $numRows, $fieldsCount, $GLOBALS['querytime'], $GLOBALS['text_dir'], - $analyzedSqlResults['is_maint'], - $analyzedSqlResults['is_explain'], - $analyzedSqlResults['is_show'], + $statementInfo->isMaint, + $statementInfo->isExplain, + $statementInfo->isShow, $showTable, $printView, $editable, @@ -1240,7 +1224,7 @@ class Sql $tableHtml .= $displayResultsObject->getTable( $result, $displayParts, - $analyzedSqlResults, + $statementInfo, $isLimitedDisplay ); } @@ -1258,17 +1242,17 @@ class Sql $displayResultsObject->setProperties( $unlimNumRows, $fieldsMeta, - $analyzedSqlResults['is_count'], - $analyzedSqlResults['is_export'], - $analyzedSqlResults['is_func'], - $analyzedSqlResults['is_analyse'], + $statementInfo->isCount, + $statementInfo->isExport, + $statementInfo->isFunction, + $statementInfo->isAnalyse, $numRows, $fieldsCount, $GLOBALS['querytime'], $GLOBALS['text_dir'], - $analyzedSqlResults['is_maint'], - $analyzedSqlResults['is_explain'], - $analyzedSqlResults['is_show'], + $statementInfo->isMaint, + $statementInfo->isExplain, + $statementInfo->isShow, $showTable, $printView, $editable, @@ -1279,7 +1263,7 @@ class Sql $tableHtml .= $displayResultsObject->getTable( $result, $displayParts, - $analyzedSqlResults, + $statementInfo, $isLimitedDisplay ); } @@ -1366,7 +1350,6 @@ class Sql * Function to display results when the executed query returns non empty results * * @param ResultInterface|false|null $result executed query results - * @param array $analyzedSqlResults analysed sql results * @param string $db current database * @param string|null $table current table * @param array|null $sqlData sql data @@ -1385,7 +1368,7 @@ class Sql */ private function getQueryResponseForResultsReturned( $result, - array $analyzedSqlResults, + StatementInfo $statementInfo, string $db, ?string $table, ?array $sqlData, @@ -1432,7 +1415,7 @@ class Sql $updatableView = false; - $statement = $analyzedSqlResults['statement'] ?? null; + $statement = $statementInfo->statement; if ($statement instanceof SelectStatement) { if ($statement->expr && $statement->expr[0]->expr === '*' && $table) { $_table = new Table($table, $db); @@ -1440,9 +1423,9 @@ class Sql } if ( - $analyzedSqlResults['join'] - || $analyzedSqlResults['is_subquery'] - || count($analyzedSqlResults['select_tables']) !== 1 + $statementInfo->join + || $statementInfo->isSubquery + || count($statementInfo->selectTables) !== 1 ) { $justOneTable = false; } @@ -1526,7 +1509,7 @@ class Sql $numRows, $GLOBALS['showtable'], $result, - $analyzedSqlResults + $statementInfo ); $bookmarkSupportHtml = ''; @@ -1563,7 +1546,6 @@ class Sql /** * Function to execute the query and send the response * - * @param array|null $analyzedSqlResults analysed sql results * @param bool $isGotoFile whether goto file or not * @param string $db current database * @param string|null $table current table @@ -1579,7 +1561,7 @@ class Sql * @param string|null $completeQuery complete query */ public function executeQueryAndSendQueryResponse( - $analyzedSqlResults, + ?StatementInfo $statementInfo, $isGotoFile, string $db, ?string $table, @@ -1594,19 +1576,15 @@ class Sql $sqlQuery, $completeQuery ): string { - if ($analyzedSqlResults == null) { + if ($statementInfo === null) { // Parse and analyze the query - [ - $analyzedSqlResults, - $db, - $tableFromSql, - ] = ParseAnalyze::sqlQuery($sqlQuery, $db); + [$statementInfo, $db, $tableFromSql] = ParseAnalyze::sqlQuery($sqlQuery, $db); $table = $tableFromSql ?: $table; } return $this->executeQueryAndGetQueryResponse( - $analyzedSqlResults, // analyzed_sql_results + $statementInfo, $isGotoFile, // is_gotofile $db, // db $table, // table @@ -1626,7 +1604,6 @@ class Sql /** * Function to execute the query and send the response * - * @param array $analyzedSqlResults analysed sql results * @param bool $isGotoFile whether goto file or not * @param string $db current database * @param string|null $table current table @@ -1644,7 +1621,7 @@ class Sql * @return string html */ public function executeQueryAndGetQueryResponse( - array $analyzedSqlResults, + StatementInfo $statementInfo, $isGotoFile, string $db, ?string $table, @@ -1668,13 +1645,12 @@ class Sql // Handling is also not required if we came from the "Sort by key" // drop-down. if ( - $analyzedSqlResults !== [] - && $this->isRememberSortingOrder($analyzedSqlResults) - && empty($analyzedSqlResults['union']) + $this->isRememberSortingOrder($statementInfo) + && empty($statementInfo->union) && ! isset($_POST['sort_by_key']) ) { if (! isset($_SESSION['sql_from_query_box'])) { - $this->handleSortOrder($db, $table, $analyzedSqlResults, $sqlQuery); + $statementInfo = $this->handleSortOrder($db, $table, $statementInfo, $sqlQuery); } else { unset($_SESSION['sql_from_query_box']); } @@ -1694,8 +1670,8 @@ class Sql $fullSqlQuery = $sqlQuery; // Do append a "LIMIT" clause? - if ($this->isAppendLimitClause($analyzedSqlResults)) { - $fullSqlQuery = $this->getSqlWithLimitClause($analyzedSqlResults); + if ($this->isAppendLimitClause($statementInfo)) { + $fullSqlQuery = $this->getSqlWithLimitClause($statementInfo); } $GLOBALS['reload'] = $this->hasCurrentDbChanged($db); @@ -1708,7 +1684,7 @@ class Sql $profilingResults, $extraData, ] = $this->executeTheQuery( - $analyzedSqlResults, + $statementInfo, $fullSqlQuery, $isGotoFile, $db, @@ -1725,9 +1701,9 @@ class Sql $warningMessages = $this->operations->getWarningMessagesArray(); // No rows returned -> move back to the calling page - if (($numRows == 0 && $unlimNumRows == 0) || $analyzedSqlResults['is_affected']) { + if (($numRows == 0 && $unlimNumRows == 0) || $statementInfo->isAffected) { $htmlOutput = $this->getQueryResponseForNoResultsReturned( - $analyzedSqlResults, + $statementInfo, $db, $table, $messageToShow, @@ -1743,7 +1719,7 @@ class Sql // At least one row is returned -> displays a table with results $htmlOutput = $this->getQueryResponseForResultsReturned( $result, - $analyzedSqlResults, + $statementInfo, $db, $table, $sqlData, diff --git a/libraries/classes/StatementInfo.php b/libraries/classes/StatementInfo.php new file mode 100644 index 0000000000..e36959d555 --- /dev/null +++ b/libraries/classes/StatementInfo.php @@ -0,0 +1,232 @@ +> + * @psalm-var list + */ + public $selectTables; + /** + * @var array + * @psalm-var list + */ + public $selectExpression; + + /** + * @param string|false $queryType + * @param array> $selectTables + * @param array $selectExpression + * @psalm-param list $selectTables + * @psalm-param list $selectExpression + */ + private function __construct( + bool $distinct, + bool $dropDatabase, + bool $group, + bool $having, + bool $isAffected, + bool $isAnalyse, + bool $isCount, + bool $isDelete, + bool $isExplain, + bool $isExport, + bool $isFunction, + bool $isGroup, + bool $isInsert, + bool $isMaint, + bool $isProcedure, + bool $isReplace, + bool $isSelect, + bool $isShow, + bool $isSubquery, + bool $join, + bool $limit, + bool $offset, + bool $order, + $queryType, + bool $reload, + bool $selectFrom, + bool $union, + ?SqlParser\Parser $parser, + ?SqlParser\Statement $statement, + array $selectTables, + array $selectExpression + ) { + $this->distinct = $distinct; + $this->dropDatabase = $dropDatabase; + $this->group = $group; + $this->having = $having; + $this->isAffected = $isAffected; + $this->isAnalyse = $isAnalyse; + $this->isCount = $isCount; + $this->isDelete = $isDelete; + $this->isExplain = $isExplain; + $this->isExport = $isExport; + $this->isFunction = $isFunction; + $this->isGroup = $isGroup; + $this->isInsert = $isInsert; + $this->isMaint = $isMaint; + $this->isProcedure = $isProcedure; + $this->isReplace = $isReplace; + $this->isSelect = $isSelect; + $this->isShow = $isShow; + $this->isSubquery = $isSubquery; + $this->join = $join; + $this->limit = $limit; + $this->offset = $offset; + $this->order = $order; + $this->queryType = $queryType; + $this->reload = $reload; + $this->selectFrom = $selectFrom; + $this->union = $union; + $this->parser = $parser; + $this->statement = $statement; + $this->selectTables = $selectTables; + $this->selectExpression = $selectExpression; + } + + /** + * @param array|string|null>|bool|string|Parser|Statement> $info + * @psalm-param array{ + * distinct: bool, + * drop_database: bool, + * group: bool, + * having: bool, + * is_affected: bool, + * is_analyse: bool, + * is_count: bool, + * is_delete: bool, + * is_explain: bool, + * is_export: bool, + * is_func: bool, + * is_group: bool, + * is_insert: bool, + * is_maint: bool, + * is_procedure: bool, + * is_replace: bool, + * is_select: bool, + * is_show: bool, + * is_subquery: bool, + * join: bool, + * limit: bool, + * offset: bool, + * order: bool, + * querytype: ( + * 'ALTER'|'ANALYZE'|'CALL'|'CHECK'|'CHECKSUM'|'CREATE'|'DELETE'|'DROP'| + * 'EXPLAIN'|'INSERT'|'LOAD'|'OPTIMIZE'|'REPAIR'|'REPLACE'|'SELECT'|'SET'|'SHOW'|'UPDATE'|false + * ), + * reload: bool, + * select_from: bool, + * union: bool, + * parser?: Parser, + * statement?: Statement, + * select_tables?: list, + * select_expr?: list + * } $info + */ + public static function fromArray(array $info): self + { + return new self( + $info['distinct'], + $info['drop_database'], + $info['group'], + $info['having'], + $info['is_affected'], + $info['is_analyse'], + $info['is_count'], + $info['is_delete'], + $info['is_explain'], + $info['is_export'], + $info['is_func'], + $info['is_group'], + $info['is_insert'], + $info['is_maint'], + $info['is_procedure'], + $info['is_replace'], + $info['is_select'], + $info['is_show'], + $info['is_subquery'], + $info['join'], + $info['limit'], + $info['offset'], + $info['order'], + $info['querytype'], + $info['reload'], + $info['select_from'], + $info['union'], + $info['parser'] ?? null, + $info['statement'] ?? null, + $info['select_tables'] ?? [], + $info['select_expr'] ?? [] + ); + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index aee6f67131..49e7987a9e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2770,26 +2770,11 @@ parameters: count: 2 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getBulkLinks\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getColumnParams\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getCommentForRow\\(\\) has parameter \\$commentsMap with no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForGeometryColumns\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForGeometryColumns\\(\\) has parameter \\$transformOptions with no value type specified in iterable type array\\.$#" count: 1 @@ -2800,11 +2785,6 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForNonNumericColumns\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForNonNumericColumns\\(\\) has parameter \\$transformOptions with no value type specified in iterable type array\\.$#" count: 1 @@ -2815,21 +2795,11 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForNumericColumns\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataCellForNumericColumns\\(\\) has parameter \\$transformOptions with no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataForResettingColumnOrder\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getDataForResettingColumnOrder\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -2850,16 +2820,6 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getResultsOperations\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getRowData\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getRowData\\(\\) has parameter \\$transformOptions with no value type specified in iterable type array\\.$#" count: 1 @@ -2875,11 +2835,6 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getRowValues\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getRowValues\\(\\) has parameter \\$colOrder with no value type specified in iterable type array\\.$#" count: 1 @@ -2920,31 +2875,11 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTable\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableBody\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableCommentsArray\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableCommentsArray\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableHeaders\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableHeaders\\(\\) has parameter \\$sortDirection with no value type specified in iterable type array\\.$#" count: 1 @@ -2960,11 +2895,6 @@ parameters: count: 2 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableHeadersForColumns\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getTableHeadersForColumns\\(\\) has parameter \\$sortDirection with no value type specified in iterable type array\\.$#" count: 1 @@ -2985,11 +2915,6 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:getUrlSqlQuery\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:handleNonPrintableContents\\(\\) has parameter \\$transformOptions with no value type specified in iterable type array\\.$#" count: 1 @@ -3010,21 +2935,6 @@ parameters: count: 1 path: libraries/classes/Display/Results.php - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:isSelect\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:setHighlightedColumnGlobalField\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - - - message: "#^Method PhpMyAdmin\\\\Display\\\\Results\\:\\:setMessageInformation\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Display/Results.php - - message: "#^Offset 3 does not exist on array\\{string\\|null, string\\|null, string\\|null\\}\\.$#" count: 1 @@ -5370,11 +5280,6 @@ parameters: count: 1 path: libraries/classes/OutputBuffering.php - - - message: "#^Method PhpMyAdmin\\\\ParseAnalyze\\:\\:sqlQuery\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/ParseAnalyze.php - - message: "#^Method PhpMyAdmin\\\\Partitioning\\\\Maintenance\\:\\:analyze\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -7510,31 +7415,11 @@ parameters: count: 1 path: libraries/classes/Setup/Index.php - - - message: "#^Cannot access property \\$where on mixed\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:countQueryResults\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:countQueryResults\\(\\) should return int\\|numeric\\-string but returns mixed\\.$#" count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:deleteTransformationInfo\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeQueryAndGetQueryResponse\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeQueryAndGetQueryResponse\\(\\) has parameter \\$extraData with no value type specified in iterable type array\\.$#" count: 1 @@ -7545,11 +7430,6 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeQueryAndSendQueryResponse\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeQueryAndSendQueryResponse\\(\\) has parameter \\$extraData with no value type specified in iterable type array\\.$#" count: 1 @@ -7560,11 +7440,6 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeTheQuery\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:executeTheQuery\\(\\) has parameter \\$extraData with no value type specified in iterable type array\\.$#" count: 1 @@ -7590,26 +7465,11 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getHtmlForSqlQueryResultsTable\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getHtmlForSqlQueryResultsTable\\(\\) has parameter \\$showTable with no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getMessageForNoRowsReturned\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getQueryResponseForNoResultsReturned\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getQueryResponseForNoResultsReturned\\(\\) has parameter \\$extraData with no value type specified in iterable type array\\.$#" count: 1 @@ -7620,11 +7480,6 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getQueryResponseForResultsReturned\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getQueryResponseForResultsReturned\\(\\) has parameter \\$profilingResults with no value type specified in iterable type array\\.$#" count: 1 @@ -7635,41 +7490,11 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getSqlWithLimitClause\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:getValuesForColumn\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/Sql.php - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:handleSortOrder\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:hasNoRightsToDropDatabase\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:isAppendLimitClause\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:isDeleteTransformationInfo\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:isRememberSortingOrder\\(\\) has parameter \\$analyzedSqlResults with no value type specified in iterable type array\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Method PhpMyAdmin\\\\Sql\\:\\:resultSetContainsUniqueKey\\(\\) has parameter \\$fieldsMeta with no value type specified in iterable type array\\.$#" count: 1 @@ -7715,11 +7540,6 @@ parameters: count: 1 path: libraries/classes/Sql.php - - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, mixed given\\.$#" - count: 1 - path: libraries/classes/Sql.php - - message: "#^Parameter \\#2 \\$foreign_field of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:foreignDropdown\\(\\) expects string, mixed given\\.$#" count: 1 @@ -9327,7 +9147,7 @@ parameters: - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:dataProviderForTestGetDataCellForNonNumericColumns\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 4 + count: 3 path: test/classes/Display/ResultsTest.php - @@ -9350,11 +9170,6 @@ parameters: count: 2 path: test/classes/Display/ResultsTest.php - - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:dataProviderForTestSetHighlightedColumnGlobalField\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: test/classes/Display/ResultsTest.php - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:dataProviderGetSortOrderHiddenInputs\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -9370,11 +9185,6 @@ parameters: count: 1 path: test/classes/Display/ResultsTest.php - - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:testGetDataCellForNonNumericColumns\\(\\) has parameter \\$analyzed_sql_results with no value type specified in iterable type array\\.$#" - count: 1 - path: test/classes/Display/ResultsTest.php - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:testGetDataCellForNonNumericColumns\\(\\) has parameter \\$map with no value type specified in iterable type array\\.$#" count: 1 @@ -9450,16 +9260,6 @@ parameters: count: 1 path: test/classes/Display/ResultsTest.php - - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:testSetHighlightedColumnGlobalField\\(\\) has parameter \\$analyzed_sql with no value type specified in iterable type array\\.$#" - count: 1 - path: test/classes/Display/ResultsTest.php - - - - message: "#^Method PhpMyAdmin\\\\Tests\\\\Display\\\\ResultsTest\\:\\:testSetHighlightedColumnGlobalField\\(\\) has parameter \\$output with no value type specified in iterable type array\\.$#" - count: 1 - path: test/classes/Display/ResultsTest.php - - message: "#^Parameter \\#1 \\$string of function htmlspecialchars_decode expects string, mixed given\\.$#" count: 1 @@ -10370,16 +10170,6 @@ parameters: count: 1 path: test/classes/SqlTest.php - - - message: "#^Parameter \\#1 \\$analyzedSqlResults of method PhpMyAdmin\\\\Sql\\:\\:hasNoRightsToDropDatabase\\(\\) expects array, mixed given\\.$#" - count: 3 - path: test/classes/SqlTest.php - - - - message: "#^Parameter \\#1 \\$analyzedSqlResults of static method PhpMyAdmin\\\\Sql\\:\\:isJustBrowsing\\(\\) expects array\\, mixed given\\.$#" - count: 3 - path: test/classes/SqlTest.php - - message: "#^Method PhpMyAdmin\\\\Tests\\\\StorageEngineTest\\:\\:providerGetEngine\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8c3604bc45..4d1ade41a3 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -9,6 +9,7 @@ parameters: bootstrapFiles: - test/phpstan-constants.php stubFiles: + - test/stubs/Query.stub - test/stubs/uploadprogress.stub excludePaths: - examples/openid.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3bc713879a..ba3a269359 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -2251,7 +2251,7 @@ $GLOBALS['cfg']['AllowUserDropDatabase'] - + $GLOBALS['cfg']['AllowUserDropDatabase'] $GLOBALS['cfg']['MemoryLimit'] $GLOBALS['format'] @@ -2259,14 +2259,12 @@ $GLOBALS['import_file'] $GLOBALS['import_notice'] $GLOBALS['local_import_file'] - $GLOBALS['table'] $_POST['bkm_label'] $_POST['bkm_label'] $_POST['bookmark_variable'] $_POST['sql_query'] $_POST['sql_query'] $_SESSION['Import_message']['go_back_url'] - $analyzed_sql_results $die['error'] $die['sql'] $importHandle ?? null @@ -2276,15 +2274,11 @@ $GLOBALS['urlParams'] $parameter - + $_FILES['import_file']['name'] $_FILES['import_file']['name'] $_FILES['import_file']['tmp_name'] $_SESSION['Import_message']['go_back_url'] - $analyzed_sql_results['offset'] - $analyzed_sql_results['offset'] - $analyzed_sql_results['reload'] - $analyzed_sql_results['reload'] $die['error'] $die['sql'] @@ -2300,7 +2294,7 @@ $_SESSION['Import_message']['message'] $_SESSION['Import_message']['message'] - + $GLOBALS['MAX_FILE_SIZE'] $GLOBALS['active_page'] $GLOBALS['ajax_reload'] @@ -2323,12 +2317,8 @@ $GLOBALS['msg'] $GLOBALS['my_die'] $GLOBALS['noplugin'] - $GLOBALS['offset'] - $GLOBALS['offset'] $GLOBALS['read_multiply'] $GLOBALS['reload'] - $GLOBALS['reload'] - $GLOBALS['reload'] $GLOBALS['reset_charset'] $GLOBALS['result'] $GLOBALS['run_query'] @@ -2336,8 +2326,6 @@ $GLOBALS['skip_queries'] $GLOBALS['sql_file'] $GLOBALS['sql_query_disabled'] - $GLOBALS['table'] - $GLOBALS['table'] $GLOBALS['timeout_passed'] $GLOBALS['timestamp'] $GLOBALS['urlParams']['local_import_file'] @@ -3102,13 +3090,11 @@ $GLOBALS['cfg']['AllowUserDropDatabase'] - + $GLOBALS['cfg']['AllowUserDropDatabase'] $GLOBALS['db'] $GLOBALS['db'] $GLOBALS['db'] - $GLOBALS['db'] - $GLOBALS['db'] $GLOBALS['disp_message'] ?? null $GLOBALS['errorUrl'] $GLOBALS['errorUrl'] @@ -3117,19 +3103,16 @@ $GLOBALS['message_to_show'] ?? null $GLOBALS['sql_query'] $GLOBALS['sql_query'] - $GLOBALS['table'] - $GLOBALS['table'] $_GET['sql_query'] $_GET['sql_signature'] $_POST['bkm_fields'] - $analyzed_sql_results $GLOBALS['ajax_reload']['reload'] $_POST['bkm_fields']['bkm_label'] $_POST['bkm_fields']['bkm_label'] - + $GLOBALS['ajax_reload'] $GLOBALS['back'] $GLOBALS['db'] @@ -3144,7 +3127,6 @@ $GLOBALS['sql_query'] $GLOBALS['sql_query'] $GLOBALS['sql_query'] - $GLOBALS['table'] $GLOBALS['table_from_sql'] $GLOBALS['unlim_num_rows'] $GLOBALS['unlim_num_rows'] @@ -3887,16 +3869,12 @@ - - $analyzed_sql_results ?? '' + $sval $sval - - $analyzed_sql_results ?? '' - @@ -4274,7 +4252,7 @@ - + $_POST['db'] $_POST['table'] $_POST['where_clause'] @@ -4285,7 +4263,6 @@ $dataLabel $dataLabel $foreignData['foreign_field'] - $foreignData['foreign_field'] $properties['type'] $selected_operator $this->columnNames[$column_index] @@ -5094,8 +5071,7 @@ - - $db + $table $table @@ -5995,19 +5971,15 @@ $delUrlParams - + + $statementInfo->statement->expr + $statementInfo->statement->where + + $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['pos'] / $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['query'] $_SESSION['tmpval']['query'] - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['select_tables'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement']->from $clause $clauseIsUnique $clauseIsUnique @@ -6037,6 +6009,7 @@ $sortExpressionNoDirection[$indexInExpression] $sortExpressionNoDirection[$indexInExpression] $sqlQuery + $statementInfo->statement->from $total $urlParams['where_clause'] $whereClause @@ -6046,15 +6019,14 @@ (int) $this->properties['unlim_num_rows'] / $_SESSION['tmpval']['max_rows'] empty($field->database) ? $this->properties['db'] : $field->database - - $analyzedSqlResults + $linkingUrlParams $row $sortDirection $sortExpression $urlParams - + $_SESSION['tmpval']['display_binary'] $_SESSION['tmpval']['display_binary'] $_SESSION['tmpval']['display_binary'] @@ -6078,7 +6050,6 @@ $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['max_rows'] - $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['pftext'] $_SESSION['tmpval']['pftext'] $_SESSION['tmpval']['pftext'] @@ -6088,8 +6059,6 @@ $_SESSION['tmpval']['pos'] $_SESSION['tmpval']['pos'] $_SESSION['tmpval']['pos'] - $_SESSION['tmpval']['pos'] - $_SESSION['tmpval']['pos'] $_SESSION['tmpval']['possible_as_geometry'] $_SESSION['tmpval']['query'] $_SESSION['tmpval']['query'] @@ -6196,7 +6165,7 @@ $row[$sortedColumnIndex] $row[$sortedColumnIndex] - + $GLOBALS['row'] $GLOBALS['theme'] $_SESSION['tmpval']['geoOption'] @@ -6214,16 +6183,11 @@ $expr $field $file - $firstShownRec - $firstShownRec - $firstShownRec $hiddenFields['session_max_rows'] $i $i $identifier $index - $lastShownRec - $lastShownRec $linkingUrlParams[$new_param['param_info']] $m $meta->name @@ -6238,7 +6202,6 @@ $query['relational_display'] $rel $relationalDisplay - $rowCount $rowInfo[mb_strtolower($fieldsMeta[$m]->orgname)] $sessionMaxRows $sessionMaxRows @@ -6251,11 +6214,14 @@ $whereClauseMap[$rowNumber][$meta->orgtable] $whereClauseMap[$rowNumber][$this->properties['table']] + + Message + new $className() new $this->transformationInfo[$dbLower][$tblLower][$nameLower][1]() - + $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['pos'] @@ -6266,27 +6232,17 @@ $displaySize[0] $displaySize[1] $file - $firstShownRec - $firstShownRec $sortExpressionNoDirection[$indexInExpression] - - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['statement']->expr - $analyzedSqlResults['statement']->from - $analyzedSqlResults['statement']->from - $analyzedSqlResults['statement']->from[0]->table - $analyzedSqlResults['statement']->limit - $analyzedSqlResults['statement']->limit->offset - $analyzedSqlResults['statement']->limit->rowCount - $analyzedSqlResults['statement']->where + $expr->alias $expr->column $expr->column $expr->identifiers $field->database $field->table + $statementInfo->statement->from[0]->table + $statementInfo->statement->limit->offset $map @@ -6325,10 +6281,20 @@ (string) $fieldsMeta[$i]->name (string) $fieldsMeta[$i]->name + + empty($statementInfo->statement->from) + $firstStatement->order isset($meta->internalMediaType) + + $afterCount + $posNext + $preCount + $sortedColumnMessage + $total + @@ -7647,23 +7613,12 @@ $server['ssl'] $server['ssl_verify'] - + $GLOBALS['special_message'] $GLOBALS['special_message'] $alt $defaultFunction $field['True_Type'] - $queryBase - $queryBase - $queryBase - $sqlQuery - $sqlQuery - $sqlQuery - $sqlQuery - $sqlQuery - $sqlQuery - $sqlQuery - $sqlQuery $subvalue $title $value @@ -7683,17 +7638,14 @@ $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['pos'] - + $GLOBALS['data'] $alt $defaultFunction $defaultFunction $defaultFunction - $queryBase - $sqlQuery $subvalue $title - $urlParams['sql_query'] $value $value @@ -7703,10 +7655,9 @@ getDisplay - + $GLOBALS['using_bookmark_message']->getDisplay() $attributes['class'] - $sqlQuery $value @@ -9070,20 +9021,6 @@ $content - - - $analyzedSqlResults['select_tables'] - $db - - - $analyzedSqlResults['select_tables'][0] - $analyzedSqlResults['select_tables'][0] - - - $db - $table - - $row['Table'] @@ -13034,41 +12971,12 @@ $unlimNumRows - + $_POST[$requestIndex] $_POST['bkm_label'] $_POST['dropped_column'] ?? null $_POST['table_create_time'] ?? null - $analyzedSqlResults - $analyzedSqlResults['is_affected'] - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_show'] - $analyzedSqlResults['is_show'] - $analyzedSqlResults['parser']->errors - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['select_expr'] - $analyzedSqlResults['select_tables'] - $analyzedSqlResults['select_tables'] - $analyzedSqlResults['select_tables'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement'] - $analyzedSqlResults['statement']->where $columns[$indexColumnName]['Extra'] - $db $extraData['error'] $fieldInfoResult[0]['Type'] $foreignData['foreign_field'] @@ -13076,19 +12984,15 @@ $oneResult['Duration'] $oneResult['Status'] $sortCol - $statement - $table - $tokenList $unlimNumRows $unlimNumRows Message::sanitize($warning) - - $analyzedSqlResults + $showTable $showTable - + $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['max_rows'] $_SESSION['tmpval']['max_rows'] @@ -13096,8 +13000,6 @@ $_SESSION['tmpval']['pos'] $_SESSION['tmpval']['pos'] $_SESSION['tmpval']['pos'] - $analyzedSqlResults['select_expr'][0] - $analyzedSqlResults['statement']->where[0] $fieldInfoResult[0]['Type'] $oneResult['Duration'] $oneResult['Duration'] @@ -13106,6 +13008,7 @@ $oneResult['Duration'] $oneResult['Duration'] $oneResult['Status'] + $statementInfo->statement->where[0] $_SESSION['tmpval']['pos'] @@ -13114,7 +13017,7 @@ $columns[$indexColumnName] - + $maxRows $oneFieldMeta $oneMeta @@ -13126,11 +13029,6 @@ $profiling['total_time'] $resultSetColumnNames[] $sortCol - $statement - $statement - $statement - $table - $tokenList $unlimNumRows $unlimNumRows $unlimNumRows @@ -13153,16 +13051,10 @@ $oneResult['Duration'] $profiling['chart'][$status] - - $analyzedSqlResults['parser']->errors - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['parser']->list - $analyzedSqlResults['statement']->where - $analyzedSqlResults['statement']->where[0]->expr + $oneFieldMeta->table $oneMeta->name + $statementInfo->statement->where[0]->expr $pos @@ -15146,42 +15038,10 @@ - - $analyzedSqlResults - $analyzedSqlResults - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_show'] - $analyzedSqlResults['is_show'] + $output $output - - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_analyse'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_count'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_explain'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_export'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_func'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_maint'] - $analyzedSqlResults['is_show'] - $analyzedSqlResults['is_show'] - $_SESSION['tmpval']['display_binary'] $_SESSION['tmpval']['display_binary'] @@ -15200,8 +15060,7 @@ $output $output - - array + array array array @@ -16149,18 +16008,6 @@ - - $this->parseAndAnalyze('DROP DATABASE db') - $this->parseAndAnalyze('DROP TABLE tbl') - $this->parseAndAnalyze('SELECT * FROM db.tbl') - $this->parseAndAnalyze('SELECT * FROM tbl WHERE 1') - $this->parseAndAnalyze('SELECT * from tbl') - $this->parseAndAnalyze('SELECT * from tbl1, tbl2 LIMIT 0, 10') - - - $analyzed_sql_results - $analyzed_sql_results - array diff --git a/psalm.xml b/psalm.xml index 467c942d19..cf26119f58 100644 --- a/psalm.xml +++ b/psalm.xml @@ -26,6 +26,7 @@ + @@ -305,6 +306,7 @@ text_dir: string, token_mismatch: bool, token_provided: bool, + unparsed_sql?: string, urlParams: array, username: string, xml_export_triggers: bool, diff --git a/test/classes/Display/ResultsTest.php b/test/classes/Display/ResultsTest.php index 051a5a6ce8..c2956979bb 100644 --- a/test/classes/Display/ResultsTest.php +++ b/test/classes/Display/ResultsTest.php @@ -15,8 +15,8 @@ use PhpMyAdmin\ParseAnalyze; use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_External; use PhpMyAdmin\Plugins\Transformations\Text_Plain_Link; use PhpMyAdmin\Plugins\TransformationsPlugin; -use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\SqlParser\Utils\Query; +use PhpMyAdmin\StatementInfo; use PhpMyAdmin\Template; use PhpMyAdmin\Tests\AbstractTestCase; use PhpMyAdmin\Transformations; @@ -82,18 +82,12 @@ class ResultsTest extends AbstractTestCase */ public function testisSelect(): void { - $parser = new Parser('SELECT * FROM pma'); $this->assertTrue( $this->callFunction( $this->object, DisplayResults::class, 'isSelect', - [ - [ - 'statement' => $parser->statements[0], - 'select_from' => true, - ], - ] + [StatementInfo::fromArray(Query::getAll('SELECT * FROM pma'))] ) ); } @@ -368,45 +362,21 @@ class ResultsTest extends AbstractTestCase ); } - /** - * Data provider for testSetHighlightedColumnGlobalField - * - * @return array parameters and output - */ - public function dataProviderForTestSetHighlightedColumnGlobalField(): array - { - $parser = new Parser('SELECT * FROM db_name WHERE `db_name`.`tbl`.id > 0 AND `id` < 10'); - - return [ - [ - ['statement' => $parser->statements[0]], - [ - 'db_name' => 'true', - 'tbl' => 'true', - 'id' => 'true', - ], - ], - ]; - } - - /** - * Test setHighlightedColumnGlobalField - * - * @param array $analyzed_sql the analyzed query - * @param array $output setting value of setHighlightedColumnGlobalField - * - * @dataProvider dataProviderForTestSetHighlightedColumnGlobalField - */ - public function testSetHighlightedColumnGlobalField(array $analyzed_sql, array $output): void + public function testSetHighlightedColumnGlobalField(): void { + $query = 'SELECT * FROM db_name WHERE `db_name`.`tbl`.id > 0 AND `id` < 10'; $this->callFunction( $this->object, DisplayResults::class, 'setHighlightedColumnGlobalField', - [$analyzed_sql] + [StatementInfo::fromArray(Query::getAll($query))] ); - $this->assertEquals($output, $this->object->properties['highlight_columns']); + $this->assertEquals([ + 'db_name' => 'true', + 'tbl' => 'true', + 'id' => 'true', + ], $this->object->properties['highlight_columns']); } /** @@ -633,7 +603,6 @@ class ResultsTest extends AbstractTestCase * bool, * TransformationsPlugin|null, * array, - * array, * string * }} */ @@ -684,7 +653,6 @@ class ResultsTest extends AbstractTestCase false, null, ['https://www.example.com/'], - [], 'class="disableAjax">[BLOB - 4 B]' . '' . "\n", ], @@ -698,7 +666,6 @@ class ResultsTest extends AbstractTestCase false, $transformation_plugin, [], - [], '' . '1001' . '' . "\n", @@ -713,7 +680,6 @@ class ResultsTest extends AbstractTestCase false, $transformation_plugin, [], - [], '' . "\n" @@ -730,7 +696,6 @@ class ResultsTest extends AbstractTestCase false, null, [], - [], 'foo bar baz' . "\n", @@ -745,7 +710,6 @@ class ResultsTest extends AbstractTestCase false, $transformation_plugin_external, [], - [], 'foo bar baz' . "\n", @@ -760,7 +724,6 @@ class ResultsTest extends AbstractTestCase false, null, [], - [], '2020-09-20 16:35:00' . "\n", @@ -769,16 +732,15 @@ class ResultsTest extends AbstractTestCase } /** - * @param string $protectBinary all|blob|noblob|no - * @param string|null $column the relevant column in data row - * @param string $class the html class for column - * @param object $meta the meta-information about the field - * @param array $map the list of relations - * @param array $_url_params the parameters for generate url - * @param bool $condition_field the column should highlighted or not - * @param array $transform_options the transformation parameters - * @param array $analyzed_sql_results the analyzed query - * @param string $output the output of this function + * @param string $protectBinary all|blob|noblob|no + * @param string|null $column the relevant column in data row + * @param string $class the html class for column + * @param object $meta the meta-information about the field + * @param array $map the list of relations + * @param array $_url_params the parameters for generate url + * @param bool $condition_field the column should highlighted or not + * @param array $transform_options the transformation parameters + * @param string $output the output of this function * * @dataProvider dataProviderForTestGetDataCellForNonNumericColumns */ @@ -792,7 +754,6 @@ class ResultsTest extends AbstractTestCase bool $condition_field, ?TransformationsPlugin $transformation_plugin, array $transform_options, - array $analyzed_sql_results, string $output ): void { $_SESSION['tmpval']['display_binary'] = true; @@ -800,6 +761,7 @@ class ResultsTest extends AbstractTestCase $_SESSION['tmpval']['relational_display'] = false; $GLOBALS['cfg']['LimitChars'] = 50; $GLOBALS['cfg']['ProtectBinary'] = $protectBinary; + $statementInfo = $this->createStub(StatementInfo::class); $this->assertStringContainsString( $output, $this->callFunction( @@ -815,7 +777,7 @@ class ResultsTest extends AbstractTestCase $condition_field, $transformation_plugin, $transform_options, - $analyzed_sql_results, + $statementInfo, ] ) ); @@ -906,7 +868,7 @@ class ResultsTest extends AbstractTestCase 'disabled', false, $query, - Query::getAll($query), + StatementInfo::fromArray(Query::getAll($query)), ] ); @@ -1319,7 +1281,7 @@ class ResultsTest extends AbstractTestCase $object = new DisplayResults($this->dbi, $GLOBALS['db'], $GLOBALS['table'], 1, '', $query); $object->properties['unique_id'] = 1234567890; - [$analyzedSqlResults] = ParseAnalyze::sqlQuery($query, $GLOBALS['db']); + [$statementInfo] = ParseAnalyze::sqlQuery($query, $GLOBALS['db']); $fieldsMeta = [ new FieldMetadata( MYSQLI_TYPE_DECIMAL, @@ -1333,17 +1295,17 @@ class ResultsTest extends AbstractTestCase $object->setProperties( 3, $fieldsMeta, - $analyzedSqlResults['is_count'], - $analyzedSqlResults['is_export'], - $analyzedSqlResults['is_func'], - $analyzedSqlResults['is_analyse'], + $statementInfo->isCount, + $statementInfo->isExport, + $statementInfo->isFunction, + $statementInfo->isAnalyse, 3, count($fieldsMeta), 1.234, 'ltr', - $analyzedSqlResults['is_maint'], - $analyzedSqlResults['is_explain'], - $analyzedSqlResults['is_show'], + $statementInfo->isMaint, + $statementInfo->isExplain, + $statementInfo->isShow, null, null, true, @@ -1376,7 +1338,7 @@ class ResultsTest extends AbstractTestCase ]); $this->assertNotFalse($dtResult); - $actual = $object->getTable($dtResult, $displayParts, $analyzedSqlResults); + $actual = $object->getTable($dtResult, $displayParts, $statementInfo); $template = new Template(); @@ -1599,7 +1561,7 @@ class ResultsTest extends AbstractTestCase $object = new DisplayResults($dbi, $GLOBALS['db'], $GLOBALS['table'], 1, '', $query); $object->properties['unique_id'] = 1234567890; - [$analyzedSqlResults] = ParseAnalyze::sqlQuery($query, $GLOBALS['db']); + [$statementInfo] = ParseAnalyze::sqlQuery($query, $GLOBALS['db']); $fieldsMeta = [ new FieldMetadata( MYSQLI_TYPE_LONG, @@ -1614,17 +1576,17 @@ class ResultsTest extends AbstractTestCase $object->setProperties( 2, $fieldsMeta, - $analyzedSqlResults['is_count'], - $analyzedSqlResults['is_export'], - $analyzedSqlResults['is_func'], - $analyzedSqlResults['is_analyse'], + $statementInfo->isCount, + $statementInfo->isExport, + $statementInfo->isFunction, + $statementInfo->isAnalyse, 2, count($fieldsMeta), 1.234, 'ltr', - $analyzedSqlResults['is_maint'], - $analyzedSqlResults['is_explain'], - $analyzedSqlResults['is_show'], + $statementInfo->isMaint, + $statementInfo->isExplain, + $statementInfo->isShow, null, null, true, @@ -1657,7 +1619,7 @@ class ResultsTest extends AbstractTestCase ]); $this->assertNotFalse($dtResult); - $actual = $object->getTable($dtResult, $displayParts, $analyzedSqlResults); + $actual = $object->getTable($dtResult, $displayParts, $statementInfo); $template = new Template(); diff --git a/test/classes/ParseAnalyzeTest.php b/test/classes/ParseAnalyzeTest.php new file mode 100644 index 0000000000..4e35a7a202 --- /dev/null +++ b/test/classes/ParseAnalyzeTest.php @@ -0,0 +1,59 @@ +setAjax(false); + + $GLOBALS['unparsed_sql'] = ''; + + $actual = ParseAnalyze::sqlQuery('SELECT * FROM `sakila`.`actor`', 'sakila_test'); + + /** @psalm-suppress TypeDoesNotContainType */ + $this->assertSame('SELECT * FROM `sakila`.`actor`', $GLOBALS['unparsed_sql']); + $this->assertCount(3, $actual); + $this->assertInstanceOf(StatementInfo::class, $actual[0]); + $this->assertSame('sakila', $actual[1]); + $this->assertSame('actor', $actual[2]); + $this->assertTrue($actual[0]->reload); + $this->assertNotEmpty($actual[0]->selectTables); + $this->assertSame([['actor', 'sakila']], $actual[0]->selectTables); + $this->assertNotEmpty($actual[0]->selectExpression); + $this->assertSame(['*'], $actual[0]->selectExpression); + } + + public function testSqlQuery2(): void + { + $GLOBALS['lang'] = 'en'; + ResponseRenderer::getInstance()->setAjax(false); + + $GLOBALS['unparsed_sql'] = ''; + + $actual = ParseAnalyze::sqlQuery('SELECT `first_name`, `title` FROM `actor`, `film`', 'sakila'); + + /** @psalm-suppress TypeDoesNotContainType */ + $this->assertSame('SELECT `first_name`, `title` FROM `actor`, `film`', $GLOBALS['unparsed_sql']); + $this->assertCount(3, $actual); + $this->assertInstanceOf(StatementInfo::class, $actual[0]); + $this->assertSame('sakila', $actual[1]); + $this->assertSame('', $actual[2]); + $this->assertFalse($actual[0]->reload); + $this->assertNotEmpty($actual[0]->selectTables); + $this->assertSame([['actor', null], ['film', null]], $actual[0]->selectTables); + $this->assertNotEmpty($actual[0]->selectExpression); + $this->assertSame(['`first_name`', '`title`'], $actual[0]->selectExpression); + } +} diff --git a/test/classes/SqlTest.php b/test/classes/SqlTest.php index b464cbfe8a..6cc393239d 100644 --- a/test/classes/SqlTest.php +++ b/test/classes/SqlTest.php @@ -72,11 +72,12 @@ class SqlTest extends AbstractTestCase $GLOBALS['_SESSION']['tmpval']['pos'] = 1; $GLOBALS['_SESSION']['tmpval']['max_rows'] = 2; - $analyzed_sql_results = $this->parseAndAnalyze('SELECT * FROM test LIMIT 0, 10'); - $this->assertEquals( - 'SELECT * FROM test LIMIT 1, 2 ', - $this->callFunction($this->sql, Sql::class, 'getSqlWithLimitClause', [&$analyzed_sql_results]) - ); + $this->assertEquals('SELECT * FROM test LIMIT 1, 2 ', $this->callFunction( + $this->sql, + Sql::class, + 'getSqlWithLimitClause', + [ParseAnalyze::sqlQuery('SELECT * FROM test LIMIT 0, 10', $GLOBALS['db'])[0]] + )); } /** @@ -89,31 +90,31 @@ class SqlTest extends AbstractTestCase $this->assertTrue( $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [ - $this->parseAndAnalyze('SELECT * FROM tbl'), + ParseAnalyze::sqlQuery('SELECT * FROM tbl', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [ - $this->parseAndAnalyze('SELECT col FROM tbl'), + ParseAnalyze::sqlQuery('SELECT col FROM tbl', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [ - $this->parseAndAnalyze('SELECT 1'), + ParseAnalyze::sqlQuery('SELECT 1', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [ - $this->parseAndAnalyze('SELECT col1, col2 FROM tbl'), + ParseAnalyze::sqlQuery('SELECT col1, col2 FROM tbl', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isRememberSortingOrder', [ - $this->parseAndAnalyze('SELECT COUNT(*) from tbl'), + ParseAnalyze::sqlQuery('SELECT COUNT(*) from tbl', $GLOBALS['db'])[0], ]) ); } @@ -128,13 +129,13 @@ class SqlTest extends AbstractTestCase $this->assertTrue( $this->callFunction($this->sql, Sql::class, 'isAppendLimitClause', [ - $this->parseAndAnalyze('SELECT * FROM tbl'), + ParseAnalyze::sqlQuery('SELECT * FROM tbl', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isAppendLimitClause', [ - $this->parseAndAnalyze('SELECT * from tbl LIMIT 0, 10'), + ParseAnalyze::sqlQuery('SELECT * from tbl LIMIT 0, 10', $GLOBALS['db'])[0], ]) ); } @@ -145,17 +146,17 @@ class SqlTest extends AbstractTestCase $GLOBALS['_SESSION']['tmpval']['max_rows'] = 10; $this->assertTrue(Sql::isJustBrowsing( - $this->parseAndAnalyze('SELECT * FROM db.tbl'), + ParseAnalyze::sqlQuery('SELECT * FROM db.tbl', $GLOBALS['db'])[0], null )); $this->assertTrue(Sql::isJustBrowsing( - $this->parseAndAnalyze('SELECT * FROM tbl WHERE 1'), + ParseAnalyze::sqlQuery('SELECT * FROM tbl WHERE 1', $GLOBALS['db'])[0], null )); $this->assertFalse(Sql::isJustBrowsing( - $this->parseAndAnalyze('SELECT * from tbl1, tbl2 LIMIT 0, 10'), + ParseAnalyze::sqlQuery('SELECT * from tbl1, tbl2 LIMIT 0, 10', $GLOBALS['db'])[0], null )); } @@ -167,19 +168,19 @@ class SqlTest extends AbstractTestCase { $this->assertTrue( $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [ - $this->parseAndAnalyze('ALTER TABLE tbl DROP COLUMN col'), + ParseAnalyze::sqlQuery('ALTER TABLE tbl DROP COLUMN col', $GLOBALS['db'])[0], ]) ); $this->assertTrue( $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [ - $this->parseAndAnalyze('DROP TABLE tbl'), + ParseAnalyze::sqlQuery('DROP TABLE tbl', $GLOBALS['db'])[0], ]) ); $this->assertFalse( $this->callFunction($this->sql, Sql::class, 'isDeleteTransformationInfo', [ - $this->parseAndAnalyze('SELECT * from tbl'), + ParseAnalyze::sqlQuery('SELECT * from tbl', $GLOBALS['db'])[0], ]) ); } @@ -191,7 +192,7 @@ class SqlTest extends AbstractTestCase { $this->assertTrue( $this->sql->hasNoRightsToDropDatabase( - $this->parseAndAnalyze('DROP DATABASE db'), + ParseAnalyze::sqlQuery('DROP DATABASE db', $GLOBALS['db'])[0], false, false ) @@ -199,7 +200,7 @@ class SqlTest extends AbstractTestCase $this->assertFalse( $this->sql->hasNoRightsToDropDatabase( - $this->parseAndAnalyze('DROP TABLE tbl'), + ParseAnalyze::sqlQuery('DROP TABLE tbl', $GLOBALS['db'])[0], false, false ) @@ -207,7 +208,7 @@ class SqlTest extends AbstractTestCase $this->assertFalse( $this->sql->hasNoRightsToDropDatabase( - $this->parseAndAnalyze('SELECT * from tbl'), + ParseAnalyze::sqlQuery('SELECT * from tbl', $GLOBALS['db'])[0], false, false ) @@ -342,16 +343,6 @@ class SqlTest extends AbstractTestCase ); } - /** - * @return mixed - */ - private function parseAndAnalyze(string $sqlQuery) - { - [$analyzedSqlResults] = ParseAnalyze::sqlQuery($sqlQuery, $GLOBALS['db']); - - return $analyzedSqlResults; - } - public function dataProviderCountQueryResults(): array { // sql query @@ -552,8 +543,6 @@ class SqlTest extends AbstractTestCase $_SESSION['tmpval'] = $sessionTmpVal; - $analyzed_sql_results = $sqlQuery === null ? [] : $this->parseAndAnalyze($sqlQuery); - $result = $this->callFunction( $this->sql, Sql::class, @@ -563,7 +552,7 @@ class SqlTest extends AbstractTestCase $justBrowsing, 'my_dataset',// db 'company_users',// table - $analyzed_sql_results, + ParseAnalyze::sqlQuery($sqlQuery ?? '', $GLOBALS['db'])[0], ] ); $this->assertSame($expectedNumRows, $result); diff --git a/test/classes/StatementInfoTest.php b/test/classes/StatementInfoTest.php new file mode 100644 index 0000000000..21e41a96be --- /dev/null +++ b/test/classes/StatementInfoTest.php @@ -0,0 +1,159 @@ +statements[0]; + $info = [ + 'distinct' => false, + 'drop_database' => false, + 'group' => false, + 'having' => false, + 'is_affected' => false, + 'is_analyse' => false, + 'is_count' => false, + 'is_delete' => false, + 'is_explain' => false, + 'is_export' => false, + 'is_func' => false, + 'is_group' => false, + 'is_insert' => false, + 'is_maint' => false, + 'is_procedure' => false, + 'is_replace' => false, + 'is_select' => true, + 'is_show' => false, + 'is_subquery' => false, + 'join' => false, + 'limit' => false, + 'offset' => false, + 'order' => false, + 'querytype' => 'SELECT', + 'reload' => false, + 'select_from' => true, + 'union' => false, + 'parser' => $parser, + 'statement' => $statement, + 'select_tables' => [['actor', null]], + 'select_expr' => ['*'], + ]; + + $statementInfo = StatementInfo::fromArray($info); + + $this->assertFalse($statementInfo->distinct); + $this->assertFalse($statementInfo->dropDatabase); + $this->assertFalse($statementInfo->group); + $this->assertFalse($statementInfo->having); + $this->assertFalse($statementInfo->isAffected); + $this->assertFalse($statementInfo->isAnalyse); + $this->assertFalse($statementInfo->isCount); + $this->assertFalse($statementInfo->isDelete); + $this->assertFalse($statementInfo->isExplain); + $this->assertFalse($statementInfo->isExport); + $this->assertFalse($statementInfo->isFunction); + $this->assertFalse($statementInfo->isGroup); + $this->assertFalse($statementInfo->isInsert); + $this->assertFalse($statementInfo->isMaint); + $this->assertFalse($statementInfo->isProcedure); + $this->assertFalse($statementInfo->isReplace); + $this->assertTrue($statementInfo->isSelect); + $this->assertFalse($statementInfo->isShow); + $this->assertFalse($statementInfo->isSubquery); + $this->assertFalse($statementInfo->join); + $this->assertFalse($statementInfo->limit); + $this->assertFalse($statementInfo->offset); + $this->assertFalse($statementInfo->order); + $this->assertSame('SELECT', $statementInfo->queryType); + $this->assertFalse($statementInfo->reload); + $this->assertTrue($statementInfo->selectFrom); + $this->assertFalse($statementInfo->union); + $this->assertNotNull($statementInfo->parser); + $this->assertNotNull($statementInfo->statement); + $this->assertNotEmpty($statementInfo->selectTables); + $this->assertNotEmpty($statementInfo->selectExpression); + $this->assertSame($info['parser'], $statementInfo->parser); + $this->assertSame($info['statement'], $statementInfo->statement); + $this->assertSame($info['select_tables'], $statementInfo->selectTables); + $this->assertSame($info['select_expr'], $statementInfo->selectExpression); + } + + public function testFromArrayWithEmptyStatement(): void + { + $info = [ + 'distinct' => false, + 'drop_database' => false, + 'group' => false, + 'having' => false, + 'is_affected' => false, + 'is_analyse' => false, + 'is_count' => false, + 'is_delete' => false, + 'is_explain' => false, + 'is_export' => false, + 'is_func' => false, + 'is_group' => false, + 'is_insert' => false, + 'is_maint' => false, + 'is_procedure' => false, + 'is_replace' => false, + 'is_select' => false, + 'is_show' => false, + 'is_subquery' => false, + 'join' => false, + 'limit' => false, + 'offset' => false, + 'order' => false, + 'querytype' => false, + 'reload' => false, + 'select_from' => false, + 'union' => false, + ]; + + $statementInfo = StatementInfo::fromArray($info); + + $this->assertFalse($statementInfo->distinct); + $this->assertFalse($statementInfo->dropDatabase); + $this->assertFalse($statementInfo->group); + $this->assertFalse($statementInfo->having); + $this->assertFalse($statementInfo->isAffected); + $this->assertFalse($statementInfo->isAnalyse); + $this->assertFalse($statementInfo->isCount); + $this->assertFalse($statementInfo->isDelete); + $this->assertFalse($statementInfo->isExplain); + $this->assertFalse($statementInfo->isExport); + $this->assertFalse($statementInfo->isFunction); + $this->assertFalse($statementInfo->isGroup); + $this->assertFalse($statementInfo->isInsert); + $this->assertFalse($statementInfo->isMaint); + $this->assertFalse($statementInfo->isProcedure); + $this->assertFalse($statementInfo->isReplace); + $this->assertFalse($statementInfo->isSelect); + $this->assertFalse($statementInfo->isShow); + $this->assertFalse($statementInfo->isSubquery); + $this->assertFalse($statementInfo->join); + $this->assertFalse($statementInfo->limit); + $this->assertFalse($statementInfo->offset); + $this->assertFalse($statementInfo->order); + $this->assertFalse($statementInfo->queryType); + $this->assertFalse($statementInfo->reload); + $this->assertFalse($statementInfo->selectFrom); + $this->assertFalse($statementInfo->union); + $this->assertNull($statementInfo->parser); + $this->assertNull($statementInfo->statement); + $this->assertEmpty($statementInfo->selectTables); + $this->assertEmpty($statementInfo->selectExpression); + } +} diff --git a/test/stubs/Query.stub b/test/stubs/Query.stub new file mode 100644 index 0000000000..9a09dacd5c --- /dev/null +++ b/test/stubs/Query.stub @@ -0,0 +1,53 @@ +, + * select_expr?: list + * } + */ + public static function getAll($query); +}