Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
9bf2e8c
fix(app): show MATERIALIZED and ALIAS columns in the row details panel
BilalAtique Sep 26, 2026
fcd4774
fix(app): fetch the row without the settings when the connection reje…
BilalAtique Sep 26, 2026
fa630d7
fix(app): honor the source's value for the row query settings
BilalAtique Sep 26, 2026
274cd95
fix(app): fall back only when the connection rejects the row query se…
BilalAtique Sep 26, 2026
43dcfc2
docs(changeset): note the row fallback when the settings are rejected
BilalAtique Sep 26, 2026
80207e7
fix(app): reset the row settings rejection when a connection is edited
BilalAtique Sep 26, 2026
e269745
Merge remote-tracking branch 'upstream/main' into fix/row-panel-mater…
BilalAtique Sep 29, 2026
ddba013
feat(app): make MATERIALIZED and ALIAS columns in the row panel opt-in
BilalAtique Sep 29, 2026
04232a7
fix(app): count only a ClickHouse settings rejection as rejected
BilalAtique Sep 29, 2026
3f14876
fix(app): offer to turn the option off when a row fails to load
BilalAtique Sep 29, 2026
77c7851
fix(app): match the row hint to the settings the query adds
BilalAtique Sep 29, 2026
e59653e
Merge remote-tracking branch 'upstream/main' into fix/row-panel-mater…
BilalAtique Sep 29, 2026
b858fc0
fix(app): report a rejected row setting instead of retrying without it
BilalAtique Sep 29, 2026
958b87b
fix: Hide materialized/alias option when it does nothing
pulpdrew Sep 30, 2026
98917cb
Merge branch 'main' into fix/row-panel-materialized-alias-columns
pulpdrew Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/row-panel-materialized-alias-columns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@hyperdx/app': patch
---

feat: add an option to show MATERIALIZED and ALIAS columns in the row details panel

ClickHouse leaves MATERIALIZED and ALIAS columns out of `SELECT *`, so the row
details panel never showed them. A new "Show materialized and alias columns"
item in the properties view options menu, off by default, adds
`asterisk_include_materialized_columns` and `asterisk_include_alias_columns` to
the row query. It has no effect on a source with a Known Columns List, and a
value that the source's query settings give for either setting wins. If the row
fails to load while the option is on (for example, because the connection's user
is `readonly = 1`, or an ALIAS column cannot be evaluated), the error state
offers to turn the option off.
17 changes: 13 additions & 4 deletions packages/app/src/components/DBRowDataPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import {
import { Box } from '@mantine/core';

import { useQueriedChartConfig } from '@/hooks/useChartConfig';
import {
getKnownColumnsList,
getSelectAllColumnsQuerySettings,
useSelectAllColumnsQuerySettings,
} from '@/hooks/useMaterializedAliasColumnsOption';
import { WithClause } from '@/hooks/useRowWhere';
import {
getDisplayedTimestampValueExpression,
Expand Down Expand Up @@ -90,10 +95,7 @@ export function useRowData({
// target tables declare different column sets. When the source declares a
// "known columns" list (columns known to exist across all target tables) we
// select that instead of `*` when fetching full row data.
const knownColumns =
isLogSource(source) || isTraceSource(source)
? source.knownColumnsListExpression?.trim()
: undefined;
const knownColumns = getKnownColumnsList(source);

const baseConfig = {
source: source.id,
Expand Down Expand Up @@ -210,6 +212,8 @@ export function useRowData({
...(aliasWith && aliasWith.length > 0 ? { with: aliasWith } : {}),
};

const additionalQuerySettings = useSelectAllColumnsQuerySettings(source);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — The option also changes row lookups that have no error state, so a failing ALIAS column blanks the span detail with no way to recover

useRowData applies the settings for every caller, including SpanDetailPanel in components/DBTracePanel.tsx:120 and components/DBRowOverviewPanel.tsx:49. Both read only data and ignore isError. With the option on, an ALIAS column that throws leaves the span detail empty and silent: no error state, and so no "Hide" action. Either render DBRowSidePanelErrorState on error in SpanDetailPanel, or pass the settings only from callers that can show the error (for example, an includeMaterializedAliasColumns argument on useRowData).

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpanDetailPanel and DBRowOverviewPanel already ignore isError for every failure on main, so making them show the error state would change files this PR doesn't touch. The option can still be turned off: from the Column Values menu when the side panel's own row loads, or from the new action in its error state when that row fails too.


const baseQueryKey = ['row_side_panel', rowId, aliasWith, source];
// Both halves of the filter are needed for `renderChartConfig` to emit one, so
// a source with no usable timestamp expression can't be bounded at all.
Expand All @@ -225,6 +229,7 @@ export function useRowData({
{
queryKey: [...baseQueryKey, dateRange],
enabled: rowId != null && hasWindow,
additionalQuerySettings,
},
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
);

Expand All @@ -245,6 +250,7 @@ export function useRowData({
const fallbackResult = useQueriedChartConfig(baseConfig, {
queryKey: [...baseQueryKey, undefined],
enabled: rowId != null && isFallbackActive,
additionalQuerySettings,
});

const queryResult = isFallbackActive ? fallbackResult : boundedResult;
Expand Down Expand Up @@ -379,6 +385,9 @@ export function RowDataPanel({
data={firstRow}
jsonColumns={jsonColumns}
mapColumns={mapColumns}
showMaterializedAliasColumnsOption={
getSelectAllColumnsQuerySettings(source) != null
}
/>
</Box>
</div>
Expand Down
42 changes: 42 additions & 0 deletions packages/app/src/components/DBRowJsonViewer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { fireEvent, screen, within } from '@testing-library/react';

import { useMaterializedAliasColumnsOption } from '@/hooks/useMaterializedAliasColumnsOption';

import { buildJSONExtractQuery, DBRowJsonViewer } from './DBRowJsonViewer';
import { RowSidePanelContext } from './DBRowSidePanel';

Expand Down Expand Up @@ -186,6 +188,46 @@ describe('DBRowJsonViewer', () => {
expect(mockToggleColumn).toHaveBeenCalledWith("LogAttributes['field1']");
});

it('turns the materialized and alias columns option on and off from the menu', async () => {
const OptionValue = () => (
<span data-testid="materialized-alias-option">
{String(useMaterializedAliasColumnsOption()[0])}
</span>
);
const { container } = renderWithMantine(
<RowSidePanelContext value={defaultContext}>
<DBRowJsonViewer data={logData} showMaterializedAliasColumnsOption />
<OptionValue />
</RowSidePanelContext>,
);
const option = screen.getByTestId('materialized-alias-option');
const toggle = async () => {
fireEvent.click(container.querySelector('[aria-haspopup="menu"]')!);
fireEvent.click(
await screen.findByTestId('json-viewer-materialized-alias-toggle'),
);
};

expect(option).toHaveTextContent('false');
await toggle();
expect(option).toHaveTextContent('true');
expect(
localStorage.getItem('hdx-row-show-materialized-alias-columns'),
).toBe('true');
await toggle();
expect(option).toHaveTextContent('false');
});

it('hides the materialized and alias columns option unless asked to show it', async () => {
const { container } = renderComponent(logData);
fireEvent.click(container.querySelector('[aria-haspopup="menu"]')!);

expect(await screen.findByText('Hide blank values')).toBeInTheDocument();
expect(
screen.queryByTestId('json-viewer-materialized-alias-toggle'),
).not.toBeInTheDocument();
});

describe('timestamp fields', () => {
it('displays Timestamp using the same formatter as the results table', () => {
renderComponent({
Expand Down
39 changes: 37 additions & 2 deletions packages/app/src/components/DBRowJsonViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import HyperJson, {
GetLineActions,
LineAction,
} from '@/components/HyperJson';
import { useMaterializedAliasColumnsOption } from '@/hooks/useMaterializedAliasColumnsOption';
import { useFormatTime } from '@/useFormatTime';
import { isColumnInSelect, mergePath } from '@/utils';
import {
Expand Down Expand Up @@ -227,8 +228,16 @@ const viewerOptionsAtom = atomWithStorage<ViewerOptions>(
viewerOptionsStorage,
);

function HyperJsonMenu({ rowData }: { rowData: any }) {
function HyperJsonMenu({
rowData,
showMaterializedAliasColumnsOption,
}: {
rowData: any;
showMaterializedAliasColumnsOption: boolean;
}) {
const [jsonOptions, setJsonOptions] = useAtom(viewerOptionsAtom);
const [showMaterializedAliasColumns, setShowMaterializedAliasColumns] =
useMaterializedAliasColumnsOption();
const effectiveWhiteSpace = jsonOptions.whiteSpace ?? 'pre-wrap';

return (
Expand Down Expand Up @@ -335,6 +344,23 @@ function HyperJsonMenu({ rowData }: { rowData: any }) {
>
Hide blank values
</Menu.Item>
{showMaterializedAliasColumnsOption && (
<Menu.Item
lh="1"
py={8}
data-testid="json-viewer-materialized-alias-toggle"
rightSection={
showMaterializedAliasColumns ? (
<IconCheck size={14} className="ps-2" />
) : null
}
onClick={() =>
setShowMaterializedAliasColumns(!showMaterializedAliasColumns)
}
>
Show materialized and alias columns
</Menu.Item>
)}
</Menu.Dropdown>
</Menu>
</Group>
Expand All @@ -345,13 +371,17 @@ export function DBRowJsonViewer({
data,
jsonColumns,
mapColumns,
showMaterializedAliasColumnsOption = false,
}: {
data: any;
jsonColumns?: string[];
// Map column names from the result-set metadata. Threaded into
// `mergePath` so numeric-looking sub-keys on a Map render as
// `Map['key']` instead of the array `Map[N+1]`. HDX-4369.
mapColumns?: string[];
// Only a viewer of the whole row from `useRowData` is affected by the option,
// and only when the source's row query would change.
showMaterializedAliasColumnsOption?: boolean;
}) {
const formatTime = useFormatTime();
const {
Expand Down Expand Up @@ -699,7 +729,12 @@ export function DBRowJsonViewer({
</Button>
)}
<div className="flex-grow-1" />
<HyperJsonMenu rowData={rowData} />
<HyperJsonMenu
rowData={rowData}
showMaterializedAliasColumnsOption={
showMaterializedAliasColumnsOption
}
/>
</Group>
</Box>
<Paper bg="transparent" mt="sm">
Expand Down
37 changes: 37 additions & 0 deletions packages/app/src/components/DBRowSidePanelErrorState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { useDisclosure } from '@mantine/hooks';
import { IconAlertTriangle } from '@tabler/icons-react';

import { IS_LOCAL_MODE } from '@/config';
import {
useMaterializedAliasColumnsOption,
useSelectAllColumnsQuerySettings,
} from '@/hooks/useMaterializedAliasColumnsOption';
import { useTableMetadata } from '@/hooks/useMetadata';
import { useBrandDisplayName } from '@/theme/ThemeProvider';

Expand Down Expand Up @@ -82,6 +86,30 @@ function KnownColumnsListHint({
);
}

// The option lives in the Column Values menu, which a failed row does not show.
function MaterializedAliasColumnsHint({ onHide }: { onHide: () => void }) {
return (
<Alert
variant="warning"
icon={<IconAlertTriangle size={16} />}
title="Materialized and alias columns are on"
data-testid="materialized-alias-columns-hint"
>
<Stack gap="xs" align="start">
<Text size="sm">
The row query includes MATERIALIZED and ALIAS columns. The whole row
fails to load if the connection&apos;s user cannot change query
settings (for example, a readonly user) or if one of these columns
cannot be evaluated.
</Text>
<Button size="xs" variant="subtle" onClick={onHide}>
Hide materialized and alias columns
</Button>
</Stack>
</Alert>
);
}

export function DBRowSidePanelErrorState({
error,
source,
Expand All @@ -94,6 +122,9 @@ export function DBRowSidePanelErrorState({

const showHint =
isMissingColumnError(error) && !!tableMetadata?.isPointerTable;
const selectAllColumnsSettings = useSelectAllColumnsQuerySettings(source);
const [, setShowMaterializedAliasColumns] =
useMaterializedAliasColumnsOption();

return (
<Stack gap="sm" data-testid="row-error-state">
Expand All @@ -103,6 +134,12 @@ export function DBRowSidePanelErrorState({
<KnownColumnsListHint onEditClick={editModal.open} source={source} />
)}

{selectAllColumnsSettings != null && (
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — The materialized/alias warning appears for every row error, even ones the settings did not cause

The hint only checks selectAllColumnsSettings != null. It never looks at error. So a network failure, a timeout, or a pointer-table missing-column error (which already shows KnownColumnsListHint) also gets a warning titled "Materialized and alias columns are on", pointing the user to the wrong fix. Fix: keep the action button available, but only use the warning title and copy when the error plausibly comes from the settings, for example error.query contains asterisk_include_ together with a READONLY or setting-constraint code, or an error thrown while evaluating a column. Otherwise use neutral copy.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. The error screen hides the menu, so the button has to be there whenever the option could be the cause, and sorting settings errors from other errors is the check review asked me to drop.

<MaterializedAliasColumnsHint
onHide={() => setShowMaterializedAliasColumns(false)}
/>
)}

<Stack align="start">
<Text size="sm" mt={10}>
Error Message:
Expand Down
Loading
Loading