mirror of
https://github.com/supabase/supabase.git
synced 2026-11-06 12:24:31 +08:00
## Problem attempt to [fix this ticket](https://supabase.frontapp.com/inboxes/teams/folders/27650590/unassigned/116845868830): when the studio ai assistant runs a query with previously set database opt-in, `execute_sql` returns every row while the query cell's footer says "Limit 100 rows" causing in case of large amount of returned rows browser to freeze + the chat fails with "Sorry, I'm having trouble responding right now" until the user clears it ## Solution - `execute_sql` now keeps the first 100 rows using the cell's existing `DEFAULT_CELL_ROW_LIMIT` - uses in history sanitizer the same `toModelRows` helper - chats saved before this change have no `totalRows` so the helper falls back to `rows.length` and cuts them to 100 too <!-- ## Preview links If relevant, include links to changed pages for easy review access. Copy the preview base URL from the Vercel bot comment on this PR. Use the following table as an example template. | Site | Live | Preview | Search for | | -------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------- | | WWW | [/blog/your-post](https://supabase.com/blog/your-post) | [/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post) | unique phrase from the change | | Docs | [/docs/guides/your-page](https://supabase.com/docs/guides/your-page) | [/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page) | unique phrase from the change | | Studio | [/dashboard](https://supabase.com/dashboard) | [/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard) | unique phrase from the change | | Design system | [/design-system](https://supabase.com/design-system) | [/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system) | unique phrase from the change | | UI library | [/library](https://supabase.com/library) | [/library](https://ui-library-git-branch-name-supabase.vercel.app/library) | unique phrase from the change | | Knowledge base | [/kb/guides/your-page](https://supabase.com/kb/guides/your-page) | [/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page) | unique phrase from the change | --> <!-- ## Additional context Optionally add any other context or screenshots. --> ## Review instructions 1. In a test project, run this in the SQL editor: ```sql create table "emailSubscriber" (id bigint generated always as identity primary key, email text); create table "stripeCustomer" (id bigint generated always as identity primary key, email text); insert into "emailSubscriber" (email) select md5(g::text) || '@example.com' from generate_series(1, 50000) g; insert into "stripeCustomer" (email) select md5(g::text) || '@example.com' from generate_series(1, 1000) g; 2. on the live dashboard, open the assistant in a new chat and send "i want all the emails in emailSubscriber where the email is not in the stripeCustomer table" 3. in the opt in requested settings choose "Schema, Logs & Database Data" 4. then click run query, the cell should show 100 rows (vs 50000 rows in prod) ## Checklist Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs [style guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide) --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import { SupportCategories } from '@supabase/shared-types/out/constants'
|
|
import { PropsWithChildren, useEffect, useRef } from 'react'
|
|
import { Admonition } from 'ui-patterns/Admonition'
|
|
|
|
import { SupportLink } from '@/components/interfaces/Support/SupportLink'
|
|
import { InlineLinkClassName } from '@/components/ui/InlineLink'
|
|
import { isDashboardErrorSampled } from '@/lib/telemetry/error-sampling'
|
|
import { useTrack } from '@/lib/telemetry/track'
|
|
|
|
export interface AlertErrorProps {
|
|
projectRef?: string
|
|
orgSlug?: string
|
|
subject?: string
|
|
description?: string
|
|
error?: { message: string } | null
|
|
layout?: 'vertical' | 'horizontal' | 'responsive'
|
|
className?: string
|
|
showIcon?: boolean
|
|
showInstructions?: boolean
|
|
showErrorPrefix?: boolean
|
|
additionalActions?: React.ReactNode
|
|
hideContactSupport?: boolean
|
|
}
|
|
|
|
const SUPPORT_PHRASE_REGEX = /(contact support)/i
|
|
|
|
// [Joshen] To standardize the language for all error UIs
|
|
export const AlertError = ({
|
|
projectRef,
|
|
orgSlug,
|
|
subject,
|
|
hideContactSupport = false,
|
|
description = hideContactSupport
|
|
? 'Try refreshing your browser.'
|
|
: 'Try refreshing your browser, but if the issue persists for more than a few minutes, contact support.',
|
|
error,
|
|
className,
|
|
showIcon = true,
|
|
layout,
|
|
showInstructions = true,
|
|
showErrorPrefix = true,
|
|
children,
|
|
additionalActions,
|
|
}: PropsWithChildren<AlertErrorProps>) => {
|
|
const track = useTrack()
|
|
const hasTrackedRef = useRef(false)
|
|
|
|
const formattedErrorMessage = error?.message?.includes('503')
|
|
? '503 Service Temporarily Unavailable'
|
|
: error?.message
|
|
|
|
const hasInlineSupportLink =
|
|
showInstructions && !hideContactSupport && SUPPORT_PHRASE_REGEX.test(description)
|
|
const canShowSupportFallback = !hideContactSupport && !hasInlineSupportLink
|
|
// Without instructions there's no sentence to link, so the fallback continues the message
|
|
const isSupportFallbackInline = canShowSupportFallback && !showInstructions && !!error?.message
|
|
|
|
const renderSupportLink = (text: string, key?: number) => (
|
|
<SupportLink
|
|
key={key}
|
|
className={InlineLinkClassName}
|
|
queryParams={{
|
|
category: SupportCategories.DASHBOARD_BUG,
|
|
projectRef,
|
|
orgSlug,
|
|
subject,
|
|
errorMessage: error?.message,
|
|
}}
|
|
>
|
|
{text}
|
|
</SupportLink>
|
|
)
|
|
|
|
// Splitting with a capture group keeps the matched text, so it can be swapped for a link
|
|
const renderDescriptionWithSupportLink = (text: string) =>
|
|
text
|
|
.split(SUPPORT_PHRASE_REGEX)
|
|
.map((part, index) =>
|
|
SUPPORT_PHRASE_REGEX.test(part) ? renderSupportLink(part, index) : part
|
|
)
|
|
|
|
useEffect(() => {
|
|
if (!hasTrackedRef.current) {
|
|
hasTrackedRef.current = true
|
|
if (isDashboardErrorSampled()) {
|
|
track('dashboard_error_created', {
|
|
source: 'admonition',
|
|
})
|
|
}
|
|
}
|
|
}, [track])
|
|
|
|
return (
|
|
<Admonition
|
|
type="warning"
|
|
layout={layout ?? (additionalActions ? 'vertical' : 'responsive')}
|
|
showIcon={showIcon}
|
|
title={subject}
|
|
description={
|
|
<>
|
|
{error?.message && (
|
|
<p>
|
|
{showErrorPrefix && 'Error: '}
|
|
{formattedErrorMessage}
|
|
{isSupportFallbackInline && (
|
|
<>
|
|
{/[.!?]$/.test(formattedErrorMessage ?? '') ? ' ' : '. '}
|
|
If the issue persists, {renderSupportLink('contact support')}.
|
|
</>
|
|
)}
|
|
</p>
|
|
)}
|
|
{showInstructions && (
|
|
<p>
|
|
{hasInlineSupportLink ? renderDescriptionWithSupportLink(description) : description}
|
|
</p>
|
|
)}
|
|
{canShowSupportFallback && !isSupportFallbackInline && (
|
|
<p>{renderSupportLink('Contact support')}</p>
|
|
)}
|
|
{children}
|
|
</>
|
|
}
|
|
actions={additionalActions}
|
|
className={className}
|
|
/>
|
|
)
|
|
}
|