This is an automated email from the ASF dual-hosted git repository.

xiangfu0 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 4e12c0f68b8 Fix query console blank page when result contains a MAP 
(complex) column (#18783)
4e12c0f68b8 is described below

commit 4e12c0f68b851937e6ac50bfeefea1568de55a4e
Author: Xiang Fu <[email protected]>
AuthorDate: Tue Jun 16 18:55:30 2026 -0700

    Fix query console blank page when result contains a MAP (complex) column 
(#18783)
    
    * Fix query console blank page when result contains a MAP (complex) column
    
    The query console white-screens whenever a result set includes a MAP-typed
    column. The broker serializes a MAP cell as an object, e.g.
    `{"value":{"k":"v", ...}}`. In Table.tsx, `makeCell` treats any object that
    has a truthy `value` key as an internal "rich cell" (`{value, tooltip,
    component}` where `value` is a status string) and calls
    `styleCell(cellData.value)`. For a MAP column `cellData.value` is itself an
    object, so `styleCell`'s first line `str.toLowerCase()` throws
    `TypeError: str.toLowerCase is not a function` during render. There is no
    error boundary, so the whole results view unmounts and the page goes blank.
    
    Add a single guarded `toDisplayString` helper that coerces any non-string
    cell value to a string before the String methods run, shared by both
    `styleCell` and `makeCell`. It also guards against `JSON.stringify` throwing
    (circular references, BigInt) or returning `undefined` (functions, symbols),
    so the renderer never throws on an unexpected cell value. MAP columns now
    render their contents as JSON text instead of blanking the page.
    
    * Render rich-cell value path for falsy values (0/false/null)
    
    Address review feedback: makeCell only entered the `{value, ...}` cell path
    when `cellData.value` was truthy, so a rich cell with a valid falsy value
    (0, false, null, '') fell through to the fallback and rendered the whole
    object. styleCell now coerces any type safely, so switch the guard to a
    presence check (`has(cellData, 'value')`) and render falsy values correctly.
---
 .../src/main/resources/app/components/Table.tsx    | 41 ++++++++++++++++------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/pinot-controller/src/main/resources/app/components/Table.tsx 
b/pinot-controller/src/main/resources/app/components/Table.tsx
index 829933a8dea..c158da4ec07 100644
--- a/pinot-controller/src/main/resources/app/components/Table.tsx
+++ b/pinot-controller/src/main/resources/app/components/Table.tsx
@@ -89,6 +89,26 @@ staticSortFunctions.set("Estimated Size", sortBytes);
 staticSortFunctions.set("Reported Size", sortBytes);
 staticSortFunctions.set("Status", sortCellValue);
 
+// Safely coerce an arbitrary cell value to a string for display. Most cells 
are strings, but
+// complex column types (e.g. MAP, which the broker serializes as an object) 
arrive as non-strings.
+// JSON.stringify can itself throw (circular references, BigInt) or return 
undefined (functions,
+// symbols), so both are guarded — the results view has no error boundary, and 
any throw here
+// white-screens the whole page.
+const toDisplayString = (value: any): string => {
+  if (typeof value === 'string') {
+    return value;
+  }
+  if (value === null || value === undefined) {
+    return String(value);
+  }
+  try {
+    const json = JSON.stringify(value);
+    return json === undefined ? String(value) : json;
+  } catch (e) {
+    return '<DATA COULD NOT BE PARSED TO DISPLAY>';
+  }
+};
+
 const StyledTableRow = withStyles((theme) =>
   createStyles({
     root: {
@@ -414,7 +434,10 @@ export default function CustomizedTables({
     };
   }, [search, timeoutId, filterSearchResults]);
 
-  const styleCell = (str: string) => {
+  const styleCell = (cellValue: any) => {
+    // Coerce to a string first so the String methods below 
(toLowerCase/search/replace) never
+    // throw on a non-string cell value (e.g. a MAP column) and crash the 
whole results view.
+    const str = toDisplayString(cellValue);
     if (str.toLowerCase() === 'good' || str.toLowerCase() === 'healthy' || 
str.toLowerCase() === 'online' || str.toLowerCase() === 'alive' || 
str.toLowerCase() === 'true') {
           return (
             <StyledChip
@@ -533,17 +556,15 @@ export default function CustomizedTables({
               {styleCell(cellData.value)}
             </Tooltip>
         );
-      } else if(has(cellData, 'value') && cellData.value) {
+      } else if (has(cellData, 'value')) {
+        // Render via the value path whenever the key is present, including 
falsy values
+        // (0, false, null, ''). styleCell safely coerces any type, so we no 
longer fall
+        // through to dumping the whole {value: ...} object for a falsy value.
         return styleCell(cellData.value);
       } else {
-          try {
-            const stringifiedJSON = JSON.stringify(cellData)
-            return stringifiedJSON
-          } catch(e) {
-            // If the data is corrupted and not recognizable by 
JSON.stringify, fallback to below error message instead
-            // of crashing the whole page for the user.
-            return '<DATA COULD NOT BE PARSED TO DISPLAY>'
-          }
+          // Fall back to a stringified representation. toDisplayString guards 
against
+          // JSON.stringify throwing (e.g. corrupted/circular data) so we 
never crash the page.
+          return toDisplayString(cellData);
       }
     }
     return styleCell(cellData.toString());


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to