diff --git a/libraries/classes/Controllers/Table/ChartController.php b/libraries/classes/Controllers/Table/ChartController.php index 6da1dea342..ede10df354 100644 --- a/libraries/classes/Controllers/Table/ChartController.php +++ b/libraries/classes/Controllers/Table/ChartController.php @@ -6,6 +6,7 @@ namespace PhpMyAdmin\Controllers\Table; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\DbTableExists; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Message; use PhpMyAdmin\Response; use PhpMyAdmin\SqlParser\Components\Limit; @@ -16,7 +17,6 @@ use PhpMyAdmin\Url; use PhpMyAdmin\Util; use function array_keys; use function htmlspecialchars; -use function in_array; use function json_encode; use function min; use function strlen; @@ -125,20 +125,19 @@ class ChartController extends AbstractController $data = []; $result = $this->dbi->tryQuery($sql_query); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; while ($row = $this->dbi->fetchAssoc($result)) { $data[] = $row; } $keys = array_keys($data[0]); - $numeric_types = [ - 'int', - 'real', - ]; $numeric_column_count = 0; foreach ($keys as $idx => $key) { - if (! in_array($fields_meta[$idx]->type, $numeric_types)) { + if (isset($fields_meta[$idx]) && ( + $fields_meta[$idx]->isNotType(FieldMetadata::TYPE_INT) + || $fields_meta[$idx]->isNotType(FieldMetadata::TYPE_REAL) + )) { continue; } @@ -165,7 +164,6 @@ class ChartController extends AbstractController 'url_params' => $url_params, 'keys' => $keys, 'fields_meta' => $fields_meta, - 'numeric_types' => $numeric_types, 'numeric_column_count' => $numeric_column_count, 'sql_query' => $sql_query, ]); diff --git a/libraries/classes/Controllers/Table/GisVisualizationController.php b/libraries/classes/Controllers/Table/GisVisualizationController.php index 18435a855b..1bf16fc6c6 100644 --- a/libraries/classes/Controllers/Table/GisVisualizationController.php +++ b/libraries/classes/Controllers/Table/GisVisualizationController.php @@ -73,13 +73,13 @@ final class GisVisualizationController extends AbstractController // Execute the query and return the result $result = $this->dbi->tryQuery($sqlQuery); // Get the meta data of results - $meta = $this->dbi->getFieldsMeta($result); + $meta = $this->dbi->getFieldsMeta($result) ?? []; // Find the candidate fields for label column and spatial column $labelCandidates = []; $spatialCandidates = []; foreach ($meta as $column_meta) { - if ($column_meta->type === 'geometry') { + if ($column_meta->isMappedTypeGeometry) { $spatialCandidates[] = $column_meta->name; } else { $labelCandidates[] = $column_meta->name; diff --git a/libraries/classes/Controllers/Table/SearchController.php b/libraries/classes/Controllers/Table/SearchController.php index 829a01fce4..592069143b 100644 --- a/libraries/classes/Controllers/Table/SearchController.php +++ b/libraries/classes/Controllers/Table/SearchController.php @@ -249,7 +249,7 @@ class SearchController extends AbstractController // for bit fields we need to convert them to printable form $i = 0; foreach ($row as $col => $val) { - if ($fields_meta[$i]->type === 'bit') { + if (isset($fields_meta[$i]) && $fields_meta[$i]->isMappedTypeBit) { $row[$col] = Util::printableBitValue( (int) $val, (int) $fields_meta[$i]->length diff --git a/libraries/classes/Controllers/Table/ZoomSearchController.php b/libraries/classes/Controllers/Table/ZoomSearchController.php index dc389744f9..4b5aedabfe 100644 --- a/libraries/classes/Controllers/Table/ZoomSearchController.php +++ b/libraries/classes/Controllers/Table/ZoomSearchController.php @@ -294,12 +294,12 @@ class ZoomSearchController extends AbstractController DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE ); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; while ($row = $this->dbi->fetchAssoc($result)) { // for bit fields we need to convert them to printable form $i = 0; foreach ($row as $col => $val) { - if ($fields_meta[$i]->type === 'bit') { + if ($fields_meta[$i]->isMappedTypeBit) { $row[$col] = Util::printableBitValue( (int) $val, (int) $fields_meta[$i]->length @@ -363,7 +363,7 @@ class ZoomSearchController extends AbstractController DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE ); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; $data = []; while ($row = $this->dbi->fetchAssoc($result)) { //Need a row with indexes as 0,1,2 for the getUniqueCondition diff --git a/libraries/classes/Database/Routines.php b/libraries/classes/Database/Routines.php index a73d1642bd..21570be550 100644 --- a/libraries/classes/Database/Routines.php +++ b/libraries/classes/Database/Routines.php @@ -1226,7 +1226,8 @@ class Routines if (($result !== false) && ($num_rows > 0)) { $output .= '
| '; $output .= htmlspecialchars($field->name); $output .= ' | '; diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index 9e006a49a3..ea94e65873 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -772,6 +772,7 @@ class DatabaseInterface implements DbalInterface return []; } + /** @var FieldMetadata[] $meta */ $meta = $this->getFieldsMeta( $result ); @@ -2219,13 +2220,13 @@ class DatabaseInterface implements DbalInterface * * @param object $result result set identifier * - * @return mixed meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result) + public function getFieldsMeta($result): ?array { $result = $this->extension->getFieldsMeta($result); - if ($this->getLowerCaseNames() === '2') { + if ($result !== null && $this->getLowerCaseNames() === '2') { /** * Fixup orgtable for lower_case_table_names = 2 * @@ -2285,19 +2286,6 @@ class DatabaseInterface implements DbalInterface return $this->extension->fieldName($result, $i); } - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i): string - { - return $this->extension->fieldFlags($result, $i); - } - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbalInterface.php b/libraries/classes/Dbal/DbalInterface.php index c0da4c1f8e..7de78b4b14 100644 --- a/libraries/classes/Dbal/DbalInterface.php +++ b/libraries/classes/Dbal/DbalInterface.php @@ -6,6 +6,7 @@ namespace PhpMyAdmin\Dbal; use mysqli_result; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\SystemDatabase; use PhpMyAdmin\Table; @@ -670,9 +671,9 @@ interface DbalInterface * * @param object $result result set identifier * - * @return mixed meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result); + public function getFieldsMeta($result): ?array; /** * return number of fields in given $result @@ -703,16 +704,6 @@ interface DbalInterface */ public function fieldName($result, int $i): string; - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i): string; - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbiExtension.php b/libraries/classes/Dbal/DbiExtension.php index 102294680b..63a732a14a 100644 --- a/libraries/classes/Dbal/DbiExtension.php +++ b/libraries/classes/Dbal/DbiExtension.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Dbal; +use PhpMyAdmin\FieldMetadata; + /** * Contract for every database extension supported by phpMyAdmin */ @@ -184,9 +186,9 @@ interface DbiExtension * * @param object $result result set identifier * - * @return array meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result); + public function getFieldsMeta($result): ?array; /** * return number of fields in given $result @@ -217,16 +219,6 @@ interface DbiExtension */ public function fieldName($result, $i); - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i); - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbiMysqli.php b/libraries/classes/Dbal/DbiMysqli.php index 546a4c2d20..814baeb2c0 100644 --- a/libraries/classes/Dbal/DbiMysqli.php +++ b/libraries/classes/Dbal/DbiMysqli.php @@ -11,64 +11,23 @@ use mysqli; use mysqli_result; use mysqli_stmt; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Query\Utilities; use stdClass; use function mysqli_report; use const E_USER_WARNING; use const MYSQLI_ASSOC; -use const MYSQLI_AUTO_INCREMENT_FLAG; -use const MYSQLI_BLOB_FLAG; use const MYSQLI_BOTH; use const MYSQLI_CLIENT_COMPRESS; use const MYSQLI_CLIENT_SSL; use const MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; -use const MYSQLI_ENUM_FLAG; -use const MYSQLI_MULTIPLE_KEY_FLAG; -use const MYSQLI_NOT_NULL_FLAG; use const MYSQLI_NUM; -use const MYSQLI_NUM_FLAG; use const MYSQLI_OPT_LOCAL_INFILE; use const MYSQLI_OPT_SSL_VERIFY_SERVER_CERT; -use const MYSQLI_PART_KEY_FLAG; -use const MYSQLI_PRI_KEY_FLAG; use const MYSQLI_REPORT_OFF; -use const MYSQLI_SET_FLAG; use const MYSQLI_STORE_RESULT; -use const MYSQLI_TIMESTAMP_FLAG; -use const MYSQLI_TYPE_BIT; -use const MYSQLI_TYPE_BLOB; -use const MYSQLI_TYPE_DATE; -use const MYSQLI_TYPE_DATETIME; -use const MYSQLI_TYPE_DECIMAL; -use const MYSQLI_TYPE_DOUBLE; -use const MYSQLI_TYPE_ENUM; -use const MYSQLI_TYPE_FLOAT; -use const MYSQLI_TYPE_GEOMETRY; -use const MYSQLI_TYPE_INT24; -use const MYSQLI_TYPE_JSON; -use const MYSQLI_TYPE_LONG; -use const MYSQLI_TYPE_LONG_BLOB; -use const MYSQLI_TYPE_LONGLONG; -use const MYSQLI_TYPE_MEDIUM_BLOB; -use const MYSQLI_TYPE_NEWDATE; -use const MYSQLI_TYPE_NEWDECIMAL; -use const MYSQLI_TYPE_NULL; -use const MYSQLI_TYPE_SET; -use const MYSQLI_TYPE_SHORT; -use const MYSQLI_TYPE_STRING; -use const MYSQLI_TYPE_TIME; -use const MYSQLI_TYPE_TIMESTAMP; -use const MYSQLI_TYPE_TINY; -use const MYSQLI_TYPE_TINY_BLOB; -use const MYSQLI_TYPE_VAR_STRING; -use const MYSQLI_TYPE_YEAR; -use const MYSQLI_UNIQUE_KEY_FLAG; -use const MYSQLI_UNSIGNED_FLAG; use const MYSQLI_USE_RESULT; -use const MYSQLI_ZEROFILL_FLAG; -use function define; use function defined; -use function implode; use function is_array; use function is_bool; use function mysqli_init; @@ -80,23 +39,6 @@ use function trigger_error; */ class DbiMysqli implements DbiExtension { - /** @var array */ - private static $flagNames = [ - MYSQLI_NUM_FLAG => 'num', - MYSQLI_PART_KEY_FLAG => 'part_key', - MYSQLI_SET_FLAG => 'set', - MYSQLI_TIMESTAMP_FLAG => 'timestamp', - MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', - MYSQLI_ENUM_FLAG => 'enum', - MYSQLI_ZEROFILL_FLAG => 'zerofill', - MYSQLI_UNSIGNED_FLAG => 'unsigned', - MYSQLI_BLOB_FLAG => 'blob', - MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', - MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', - MYSQLI_PRI_KEY_FLAG => 'primary_key', - MYSQLI_NOT_NULL_FLAG => 'not_null', - ]; - /** * connects to the database server * @@ -463,84 +405,20 @@ class DbiMysqli implements DbiExtension * * @param mysqli_result $result result set identifier * - * @return array|bool meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result) + public function getFieldsMeta($result): ?array { if (! $result instanceof mysqli_result) { - return false; + return null; } - // Issue #16043 - client API mysqlnd seem not to have MYSQLI_TYPE_JSON defined - if (! defined('MYSQLI_TYPE_JSON')) { - define('MYSQLI_TYPE_JSON', 245); - } - // Build an associative array for a type look up - $typeAr = []; - $typeAr[MYSQLI_TYPE_DECIMAL] = 'real'; - $typeAr[MYSQLI_TYPE_NEWDECIMAL] = 'real'; - $typeAr[MYSQLI_TYPE_BIT] = 'int'; - $typeAr[MYSQLI_TYPE_TINY] = 'int'; - $typeAr[MYSQLI_TYPE_SHORT] = 'int'; - $typeAr[MYSQLI_TYPE_LONG] = 'int'; - $typeAr[MYSQLI_TYPE_FLOAT] = 'real'; - $typeAr[MYSQLI_TYPE_DOUBLE] = 'real'; - $typeAr[MYSQLI_TYPE_NULL] = 'null'; - $typeAr[MYSQLI_TYPE_TIMESTAMP] = 'timestamp'; - $typeAr[MYSQLI_TYPE_LONGLONG] = 'int'; - $typeAr[MYSQLI_TYPE_INT24] = 'int'; - $typeAr[MYSQLI_TYPE_DATE] = 'date'; - $typeAr[MYSQLI_TYPE_TIME] = 'time'; - $typeAr[MYSQLI_TYPE_DATETIME] = 'datetime'; - $typeAr[MYSQLI_TYPE_YEAR] = 'year'; - $typeAr[MYSQLI_TYPE_NEWDATE] = 'date'; - $typeAr[MYSQLI_TYPE_ENUM] = 'unknown'; - $typeAr[MYSQLI_TYPE_SET] = 'unknown'; - $typeAr[MYSQLI_TYPE_TINY_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_LONG_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_VAR_STRING] = 'string'; - $typeAr[MYSQLI_TYPE_STRING] = 'string'; - // MySQL returns MYSQLI_TYPE_STRING for CHAR - // and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY - // so this would override TINYINT and mark all TINYINT as string - // see https://github.com/phpmyadmin/phpmyadmin/issues/8569 - //$typeAr[MYSQLI_TYPE_CHAR] = 'string'; - $typeAr[MYSQLI_TYPE_GEOMETRY] = 'geometry'; - $typeAr[MYSQLI_TYPE_BIT] = 'bit'; - $typeAr[MYSQLI_TYPE_JSON] = 'json'; - $fields = $result->fetch_fields(); - if (! is_array($fields)) { - return false; + return null; } foreach ($fields as $k => $field) { - $fields[$k]->_type = $field->type; - $fields[$k]->type = $typeAr[$field->type]; - $fields[$k]->_flags = $field->flags; - $fields[$k]->flags = $this->fieldFlags($result, $k); - - // Enhance the field objects for mysql-extension compatibility - //$flags = explode(' ', $fields[$k]->flags); - //array_unshift($flags, 'dummy'); - $fields[$k]->multiple_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_MULTIPLE_KEY_FLAG); - $fields[$k]->primary_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_PRI_KEY_FLAG); - $fields[$k]->unique_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_UNIQUE_KEY_FLAG); - $fields[$k]->not_null - = (int) (bool) ($fields[$k]->_flags & MYSQLI_NOT_NULL_FLAG); - $fields[$k]->unsigned - = (int) (bool) ($fields[$k]->_flags & MYSQLI_UNSIGNED_FLAG); - $fields[$k]->zerofill - = (int) (bool) ($fields[$k]->_flags & MYSQLI_ZEROFILL_FLAG); - $fields[$k]->numeric - = (int) (bool) ($fields[$k]->_flags & MYSQLI_NUM_FLAG); - $fields[$k]->blob - = (int) (bool) ($fields[$k]->_flags & MYSQLI_BLOB_FLAG); + $fields[$k] = new FieldMetadata($field->type, $field->flags, $field); } return $fields; @@ -602,53 +480,6 @@ class DbiMysqli implements DbiExtension return ''; } - /** - * returns concatenated string of human readable field flags - * - * @param mysqli_result $result result set identifier - * @param int $i field - * - * @return string|false field flags - */ - public function fieldFlags($result, $i) - { - if ($i >= $this->numFields($result)) { - return false; - } - /** @var stdClass|false $fieldDefinition */ - $fieldDefinition = $result->fetch_field_direct($i); - if ($fieldDefinition === false) { - return ''; - } - - $type = $fieldDefinition->type; - $charsetNumber = $fieldDefinition->charsetnr; - $fieldDefinitionFlags = $fieldDefinition->flags; - $flags = []; - foreach (self::$flagNames as $flag => $name) { - if (! ($fieldDefinitionFlags & $flag)) { - continue; - } - - $flags[] = $name; - } - // See https://dev.mysql.com/doc/refman/6.0/en/c-api-datatypes.html: - // to determine if a string is binary, we should not use MYSQLI_BINARY_FLAG - // but instead the charsetnr member of the MYSQL_FIELD - // structure. Watch out: some types like DATE returns 63 in charsetnr - // so we have to check also the type. - // Unfortunately there is no equivalent in the mysql extension. - if (($type == MYSQLI_TYPE_TINY_BLOB || $type == MYSQLI_TYPE_BLOB - || $type == MYSQLI_TYPE_MEDIUM_BLOB || $type == MYSQLI_TYPE_LONG_BLOB - || $type == MYSQLI_TYPE_VAR_STRING || $type == MYSQLI_TYPE_STRING) - && $charsetNumber == 63 - ) { - $flags[] = 'binary'; - } - - return implode(' ', $flags); - } - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index d3851ff875..1ff4d44f2a 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -7,6 +7,7 @@ namespace PhpMyAdmin\Display; use PhpMyAdmin\Config\SpecialSchemaLinks; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Message; @@ -27,8 +28,6 @@ use PhpMyAdmin\Theme; use PhpMyAdmin\Transformations; use PhpMyAdmin\Url; use PhpMyAdmin\Util; -use stdClass; -use const MYSQLI_TYPE_BIT; use function array_filter; use function array_keys; use function array_merge; @@ -93,15 +92,6 @@ class Results public const HEADER_FLIP_TYPE_CSS = 'css'; public const HEADER_FLIP_TYPE_FAKE = 'fake'; - public const DATE_FIELD = 'date'; - public const DATETIME_FIELD = 'datetime'; - public const TIMESTAMP_FIELD = 'timestamp'; - public const TIME_FIELD = 'time'; - public const STRING_FIELD = 'string'; - public const GEOMETRY_FIELD = 'geometry'; - public const BLOB_FIELD = 'BLOB'; - public const BINARY_FIELD = 'BINARY'; - public const RELATIONAL_KEY = 'K'; public const RELATIONAL_DISPLAY_COLUMN = 'D'; @@ -361,31 +351,31 @@ class Results /** * Set properties which were not initialized at the constructor * - * @param int $unlim_num_rows the total number of rows returned by - * the SQL query without any appended - * "LIMIT" clause programmatically - * @param stdClass $fields_meta meta information about fields - * @param bool $is_count statement is SELECT COUNT - * @param int $is_export statement contains INTO OUTFILE - * @param bool $is_func statement contains a function like SUM() - * @param int $is_analyse statement contains PROCEDURE ANALYSE - * @param int $num_rows total no. of rows returned by SQL query - * @param int $fields_cnt total no.of fields returned by SQL query - * @param double $querytime time taken for execute the SQL query - * @param string $text_dir text direction - * @param bool $is_maint statement contains a maintenance command - * @param bool $is_explain statement contains EXPLAIN - * @param bool $is_show statement contains SHOW - * @param array $showtable table definitions - * @param string $printview print view was requested - * @param bool $editable whether the results set is editable - * @param bool $is_browse_dist whether browsing distinct values + * @param int $unlim_num_rows the total number of rows returned by + * the SQL query without any appended + * "LIMIT" clause programmatically + * @param array $fields_meta meta information about fields + * @param bool $is_count statement is SELECT COUNT + * @param int $is_export statement contains INTO OUTFILE + * @param bool $is_func statement contains a function like SUM() + * @param int $is_analyse statement contains PROCEDURE ANALYSE + * @param int $num_rows total no. of rows returned by SQL query + * @param int $fields_cnt total no.of fields returned by SQL query + * @param double $querytime time taken for execute the SQL query + * @param string $text_dir text direction + * @param bool $is_maint statement contains a maintenance command + * @param bool $is_explain statement contains EXPLAIN + * @param bool $is_show statement contains SHOW + * @param array $showtable table definitions + * @param string $printview print view was requested + * @param bool $editable whether the results set is editable + * @param bool $is_browse_dist whether browsing distinct values * * @return void */ public function setProperties( $unlim_num_rows, - $fields_meta, + array $fields_meta, $is_count, $is_export, $is_func, @@ -537,6 +527,7 @@ class Results // $displayParts['edit_lnk'], $displayParts['del_lnk'] and // $displayParts['text_btn'] (keeps other default values) + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $prev_table = ''; $displayParts['text_btn'] = (string) '1'; @@ -1016,6 +1007,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in the for loop $highlight_columns = $this->properties['highlight_columns']; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; // Prepare Display column comments if enabled @@ -1557,18 +1549,19 @@ class Results * * @see getTableHeaders() * - * @param array $commentsMap comments array - * @param array $fieldsMeta set of field properties + * @param array $commentsMap comments array + * @param FieldMetadata $fieldsMeta set of field properties * * @return string html content * * @access private */ - private function getCommentForRow(array $commentsMap, $fieldsMeta) + private function getCommentForRow(array $commentsMap, FieldMetadata $fieldsMeta) { return $this->template->render('display/results/comment_for_row', [ 'comments_map' => $commentsMap, - 'fields_meta' => $fieldsMeta, + 'column_name' => $fieldsMeta->name, + 'table_name' => $fieldsMeta->table, 'limit_chars' => $GLOBALS['cfg']['LimitChars'], ]); } @@ -1578,23 +1571,23 @@ class Results * * @see getTableHeaders() * - * @param stdClass $fields_meta set of field properties - * @param array $sort_expression sort expression - * @param array $sort_expression_nodirection sort expression without direction - * @param int $column_index the index of the column - * @param string $unsorted_sql_query the unsorted sql query - * @param int $session_max_rows maximum rows resulted by sql - * @param string $comments comment for row - * @param array $sort_direction sort direction - * @param bool $col_visib column is visible(false) or column isn't visible(string array) - * @param string $col_visib_j element of $col_visib array + * @param FieldMetadata $fields_meta set of field properties + * @param array $sort_expression sort expression + * @param array $sort_expression_nodirection sort expression without direction + * @param int $column_index the index of the column + * @param string $unsorted_sql_query the unsorted sql query + * @param int $session_max_rows maximum rows resulted by sql + * @param string $comments comment for row + * @param array $sort_direction sort direction + * @param bool $col_visib column is visible(false) or column isn't visible(string array) + * @param string $col_visib_j element of $col_visib array * * @return array 2 element array - $order_link, $sorted_header_html * * @access private */ private function getOrderLinkAndSortedHeaderHtml( - $fields_meta, + FieldMetadata $fields_meta, array $sort_expression, array $sort_expression_nodirection, $column_index, @@ -1696,14 +1689,14 @@ class Results * * @see getOrderLinkAndSortedHeaderHtml() * - * @param array $sort_expression sort expression - * @param array $sort_expression_nodirection sort expression without direction - * @param string $sort_tbl The name of the table to which - * the current column belongs to - * @param string $name_to_use_in_sort The current column under - * consideration - * @param array $sort_direction sort direction - * @param stdClass $fields_meta set of field properties + * @param array $sort_expression sort expression + * @param array $sort_expression_nodirection sort expression without direction + * @param string $sort_tbl The name of the table to which + * the current column belongs to + * @param string $name_to_use_in_sort The current column under + * consideration + * @param array $sort_direction sort direction + * @param FieldMetadata $fields_meta set of field properties * * @return array 3 element array - $single_sort_order, $sort_order, $order_img * @@ -1715,7 +1708,7 @@ class Results $sort_tbl, $name_to_use_in_sort, array $sort_direction, - $fields_meta + FieldMetadata $fields_meta ) { $sort_order = ''; // Check if the current column is in the order by clause @@ -1734,10 +1727,9 @@ class Results = Util::backquote( $current_name ); - $sort_direction[$special_index] = preg_match( - '@time|date@i', - $fields_meta->type ?? '' - ) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; + $isTimeOrDate = $fields_meta->isType(FieldMetadata::TYPE_TIME) + || $fields_meta->isType(FieldMetadata::TYPE_DATE); + $sort_direction[$special_index] = $isTimeOrDate ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; } $sort_expression_nodirection = array_filter($sort_expression_nodirection); @@ -1979,10 +1971,10 @@ class Results * * @see getTableHeaders() * - * @param string $order_img the sort order image - * @param stdClass $fields_meta set of field properties - * @param string $order_url the url for sort - * @param string $multi_order_url the url for sort + * @param string $order_img the sort order image + * @param FieldMetadata $fields_meta set of field properties + * @param string $order_url the url for sort + * @param string $multi_order_url the url for sort * * @return string the sort order link * @@ -1990,7 +1982,7 @@ class Results */ private function getSortOrderLink( $order_img, - $fields_meta, + FieldMetadata $fields_meta, $order_url, $multi_order_url ) { @@ -2013,21 +2005,22 @@ class Results * * @see getDraggableClassForSortableColumns() * - * @param stdClass $fields_meta set of field properties - * @param array $th_class array containing classes + * @param FieldMetadata $fields_meta set of field properties + * @param array $th_class array containing classes * * @return void */ - private function getClassForNumericColumnType($fields_meta, array &$th_class) + private function getClassForNumericColumnType(FieldMetadata $fields_meta, array &$th_class) { - if (! preg_match( - '@int|decimal|float|double|real|bit|boolean|serial@i', - (string) $fields_meta->type - )) { + // This was defined in commit b661cd7c9b31f8bc564d2f9a1b8527e0eb966de8 + // For issue https://github.com/phpmyadmin/phpmyadmin/issues/4746 + if (! $fields_meta->isType(FieldMetadata::TYPE_REAL) + && ! $fields_meta->isMappedTypeBit + && ! $fields_meta->isType(FieldMetadata::TYPE_INT)) { return; } - $th_class[] = 'text-end'; + $th_class[] = 'text-end'; } /** @@ -2035,12 +2028,12 @@ class Results * * @see getTableHeaders() * - * @param bool $col_visib the column is visible (false) - * array the column is not visible (string array) - * @param string $col_visib_j element of $col_visib array - * @param stdClass $fields_meta set of field properties - * @param string $order_link the order link - * @param string $comments the comment for the column + * @param bool $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param FieldMetadata $fields_meta set of field properties + * @param string $order_link the order link + * @param string $comments the comment for the column * * @return string html content * @@ -2049,7 +2042,7 @@ class Results private function getDraggableClassForSortableColumns( $col_visib, $col_visib_j, - $fields_meta, + FieldMetadata $fields_meta, $order_link, $comments ) { @@ -2084,12 +2077,12 @@ class Results * * @see getTableHeaders() * - * @param bool $col_visib the column is visible (false) - * array the column is not visible (string array) - * @param string $col_visib_j element of $col_visib array - * @param bool $condition_field whether to add CSS class condition - * @param stdClass $fields_meta set of field properties - * @param string $comments the comment for the column + * @param bool $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param bool $condition_field whether to add CSS class condition + * @param FieldMetadata $fields_meta set of field properties + * @param string $comments the comment for the column * * @return string html content * @@ -2099,7 +2092,7 @@ class Results $col_visib, $col_visib_j, $condition_field, - $fields_meta, + FieldMetadata $fields_meta, $comments ) { $draggable_html = 'addClass($class, $conditionField, $meta, ''); return $this->template->render('display/results/null_display', [ 'align' => $align, - 'meta' => $meta, + 'data_decimals' => $meta->decimals ?? -1, + 'data_type' => $meta->getMappedType(), 'classes' => $classes, ]); } @@ -2243,16 +2237,16 @@ class Results * getDataCellForGeometryColumns(), * getDataCellForNonNumericColumns() * - * @param string $class class of table cell - * @param bool $conditionField whether to add CSS class condition - * @param stdClass $meta the meta-information about this field - * @param string $align cell alignment + * @param string $class class of table cell + * @param bool $conditionField whether to add CSS class condition + * @param FieldMetadata $meta the meta-information about this field + * @param string $align cell alignment * * @return string the td * * @access private */ - private function buildEmptyDisplay($class, $conditionField, $meta, $align = '') + private function buildEmptyDisplay($class, $conditionField, FieldMetadata $meta, $align = '') { $classes = $this->addClass($class, $conditionField, $meta, 'text-nowrap'); @@ -2270,7 +2264,7 @@ class Results * @param string $class class of table cell * @param bool $condition_field whether to add CSS class * condition - * @param stdClass $meta the meta-information about the + * @param FieldMetadata $meta the meta-information about the * field * @param string $nowrap avoid wrapping * @param bool $is_field_truncated is field truncated (display ...) @@ -2286,7 +2280,7 @@ class Results private function addClass( $class, $condition_field, - $meta, + FieldMetadata $meta, $nowrap, $is_field_truncated = false, $transformation_plugin = '', @@ -2297,8 +2291,8 @@ class Results $nowrap, ]; - if (isset($meta->mimetype)) { - $classes[] = preg_replace('/\//', '_', $meta->mimetype); + if (isset($meta->internalMediaType)) { + $classes[] = preg_replace('/\//', '_', $meta->internalMediaType); } if ($condition_field) { @@ -2319,24 +2313,23 @@ class Results } // Define classes to be added to this data field based on the type of data - $matches = [ - 'enum' => 'enum', - 'set' => 'set', - 'binary' => 'hex', - ]; - foreach ($matches as $key => $value) { - if (mb_strpos($meta->flags, $key) === false) { - continue; - } - - $classes[] = $value; + if ($meta->isEnum()) { + $classes[] = 'enum'; } - if (mb_strpos($meta->type, 'bit') !== false) { + if ($meta->isSet()) { + $classes[] = 'set'; + } + + if ($meta->isMappedTypeBit) { $classes[] = 'bit'; } + if ($meta->isBinary()) { + $classes[] = 'hex'; + } + return implode(' ', $classes); } @@ -2607,6 +2600,7 @@ class Results */ private function setMimeMap() { + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $mimeMap = []; $added = []; @@ -2710,6 +2704,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in foreach $sql_query = $this->properties['sql_query']; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $highlight_columns = $this->properties['highlight_columns']; $mime_map = $this->properties['mime_map']; @@ -2731,7 +2726,7 @@ class Results $orgFullColName = $this->properties['db'] . '.' . $meta->orgtable . '.' . $meta->orgname; - $not_null_class = $meta->not_null ? 'not_null' : ''; + $not_null_class = $meta->isNotNull() ? 'not_null' : ''; $relation_class = isset($map[$meta->name]) ? 'relation' : ''; $hide_class = is_array($col_visib) && isset($col_visib[$currentColumn]) && ! $col_visib[$currentColumn] ? 'hide' @@ -2740,7 +2735,7 @@ class Results // handle datetime-related class, for grid editing $field_type_class - = $this->getClassForDateTimeRelatedFields($meta->type); + = $this->getClassForDateTimeRelatedFields($meta); $is_field_truncated = false; // combine all the classes applicable to this column's value @@ -2788,7 +2783,7 @@ class Results $mime_map[$orgFullColName]['transformation_options'] ?? '' ); - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', $mime_map[$orgFullColName]['mimetype'] @@ -2818,7 +2813,7 @@ class Results $orgTable = mb_strtolower($meta->orgtable); $orgName = mb_strtolower($meta->orgname); - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', $this->transformationInfo[$dbLower][$orgTable][$orgName][2] @@ -2840,7 +2835,7 @@ class Results 2 => true, ]; - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', 'Text/Plain' @@ -2896,7 +2891,8 @@ class Results // even for a string type // for decimal numeric is returning 1 // have to improve logic - if (($meta->numeric == 1 && $meta->type !== 'string') || $meta->type === 'real') { + if (($meta->isNumeric && $meta->isNotType(FieldMetadata::TYPE_STRING)) + || $meta->isType(FieldMetadata::TYPE_REAL)) { // n u m e r i c $display_params['data'][$row_no][$i] @@ -2912,7 +2908,7 @@ class Results $default_function, $transform_options ); - } elseif ($meta->type === self::GEOMETRY_FIELD) { + } elseif ($meta->isMappedTypeGeometry) { // g e o m e t r y // Remove 'grid_edit' from $class as we do not allow to @@ -2984,7 +2980,7 @@ class Results array $specialSchemaLinks, $column_value, array $row_info, - $field_name + string $field_name ) { $linking_url_params = []; $db = mb_strtolower($this->properties['db']); @@ -3042,6 +3038,7 @@ class Results private function getRowInfoForSpecialLinks(array $row, $col_order) { $row_info = []; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; for ($n = 0; $n < $this->properties['fields_cnt']; ++$n) { @@ -3361,25 +3358,25 @@ class Results * * @see getTableBody() * - * @param string $grid_edit_class the class for all editable columns - * @param string $not_null_class the class for not null columns - * @param string $relation_class the class for relations in a column - * @param string $hide_class the class for visibility of a column - * @param string $field_type_class the class related to type of the field + * @param string $gridEditClass the class for all editable columns + * @param string $notNullClass the class for not null columns + * @param string $relationClass the class for relations in a column + * @param string $hideClass the class for visibility of a column + * @param string $fieldTypeClass the class related to type of the field * * @return string the combined classes * * @access private */ private function getClassesForColumn( - $grid_edit_class, - $not_null_class, - $relation_class, - $hide_class, - $field_type_class + string $gridEditClass, + string $notNullClass, + string $relationClass, + string $hideClass, + string $fieldTypeClass ) { - return 'data ' . $grid_edit_class . ' ' . $not_null_class . ' ' - . $relation_class . ' ' . $hide_class . ' ' . $field_type_class; + return 'data ' . $gridEditClass . ' ' . $notNullClass . ' ' + . $relationClass . ' ' . $hideClass . ' ' . $fieldTypeClass; } /** @@ -3387,29 +3384,29 @@ class Results * * @see getTableBody() * - * @param string $type the type of the column field + * @param FieldMetadata $meta the type of the column field * * @return string the class for the column * * @access private */ - private function getClassForDateTimeRelatedFields($type) + private function getClassForDateTimeRelatedFields(FieldMetadata $meta): string { - if ((substr($type, 0, 9) === self::TIMESTAMP_FIELD) - || ($type === self::DATETIME_FIELD) + $fieldTypeClass = ''; + + if ($meta->isMappedTypeTimestamp + || $meta->isType(FieldMetadata::TYPE_DATETIME) ) { - $field_type_class = 'datetimefield'; - } elseif ($type === self::DATE_FIELD) { - $field_type_class = 'datefield'; - } elseif ($type === self::TIME_FIELD) { - $field_type_class = 'timefield'; - } elseif ($type === self::STRING_FIELD) { - $field_type_class = 'text'; - } else { - $field_type_class = ''; + $fieldTypeClass = 'datetimefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_DATE)) { + $fieldTypeClass = 'datefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_TIME)) { + $fieldTypeClass = 'timefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_STRING)) { + $fieldTypeClass = 'text'; } - return $field_type_class; + return $fieldTypeClass; } /** @@ -3421,8 +3418,8 @@ class Results * @param string $class the html class for column * @param bool $condition_field the column should highlighted * or not - * @param stdClass $meta the meta-information about this - * field + * @param FieldMetadata $meta the meta-information about this + * field * @param array $map the list of relations * @param bool $is_field_truncated the condition for blob data * replacements @@ -3440,7 +3437,7 @@ class Results ?string $column, $class, $condition_field, - $meta, + FieldMetadata $meta, array $map, $is_field_truncated, array $analyzed_sql_results, @@ -3494,8 +3491,8 @@ class Results * * @param string|null $column the relevant column in data row * @param string $class the html class for column - * @param stdClass $meta the meta-information about - * this field + * @param FieldMetadata $meta the meta-information about + * this 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 @@ -3514,7 +3511,7 @@ class Results private function getDataCellForGeometryColumns( ?string $column, $class, - $meta, + FieldMetadata $meta, array $map, array $_url_params, $condition_field, @@ -3534,7 +3531,7 @@ class Results // Display as [GEOMETRY - (size)] if ($_SESSION['tmpval']['geoOption'] === self::GEOMETRY_DISP_GEOM) { $geometry_text = $this->handleNonPrintableContents( - strtoupper(self::GEOMETRY_FIELD), + 'GEOMETRY', $column, $transformation_plugin, $transform_options, @@ -3611,7 +3608,7 @@ class Results } $wkbval = $this->handleNonPrintableContents( - self::BINARY_FIELD, + 'BINARY', $column, $transformation_plugin, $transform_options, @@ -3634,8 +3631,8 @@ class Results * * @param string|null $column the relevant column in data row * @param string $class the html class for column - * @param stdClass $meta the meta-information about - * the field + * @param FieldMetadata $meta the meta-information about + * the field * @param array $map the list of relations * @param array $_url_params the parameters for generate * url @@ -3661,7 +3658,7 @@ class Results private function getDataCellForNonNumericColumns( ?string $column, $class, - $meta, + FieldMetadata $meta, array $map, array $_url_params, $condition_field, @@ -3678,7 +3675,6 @@ class Results $original_length = 0; $is_analyse = $this->properties['is_analyse']; - $field_flags = $dbi->fieldFlags($dt_result, $col_index); $bIsText = is_object($transformation_plugin) && strpos($transformation_plugin->getMIMEType(), 'Text') @@ -3688,13 +3684,15 @@ class Results // if binary fields are protected // or transformation plugin is of non text type // such as image - if ((stripos($field_flags, self::BINARY_FIELD) !== false - && ($GLOBALS['cfg']['ProtectBinary'] === 'all' - || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob' - && stripos($meta->type, self::BLOB_FIELD) === false) - || ($GLOBALS['cfg']['ProtectBinary'] === 'blob' - && stripos($meta->type, self::BLOB_FIELD) !== false))) - || $bIsText + $isTypeBlob = $meta->isType(FieldMetadata::TYPE_BLOB); + $cfgProtectBinary = $GLOBALS['cfg']['ProtectBinary']; + if (($meta->isBinary() + && ( + $cfgProtectBinary === 'all' + || ($cfgProtectBinary === 'noblob' && ! $isTypeBlob) + || ($cfgProtectBinary === 'blob' && $isTypeBlob) + ) + ) || $bIsText ) { $class = str_replace('grid_edit', '', $class); } @@ -3712,7 +3710,7 @@ class Results $displayedColumn = $column; if (! (is_object($transformation_plugin) && strpos($transformation_plugin->getName(), 'Link') !== false) - && stripos($field_flags, self::BINARY_FIELD) === false + && ! $meta->isBinary() ) { [ $is_field_truncated, @@ -3722,7 +3720,7 @@ class Results } $formatted = false; - if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) { + if ($meta->isMappedTypeBit) { $displayedColumn = Util::printableBitValue( (int) $displayedColumn, (int) $meta->length @@ -3731,14 +3729,14 @@ class Results // some results of PROCEDURE ANALYSE() are reported as // being BINARY but they are quite readable, // so don't treat them as BINARY - } elseif (stripos($field_flags, self::BINARY_FIELD) !== false + } elseif ($meta->isBinary() && ! (isset($is_analyse) && $is_analyse) ) { // we show the BINARY or BLOB message and field's size // (or maybe use a transformation) - $binary_or_blob = self::BLOB_FIELD; - if ($meta->type === self::STRING_FIELD) { - $binary_or_blob = self::BINARY_FIELD; + $binary_or_blob = 'BLOB'; + if ($meta->isType(FieldMetadata::TYPE_STRING)) { + $binary_or_blob = 'BINARY'; } $displayedColumn = $this->handleNonPrintableContents( $binary_or_blob, @@ -3786,8 +3784,7 @@ class Results // do not wrap if date field type or if no-wrapping enabled by transform functions // otherwise, preserve whitespaces and wrap - $nowrap = preg_match('@DATE|TIME@i', $meta->type) - || $bool_nowrap ? 'text-nowrap' : 'pre_wrap'; + $nowrap = $meta->isDateTimeType() || $bool_nowrap ? 'text-nowrap' : 'pre_wrap'; $where_comparison = ' = \'' . $dbi->escapeString($column) @@ -3999,6 +3996,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in foreach + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $showtable = $this->properties['showtable']; $printview = $this->properties['printview']; @@ -4263,6 +4261,7 @@ class Results ) { global $dbi; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; // To use array indexes if (empty($sort_expression_nodirection)) { @@ -4309,11 +4308,12 @@ class Results // check for non printable sorted row data $meta = $fields_meta[$sorted_column_index]; - if (stripos($meta->type, self::BLOB_FIELD) !== false - || ($meta->type === self::GEOMETRY_FIELD) - ) { + $isBlobOrGeometry = $meta->isType(FieldMetadata::TYPE_BLOB) + || $meta->isMappedTypeGeometry; + + if ($isBlobOrGeometry) { $column_for_first_row = $this->handleNonPrintableContents( - $meta->type, + $meta->getMappedType(), $row[$sorted_column_index], $transformation_plugin, $transform_options, @@ -4341,11 +4341,9 @@ class Results // check for non printable sorted row data $meta = $fields_meta[$sorted_column_index]; - if (stripos($meta->type, self::BLOB_FIELD) !== false - || ($meta->type === self::GEOMETRY_FIELD) - ) { + if ($isBlobOrGeometry) { $column_for_last_row = $this->handleNonPrintableContents( - $meta->type, + $meta->getMappedType(), $row[$sorted_column_index], $transformation_plugin, $transform_options, @@ -4671,9 +4669,10 @@ class Results } } + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; foreach ($fields_meta as $meta) { - if ($meta->type === self::GEOMETRY_FIELD) { + if ($meta->isMappedTypeGeometry) { $geometry_found = true; break; } @@ -4695,18 +4694,18 @@ class Results * * @see getDataCellForGeometryColumns(), getDataCellForNonNumericColumns(), getSortedColumnMessage() * - * @param string $category BLOB|BINARY|GEOMETRY - * @param string|null $content the binary content - * @param mixed $transformation_plugin transformation plugin. - * Can also be the - * default function: - * Core::mimeDefaultFunction - * @param string $transform_options transformation parameters - * @param string $default_function default transformation function - * @param stdClass $meta the meta-information about the field - * @param array $url_params parameters that should go to the - * download link - * @param bool $is_truncated the result is truncated or not + * @param string $category BLOB|BINARY|GEOMETRY + * @param string|null $content the binary content + * @param mixed $transformation_plugin transformation plugin. + * Can also be the + * default function: + * Core::mimeDefaultFunction + * @param string $transform_options transformation parameters + * @param string $default_function default transformation function + * @param FieldMetadata $meta the meta-information about the field + * @param array $url_params parameters that should go to the + * download link + * @param bool $is_truncated the result is truncated or not * * @return mixed string or float * @@ -4718,7 +4717,7 @@ class Results $transformation_plugin, $transform_options, $default_function, - $meta, + FieldMetadata $meta, array $url_params = [], &$is_truncated = null ) { @@ -4769,9 +4768,9 @@ class Results $result = $default_function($result, [], $meta); if (($_SESSION['tmpval']['display_binary'] - && $meta->type === self::STRING_FIELD) + && $meta->isType(FieldMetadata::TYPE_STRING)) || ($_SESSION['tmpval']['display_blob'] - && stripos($meta->type, self::BLOB_FIELD) !== false) + && $meta->isType(FieldMetadata::TYPE_BLOB)) ) { // in this case, restart from the original $content if (mb_check_encoding($content, 'utf-8') @@ -4809,15 +4808,15 @@ class Results /** * Retrieves the associated foreign key info for a data cell * - * @param array $map the list of relations - * @param stdClass $meta the meta-information about the field - * @param string $where_comparison data for the where clause + * @param array $map the list of relations + * @param FieldMetadata $meta the meta-information about the field + * @param string $where_comparison data for the where clause * * @return string|null formatted data * * @access private */ - private function getFromForeign(array $map, $meta, $where_comparison) + private function getFromForeign(array $map, FieldMetadata $meta, $where_comparison) { global $dbi; @@ -4859,8 +4858,8 @@ class Results * @param bool $condition_field whether the column is a part of * the where clause * @param array $analyzed_sql_results the analyzed query - * @param stdClass $meta the meta-information about the - * field + * @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) @@ -4883,7 +4882,7 @@ class Results $class, $condition_field, array $analyzed_sql_results, - $meta, + FieldMetadata $meta, array $map, $data, $displayedData, @@ -4899,7 +4898,7 @@ class Results $printview = $this->properties['printview']; $decimals = $meta->decimals ?? '-1'; $result = ' | isMultipleKey
+ = (bool) ($fieldFlags & MYSQLI_MULTIPLE_KEY_FLAG);
+ $this->isPrimaryKey
+ = (bool) ($fieldFlags & MYSQLI_PRI_KEY_FLAG);
+ $this->isUniqueKey
+ = (bool) ($fieldFlags & MYSQLI_UNIQUE_KEY_FLAG);
+ $this->isNotNull
+ = (bool) ($fieldFlags & MYSQLI_NOT_NULL_FLAG);
+ $this->isUnsigned
+ = (bool) ($fieldFlags & MYSQLI_UNSIGNED_FLAG);
+ $this->isZerofill
+ = (bool) ($fieldFlags & MYSQLI_ZEROFILL_FLAG);
+ $this->isNumeric
+ = (bool) ($fieldFlags & MYSQLI_NUM_FLAG);
+ $this->isBlob
+ = (bool) ($fieldFlags & MYSQLI_BLOB_FLAG);
+ $this->isEnum
+ = (bool) ($fieldFlags & MYSQLI_ENUM_FLAG);
+ $this->isSet
+ = (bool) ($fieldFlags & MYSQLI_SET_FLAG);
+
+ /*
+ MYSQLI_PART_KEY_FLAG => 'part_key',
+ MYSQLI_TIMESTAMP_FLAG => 'timestamp',
+ MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment',
+ */
+
+ $this->mappedType = $this->getTypeMap()[$fieldType] ?? null;
+
+ $this->isMappedTypeBit = $this->isType(self::TYPE_BIT);
+ $this->isMappedTypeGeometry = $this->isType(self::TYPE_GEOMETRY);
+ $this->isMappedTypeTimestamp = $this->isType(self::TYPE_TIMESTAMP);
+
+ $this->name = property_exists($field, 'name') ? $field->name : '';
+ $this->orgname = property_exists($field, 'orgname') ? $field->orgname : '';
+ $this->table = property_exists($field, 'table') ? $field->table : '';
+ $this->orgtable = property_exists($field, 'orgtable') ? $field->orgtable : '';
+ $this->charsetnr = property_exists($field, 'charsetnr') ? $field->charsetnr : -1;
+ $this->decimals = property_exists($field, 'decimals') ? $field->decimals : 0;
+ $this->length = property_exists($field, 'length') ? $field->length : 0;
+
+ // 63 is the number for the MySQL charset "binary"
+ $this->isBinary = (
+ $fieldType === MYSQLI_TYPE_TINY_BLOB || $fieldType === MYSQLI_TYPE_BLOB
+ || $fieldType === MYSQLI_TYPE_MEDIUM_BLOB || $fieldType === MYSQLI_TYPE_LONG_BLOB
+ || $fieldType === MYSQLI_TYPE_VAR_STRING || $fieldType === MYSQLI_TYPE_STRING
+ ) && $this->charsetnr == 63;
+ }
+
+ /**
+ * @see https://dev.mysql.com/doc/connectors/en/apis-php-mysqli.constants.html
+ */
+ private function getTypeMap(): array
+ {
+ // Issue #16043 - client API mysqlnd seem not to have MYSQLI_TYPE_JSON defined
+ if (! defined('MYSQLI_TYPE_JSON')) {
+ define('MYSQLI_TYPE_JSON', 245);
+ }
+ // Build an associative array for a type look up
+ $typeAr = [];
+ $typeAr[MYSQLI_TYPE_DECIMAL] = self::TYPE_REAL;
+ $typeAr[MYSQLI_TYPE_NEWDECIMAL] = self::TYPE_REAL;
+ $typeAr[MYSQLI_TYPE_BIT] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_TINY] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_SHORT] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_LONG] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_FLOAT] = self::TYPE_REAL;
+ $typeAr[MYSQLI_TYPE_DOUBLE] = self::TYPE_REAL;
+ $typeAr[MYSQLI_TYPE_NULL] = self::TYPE_NULL;
+ $typeAr[MYSQLI_TYPE_TIMESTAMP] = self::TYPE_TIMESTAMP;
+ $typeAr[MYSQLI_TYPE_LONGLONG] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_INT24] = self::TYPE_INT;
+ $typeAr[MYSQLI_TYPE_DATE] = self::TYPE_DATE;
+ $typeAr[MYSQLI_TYPE_TIME] = self::TYPE_TIME;
+ $typeAr[MYSQLI_TYPE_DATETIME] = self::TYPE_DATETIME;
+ $typeAr[MYSQLI_TYPE_YEAR] = self::TYPE_YEAR;
+ $typeAr[MYSQLI_TYPE_NEWDATE] = self::TYPE_DATE;
+ $typeAr[MYSQLI_TYPE_ENUM] = self::TYPE_UNKNOWN;
+ $typeAr[MYSQLI_TYPE_SET] = self::TYPE_UNKNOWN;
+ $typeAr[MYSQLI_TYPE_TINY_BLOB] = self::TYPE_BLOB;
+ $typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = self::TYPE_BLOB;
+ $typeAr[MYSQLI_TYPE_LONG_BLOB] = self::TYPE_BLOB;
+ $typeAr[MYSQLI_TYPE_BLOB] = self::TYPE_BLOB;
+ $typeAr[MYSQLI_TYPE_VAR_STRING] = self::TYPE_STRING;
+ $typeAr[MYSQLI_TYPE_STRING] = self::TYPE_STRING;
+ // MySQL returns MYSQLI_TYPE_STRING for CHAR
+ // and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY
+ // so this would override TINYINT and mark all TINYINT as string
+ // see https://github.com/phpmyadmin/phpmyadmin/issues/8569
+ //$typeAr[MYSQLI_TYPE_CHAR] = self::TYPE_STRING;
+ $typeAr[MYSQLI_TYPE_GEOMETRY] = self::TYPE_GEOMETRY;
+ $typeAr[MYSQLI_TYPE_BIT] = self::TYPE_BIT;
+ $typeAr[MYSQLI_TYPE_JSON] = self::TYPE_JSON;
+
+ return $typeAr;
+ }
+
+ public function isNotNull(): bool
+ {
+ return $this->isNotNull;
+ }
+
+ public function isNumeric(): bool
+ {
+ return $this->isNumeric;
+ }
+
+ public function isBinary(): bool
+ {
+ return $this->isBinary;
+ }
+
+ public function isBlob(): bool
+ {
+ return $this->isBlob;
+ }
+
+ public function isPrimaryKey(): bool
+ {
+ return $this->isPrimaryKey;
+ }
+
+ public function isUniqueKey(): bool
+ {
+ return $this->isUniqueKey;
+ }
+
+ public function isMultipleKey(): bool
+ {
+ return $this->isMultipleKey;
+ }
+
+ public function isUnsigned(): bool
+ {
+ return $this->isUnsigned;
+ }
+
+ public function isZerofill(): bool
+ {
+ return $this->isZerofill;
+ }
+
+ public function isEnum(): bool
+ {
+ return $this->isEnum;
+ }
+
+ public function isSet(): bool
+ {
+ return $this->isSet;
+ }
+
+ /**
+ * Checks that it is type INT or type REAL
+ */
+ public function isNumericType(): bool
+ {
+ return $this->isType(self::TYPE_INT) || $this->isType(self::TYPE_REAL);
+ }
+
+ /**
+ * Checks that it is type DATE/TIME/DATETIME
+ */
+ public function isDateTimeType(): bool
+ {
+ return $this->isType(self::TYPE_DATE)
+ || $this->isType(self::TYPE_TIME)
+ || $this->isType(self::TYPE_DATETIME);
+ }
+
+ /**
+ * Checks that it contains time
+ * A "DATE" field returns false for example
+ */
+ public function isTimeType(): bool
+ {
+ return $this->isType(self::TYPE_TIME)
+ || $this->isType(self::TYPE_TIMESTAMP)
+ || $this->isType(self::TYPE_DATETIME);
+ }
+
+ /**
+ * Get the mapped type as a string
+ *
+ * @return string Empty when nothing could be matched
+ */
+ public function getMappedType(): string
+ {
+ $types = [
+ self::TYPE_GEOMETRY => 'geometry',
+ self::TYPE_BIT => 'bit',
+ self::TYPE_JSON => 'json',
+ self::TYPE_REAL => 'real',
+ self::TYPE_INT => 'int',
+ self::TYPE_BLOB => 'blob',
+ self::TYPE_UNKNOWN => 'unknown',
+ self::TYPE_NULL => 'null',
+ self::TYPE_STRING => 'string',
+ self::TYPE_DATE => 'date',
+ self::TYPE_TIME => 'time',
+ self::TYPE_TIMESTAMP => 'timestamp',
+ self::TYPE_DATETIME => 'datetime',
+ self::TYPE_YEAR => 'year',
+ ];
+
+ return $types[$this->mappedType] ?? '';
+ }
+
+ /**
+ * Check if it is the mapped type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function isType(int $type): bool
+ {
+ return $this->mappedType === $type;
+ }
+
+ /**
+ * Check if it is NOT the mapped type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function isNotType(int $type): bool
+ {
+ return $this->mappedType !== $type;
+ }
+}
diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php
index ddb6e6d200..a9e63ec667 100644
--- a/libraries/classes/InsertEdit.php
+++ b/libraries/classes/InsertEdit.php
@@ -231,7 +231,7 @@ class InsertEdit
* exit if we want the message to be displayed
*/
} else {// end if (no row returned)
- $meta = $this->dbi->getFieldsMeta($result[$key_id]);
+ $meta = $this->dbi->getFieldsMeta($result[$key_id]) ?? [];
[$unique_condition, $tmp_clause_is_unique] = Util::getUniqueCondition(
$result[$key_id],
@@ -2265,7 +2265,7 @@ class InsertEdit
$res = $this->dbi->query($local_query);
$row = $this->dbi->fetchRow($res);
- $meta = $this->dbi->getFieldsMeta($res);
+ $meta = $this->dbi->getFieldsMeta($res) ?? [];
// must find a unique condition based on unique key,
// not a combination of all fields
[$unique_condition, $clause_is_unique] = Util::getUniqueCondition(
@@ -2954,18 +2954,16 @@ class InsertEdit
. ' WHERE ' . $_POST['where_clause'][0];
$result = $this->dbi->tryQuery($sql_for_real_value);
- $fields_meta = $this->dbi->getFieldsMeta($result);
+ $fields_meta = $this->dbi->getFieldsMeta($result) ?? [];
+ /** @var FieldMetadata $meta */
$meta = $fields_meta[0];
$row = $this->dbi->fetchRow($result);
if ($row) {
$new_value = $row[0];
- if ((substr($meta->type, 0, 9) === 'timestamp')
- || ($meta->type === 'datetime')
- || ($meta->type === 'time')
- ) {
+ if ($meta->isTimeType()) {
$new_value = Util::addMicroseconds($new_value);
- } elseif (mb_strpos($meta->flags, 'binary') !== false) {
+ } elseif ($meta->isBinary()) {
$new_value = '0x' . bin2hex($new_value);
}
$extra_data['isNeedToRecheck'] = true;
diff --git a/libraries/classes/Plugins/Export/ExportJson.php b/libraries/classes/Plugins/Export/ExportJson.php
index 8dec8a0eb9..5f5efea00a 100644
--- a/libraries/classes/Plugins/Export/ExportJson.php
+++ b/libraries/classes/Plugins/Export/ExportJson.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
@@ -20,7 +21,6 @@ use function bin2hex;
use function explode;
use function json_encode;
use function stripslashes;
-use function strpos;
/**
* Handles the export for the JSON format
@@ -210,6 +210,7 @@ class ExportJson extends ExportPlugin
$sql_query,
array $aliases = []
) {
+ /** @var DatabaseInterface $dbi */
global $dbi;
$db_alias = $db;
@@ -244,7 +245,7 @@ class ExportJson extends ExportPlugin
DatabaseInterface::QUERY_UNBUFFERED
);
$columns_cnt = $dbi->numFields($result);
- $fieldsMeta = $dbi->getFieldsMeta($result);
+ $fieldsMeta = $dbi->getFieldsMeta($result) ?? [];
$columns = [];
for ($i = 0; $i < $columns_cnt; $i++) {
@@ -269,7 +270,8 @@ class ExportJson extends ExportPlugin
$data = [];
for ($i = 0; $i < $columns_cnt; $i++) {
- if ($fieldsMeta[$i]->type === 'geometry' || strpos($fieldsMeta[$i]->type, 'blob') !== false) {
+ if ($fieldsMeta[$i]->isMappedTypeGeometry
+ || $fieldsMeta[$i]->isType(FieldMetadata::TYPE_BLOB)) {
// export GIS and blob types as hex
$record[$i] = '0x' . bin2hex($record[$i]);
}
diff --git a/libraries/classes/Plugins/Export/ExportOds.php b/libraries/classes/Plugins/Export/ExportOds.php
index f5d461ac57..91dcc9a2d6 100644
--- a/libraries/classes/Plugins/Export/ExportOds.php
+++ b/libraries/classes/Plugins/Export/ExportOds.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
namespace PhpMyAdmin\Plugins\Export;
use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\OpenDocument;
use PhpMyAdmin\Plugins\ExportPlugin;
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
@@ -19,7 +20,6 @@ use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
use function bin2hex;
use function date;
use function htmlspecialchars;
-use function stripos;
use function stripslashes;
use function strtotime;
@@ -215,6 +215,7 @@ class ExportOds extends ExportPlugin
$sql_query,
array $aliases = []
) {
+ /** @var DatabaseInterface $dbi */
global $what, $dbi;
$db_alias = $db;
@@ -227,11 +228,8 @@ class ExportOds extends ExportPlugin
DatabaseInterface::QUERY_UNBUFFERED
);
$fields_cnt = $dbi->numFields($result);
+ /** @var FieldMetadata[] $fields_meta */
$fields_meta = $dbi->getFieldsMeta($result);
- $field_flags = [];
- for ($j = 0; $j < $fields_cnt; $j++) {
- $field_flags[$j] = $dbi->fieldFlags($result, $j);
- }
$GLOBALS['ods_buffer']
.= ' |
|---|