mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Joshen/fe 3624 queryblock causes client side crash if rendering many rows (#47021)
## Context Having a custom report block on the project home page with a SQL that returns a large set of results (e.g > 100k rows) causes a client side crash with "Maximum call stack size exceeded" <img width="400" alt="image" src="https://github.com/user-attachments/assets/e4bb5b73-e114-4687-9d0b-a7bff328167c" /> This is happening due to an array spread in `computeYAxisWidth` in `Math.max` - which am hence opting to use a `reduce` instead to mitigate the problem. Am also opting to apply the same autolimit logic in the SQL editor into the `QueryBlock` here, so that we don't unnecessarily fetch a large dataset in this UI. Added a UI indicator as well if auto limit has been applied (So this also overlaps into dashboard scalability too) <img width="1383" height="465" alt="image" src="https://github.com/user-attachments/assets/08b66398-f3b8-49ce-b4a4-23c91510bd54" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Report query blocks now feature automatic SQL limiting functionality, which restricts query results to a maximum of 100 rows when enabled * When active, query result blocks display an informational notice to users, clearly indicating the row restriction that has been applied <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
f4c3e26fd7
commit
42ec3c4960
3 files changed
+21
-3
No files matched your search
@@ -5,6 +5,7 @@ import { X } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { checkIfAppendLimitRequired, suffixWithLimit } from '../../SQLEditor/SQLEditor.utils'
|
||||
import { BURSTABLE_IO_METRIC_KEYS, DEPRECATED_REPORTS } from '../Reports.constants'
|
||||
import { ChartBlock } from './ChartBlock'
|
||||
import { DeprecatedChartBlock } from './DeprecatedChartBlock'
|
||||
@@ -78,7 +79,10 @@ export const ReportBlock = ({
|
||||
}
|
||||
)
|
||||
|
||||
const autoLimit = 100
|
||||
const sql = isSnippet ? (data?.content as SqlSnippets.Content)?.unchecked_sql : undefined
|
||||
const { appendAutoLimit } = checkIfAppendLimitRequired(sql ?? '', autoLimit)
|
||||
|
||||
const chartConfig = { ...DEFAULT_CHART_CONFIG, ...(item.chartConfig ?? {}) }
|
||||
const isDeprecatedChart = DEPRECATED_REPORTS.includes(item.attribute)
|
||||
const snippetMissing = contentError?.message.includes('Content not found')
|
||||
@@ -98,6 +102,7 @@ export const ReportBlock = ({
|
||||
sql,
|
||||
readOnlyConnectionString,
|
||||
postgresConnectionString,
|
||||
autoLimit,
|
||||
]),
|
||||
queryFn: async () => {
|
||||
if (!projectRef || !sql) return null
|
||||
@@ -109,13 +114,15 @@ export const ReportBlock = ({
|
||||
return null
|
||||
}
|
||||
|
||||
const formattedSql = suffixWithLimit(acceptUntrustedSql(sql), autoLimit)
|
||||
|
||||
return executeSql({
|
||||
projectRef,
|
||||
connectionString,
|
||||
// acceptUntrustedSql is usually not allowed in an auto-run position,
|
||||
// but in this case we are explicitly allowing it because adding a block
|
||||
// to a report is an explicit user action.
|
||||
sql: acceptUntrustedSql(sql),
|
||||
sql: formattedSql,
|
||||
})
|
||||
},
|
||||
enabled: !isLoadingContent && contentError == null,
|
||||
@@ -180,6 +187,7 @@ export const ReportBlock = ({
|
||||
onUpdateChartConfig={onUpdateChart}
|
||||
onRemoveChart={() => onRemoveChart({ metric: { key: item.attribute } })}
|
||||
disabled={isLoadingContent || snippetMissing || !sql}
|
||||
autoLimit={appendAutoLimit}
|
||||
/>
|
||||
) : isUnavailableBurstChart ? (
|
||||
<UnavailableChartBlock
|
||||
|
||||
@@ -50,6 +50,7 @@ export interface QueryBlockProps {
|
||||
blockWriteQueries?: boolean
|
||||
/** Render the chart tooltip in a portal so it isn't clipped by overflow-hidden ancestors (e.g. report cards). */
|
||||
portalTooltip?: boolean
|
||||
autoLimit?: boolean
|
||||
onExecute?: (queryType: 'select' | 'mutation') => void
|
||||
onRemoveChart?: () => void
|
||||
onUpdateChartConfig?: ({ chartConfig }: { chartConfig: Partial<ChartConfig> }) => void
|
||||
@@ -73,6 +74,7 @@ export const QueryBlock = ({
|
||||
disabled = false,
|
||||
blockWriteQueries = false,
|
||||
portalTooltip = false,
|
||||
autoLimit = false,
|
||||
onExecute,
|
||||
onRemoveChart,
|
||||
onUpdateChartConfig,
|
||||
@@ -390,6 +392,11 @@ export const QueryBlock = ({
|
||||
)}
|
||||
>
|
||||
<Results rows={results} />
|
||||
{autoLimit && (
|
||||
<p className="text-xs font-mono px-2 py-1 border-t text-foreground-light">
|
||||
Limited to only 100 rows
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -31,8 +31,11 @@ export const computeYAxisWidth = (
|
||||
): number => {
|
||||
if (isLogScale) return 52
|
||||
if (isPercentage) return Math.max(36, (3 + 1) * 8) // max tick is "100"
|
||||
const maxMagnitude =
|
||||
data.length > 0 ? Math.max(...data.map((d) => Math.abs(Number(d[key]) || 0))) : 0
|
||||
|
||||
const maxMagnitude = data.reduce((max, d) => {
|
||||
const magnitude = Math.abs(Number(d[key]) || 0)
|
||||
return magnitude > max ? magnitude : max
|
||||
}, 0)
|
||||
return Math.max(36, (formatYAxisTick(maxMagnitude).length + 1) * 8)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user