>
*/
private function getResultsOperations(
array $displayParts,
array $analyzed_sql_results
): array {
global $printview, $dbi;
$_url_params = [
'db' => $this->properties['db'],
'table' => $this->properties['table'],
'printview' => '1',
'sql_query' => $this->properties['sql_query'],
];
$geometry_found = false;
// Export link
// (the single_table parameter is used in \PhpMyAdmin\Export->getDisplay()
// to hide the SQL and the structure export dialogs)
// If the parser found a PROCEDURE clause
// (most probably PROCEDURE ANALYSE()) it makes no sense to
// display the Export link).
if (($analyzed_sql_results['querytype'] === self::QUERY_TYPE_SELECT)
&& ! isset($printview)
&& empty($analyzed_sql_results['procedure'])
) {
if (count($analyzed_sql_results['select_tables']) === 1) {
$_url_params['single_table'] = 'true';
}
// In case this query doesn't involve any tables,
// implies only raw query is to be exported
if (! $analyzed_sql_results['select_tables']) {
$_url_params['raw_query'] = 'true';
}
$_url_params['unlim_num_rows'] = $this->properties['unlim_num_rows'];
/**
* At this point we don't know the table name; this can happen
* for example with a query like
* SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp
* As a workaround we set in the table parameter the name of the
* first table of this database, so that /table/export and
* the script it calls do not fail
*/
if (empty($_url_params['table']) && ! empty($_url_params['db'])) {
$_url_params['table'] = $dbi->fetchValue('SHOW TABLES');
/* No result (probably no database selected) */
if ($_url_params['table'] === false) {
unset($_url_params['table']);
}
}
$fields_meta = $this->properties['fields_meta'];
foreach ($fields_meta as $meta) {
if ($meta->type === self::GEOMETRY_FIELD) {
$geometry_found = true;
break;
}
}
}
return [
'has_procedure' => ! empty($analyzed_sql_results['procedure']),
'has_geometry' => $geometry_found,
'has_print_link' => $displayParts['pview_lnk'] == '1',
'has_export_link' => $analyzed_sql_results['querytype'] === self::QUERY_TYPE_SELECT && ! isset($printview),
'url_params' => $_url_params,
];
}
/**
* Verifies what to do with non-printable contents (binary or BLOB)
* in Browse mode.
*
* @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 array $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
*
* @return mixed string or float
*
* @access private
*/
private function handleNonPrintableContents(
$category,
?string $content,
$transformation_plugin,
$transform_options,
$default_function,
$meta,
array $url_params = [],
&$is_truncated = null
) {
$is_truncated = false;
$result = '[' . $category;
if ($content !== null) {
$size = strlen($content);
$display_size = Util::formatByteDown($size, 3, 1);
$result .= ' - ' . $display_size[0] . ' ' . $display_size[1];
} else {
$result .= ' - NULL';
$size = 0;
$content = '';
}
$result .= ']';
// if we want to use a text transformation on a BLOB column
if (is_object($transformation_plugin)) {
$posMimeOctetstream = strpos(
$transformation_plugin->getMIMESubtype(),
'Octetstream'
);
$posMimeText = strpos($transformation_plugin->getMIMEtype(), 'Text');
if ($posMimeOctetstream
|| $posMimeText !== false
) {
// Applying Transformations on hex string of binary data
// seems more appropriate
$result = pack('H*', bin2hex($content));
}
}
if ($size <= 0) {
return $result;
}
if ($default_function != $transformation_plugin) {
$result = $transformation_plugin->applyTransformation(
$result,
$transform_options,
$meta
);
return $result;
}
$result = $default_function($result, [], $meta);
if (($_SESSION['tmpval']['display_binary']
&& $meta->type === self::STRING_FIELD)
|| ($_SESSION['tmpval']['display_blob']
&& stripos($meta->type, self::BLOB_FIELD) !== false)
) {
// in this case, restart from the original $content
if (mb_check_encoding($content, 'utf-8')
&& ! preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', $content)
) {
// show as text if it's valid utf-8
$result = htmlspecialchars($content);
} else {
$result = '0x' . bin2hex($content);
}
[
$is_truncated,
$result,
// skip 3rd param
] = $this->getPartialText($result);
}
/* Create link to download */
// in PHP < 5.5, empty() only checks variables
$tmpdb = $this->properties['db'];
if (count($url_params) > 0
&& (! empty($tmpdb) && ! empty($meta->orgtable))
) {
$url_params['where_clause_sign'] = Core::signSqlQuery($url_params['where_clause']);
$result = ''
. $result . '';
}
return $result;
}
/**
* 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
*
* @return string|null formatted data
*
* @access private
*/
private function getFromForeign(array $map, $meta, $where_comparison)
{
global $dbi;
$dispsql = 'SELECT '
. Util::backquote($map[$meta->name][2])
. ' FROM '
. Util::backquote($map[$meta->name][3])
. '.'
. Util::backquote($map[$meta->name][0])
. ' WHERE '
. Util::backquote($map[$meta->name][1])
. $where_comparison;
$dispresult = $dbi->tryQuery(
$dispsql,
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
if ($dispresult && $dbi->numRows($dispresult) > 0) {
[$dispval] = $dbi->fetchRow($dispresult, 0);
} else {
$dispval = __('Link not found!');
}
$dbi->freeResult($dispresult);
return $dispval;
}
/**
* Prepares the displayable content of a data cell in Browse mode,
* taking into account foreign key description field and transformations
*
* @see getDataCellForNumericColumns(), getDataCellForGeometryColumns(),
* getDataCellForNonNumericColumns(),
*
* @param string $class css classes for the td element
* @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 array $map the list of relations
* @param string $data data
* @param string $displayedData data that will be displayed (maybe be chunked)
* @param TransformationsPlugin $transformation_plugin transformation plugin.
* Can also be the default function:
* Core::mimeDefaultFunction
* @param string $default_function default function
* @param string $nowrap 'nowrap' if the content should
* not be wrapped
* @param string $where_comparison data for the where clause
* @param array $transform_options options for transformation
* @param bool $is_field_truncated whether the field is truncated
* @param string $original_length of a truncated column, or ''
*
* @return string formatted data
*
* @access private
*/
private function getRowData(
$class,
$condition_field,
array $analyzed_sql_results,
$meta,
array $map,
$data,
$displayedData,
$transformation_plugin,
$default_function,
$nowrap,
$where_comparison,
array $transform_options,
$is_field_truncated,
$original_length = ''
) {
$relational_display = $_SESSION['tmpval']['relational_display'];
$printview = $this->properties['printview'];
$decimals = $meta->decimals ?? '-1';
$result = 'addClass(
$class,
$condition_field,
$meta,
$nowrap,
$is_field_truncated,
$transformation_plugin,
$default_function
)
. '">';
if (! empty($analyzed_sql_results['statement']->expr)) {
foreach ($analyzed_sql_results['statement']->expr as $expr) {
if (empty($expr->alias) || empty($expr->column)) {
continue;
}
if (strcasecmp($meta->name, $expr->alias) != 0) {
continue;
}
$meta->name = $expr->column;
}
}
if (isset($map[$meta->name])) {
// Field to display from the foreign table?
if (isset($map[$meta->name][2])
&& strlen((string) $map[$meta->name][2]) > 0
) {
$dispval = $this->getFromForeign(
$map,
$meta,
$where_comparison
);
} else {
$dispval = '';
}
if (isset($printview) && ($printview == '1')) {
$result .= ($transformation_plugin != $default_function
? $transformation_plugin->applyTransformation(
$data,
$transform_options,
$meta
)
: $default_function($data)
)
. ' [->' . $dispval . ']';
} else {
if ($relational_display === self::RELATIONAL_KEY) {
// user chose "relational key" in the display options, so
// the title contains the display field
$title = ! empty($dispval)
? htmlspecialchars($dispval)
: '';
} else {
$title = htmlspecialchars($data);
}
$sqlQuery = 'SELECT * FROM '
. Util::backquote($map[$meta->name][3]) . '.'
. Util::backquote($map[$meta->name][0])
. ' WHERE '
. Util::backquote($map[$meta->name][1])
. $where_comparison;
$_url_params = [
'db' => $map[$meta->name][3],
'table' => $map[$meta->name][0],
'pos' => '0',
'sql_signature' => Core::signSqlQuery($sqlQuery),
'sql_query' => $sqlQuery,
];
if ($transformation_plugin != $default_function) {
// always apply a transformation on the real data,
// not on the display field
$displayedData = $transformation_plugin->applyTransformation(
$data,
$transform_options,
$meta
);
} else {
if ($relational_display === self::RELATIONAL_DISPLAY_COLUMN
&& ! empty($map[$meta->name][2])
) {
// user chose "relational display field" in the
// display options, so show display field in the cell
$displayedData = $dispval === null ? 'NULL' : $default_function($dispval);
} else {
// otherwise display data in the cell
$displayedData = $default_function($displayedData);
}
}
$tag_params = ['title' => $title];
if (strpos($class, 'grid_edit') !== false) {
$tag_params['class'] = 'ajax';
}
$result .= Generator::linkOrButton(
Url::getFromRoute('/sql', $_url_params),
$displayedData,
$tag_params
);
}
} else {
$result .= ($transformation_plugin != $default_function
? $transformation_plugin->applyTransformation(
$data,
$transform_options,
$meta
)
: $default_function($data)
);
}
$result .= ' | ' . "\n";
return $result;
}
/**
* Truncates given string based on LimitChars configuration
* and Session pftext variable
* (string is truncated only if necessary)
*
* @see handleNonPrintableContents(), getDataCellForGeometryColumns(), getDataCellForNonNumericColumns
*
* @param string $str string to be truncated
*
* @return array
*
* @access private
*/
private function getPartialText($str): array
{
$original_length = mb_strlen($str);
if ($original_length > $GLOBALS['cfg']['LimitChars']
&& $_SESSION['tmpval']['pftext'] === self::DISPLAY_PARTIAL_TEXT
) {
$str = mb_substr(
$str,
0,
(int) $GLOBALS['cfg']['LimitChars']
) . '...';
$truncated = true;
} else {
$truncated = false;
}
return [
$truncated,
$str,
$original_length,
];
}
}