MDL-74656 reportbuilder: ensure nulls preserved in column callbacks.

When concatenating column fields in order to perform aggregation on
them (e.g. group concatenation), we need to preserve all null values
in the data passed to each column callback.

Co-authored-by: Carlos Castillo <[email protected]>
This commit is contained in:
Paul Holden
2022-07-19 08:42:30 +01:00
co-authored by Carlos Castillo
parent c1910fc822
commit 39d606ec27
2 changed files with 17 additions and 5 deletions
@@ -81,9 +81,14 @@ abstract class base {
*
* @param string[] $sqlfields
* @param string $delimeter
* @param string $coalescechar
* @return string
*/
final protected static function get_column_fields_concat(array $sqlfields, string $delimeter = ','): string {
final protected static function get_column_fields_concat(
array $sqlfields,
string $delimeter = ',',
string $coalescechar = ' '
): string {
global $DB;
$concatfields = [];
@@ -99,8 +104,8 @@ abstract class base {
break;
}
// Coalesce all the SQL fields, to remove all nulls.
$concatfields[] = "COALESCE({$sqlfield}, ' ')";
// Coalesce all the SQL fields. Ensure cross-DB compatibility, and that we always get string data back.
$concatfields[] = "COALESCE({$sqlfield}, '{$coalescechar}')";
$concatfields[] = "'{$delimeter}'";
}
@@ -34,6 +34,9 @@ class groupconcat extends base {
/** @var string Character to use as a delimeter between column fields */
protected const COLUMN_FIELD_DELIMETER = '<|>';
/** @var string Character to use a null coalesce value */
protected const COLUMN_NULL_COALESCE = '<^>';
/** @var string Character to use as a delimeter between field values */
protected const FIELD_VALUE_DELIMETER = '<,>';
@@ -79,7 +82,7 @@ class groupconcat extends base {
return parent::get_column_field_sql($sqlfields);
}
return self::get_column_fields_concat($sqlfields, self::COLUMN_FIELD_DELIMETER);
return self::get_column_fields_concat($sqlfields, self::COLUMN_FIELD_DELIMETER, self::COLUMN_NULL_COALESCE);
}
/**
@@ -123,7 +126,11 @@ class groupconcat extends base {
continue;
}
$originalvalue = array_combine($valuenames, $valuedata);
// Re-construct original values, also ensuring any nulls contained within are restored.
$originalvalue = array_map(static function(string $value): ?string {
return $value === self::COLUMN_NULL_COALESCE ? null : $value;
}, array_combine($valuenames, $valuedata));
$originalfirstvalue = reset($originalvalue);
// Once we've re-constructed each value, we can apply callbacks to it.