diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts index 3fd1dbfbbea..88bd1b0cf94 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts @@ -16,10 +16,51 @@ export const QUERY_PERFORMANCE_COLUMNS = [ { id: 'query', name: 'Query', description: undefined, minWidth: 500 }, { id: 'prop_total_time', name: 'Time consumed', description: undefined, minWidth: 130 }, { id: 'total_time', name: 'Total time', description: 'latency', minWidth: 150 }, - { id: 'calls', name: 'Calls', description: undefined, minWidth: 100 }, + { id: 'calls', name: 'Count', description: undefined, minWidth: 100 }, { id: 'max_time', name: 'Max time', description: undefined, minWidth: 100 }, { id: 'mean_time', name: 'Mean time', description: undefined, minWidth: 100 }, { id: 'min_time', name: 'Min time', description: undefined, minWidth: 100 }, - { id: 'avg_rows', name: 'Avg. Rows', description: undefined, minWidth: 100 }, - { id: 'rolname', name: 'Role', description: undefined, minWidth: 120 }, + { id: 'rows_read', name: 'Rows read', description: undefined, minWidth: 100 }, + { id: 'cache_hit_rate', name: 'Cache hit rate', description: undefined, minWidth: 130 }, + { id: 'rolname', name: 'Role', description: undefined, minWidth: 160 }, +] as const + +export const QUERY_PERFORMANCE_ROLE_DESCRIPTION = [ + { name: 'postgres', description: 'The default Postgres role. This has admin privileges.' }, + { + name: 'anon', + description: + 'For unauthenticated, public access. This is the role which the API (PostgREST) will use when a user is not logged in.', + }, + { + name: 'authenticator', + description: + 'A special role for the API (PostgREST). It has very limited access, and is used to validate a JWT and then "change into" another role determined by the JWT verification.', + }, + { + name: 'authenticated', + description: + 'For "authenticated access." This is the role which the API (PostgREST) will use when a user is logged in.', + }, + { + name: 'service_role', + description: + 'For elevated access. This role is used by the API (PostgREST) to bypass Row Level Security.', + }, + { + name: 'supabase_auth_admin', + description: + 'Used by the Auth middleware to connect to the database and run migration. Access is scoped to the auth schema.', + }, + { + name: 'supabase_storage_admin', + description: + 'Used by the Auth middleware to connect to the database and run migration. Access is scoped to the storage schema.', + }, + { name: 'dashboard_user', description: 'For running commands via the Supabase UI.' }, + { + name: 'supabase_admin', + description: + 'An internal role Supabase uses for administrative tasks, such as running upgrades and automations.', + }, ] as const diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx index acf01e6b997..e77aa1aaf62 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx @@ -20,6 +20,7 @@ import { cn, CodeBlock, } from 'ui' +import { InfoTooltip } from 'ui-patterns/info-tooltip' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { hasIndexRecommendations } from './index-advisor.utils' import { IndexSuggestionIcon } from './IndexSuggestionIcon' @@ -28,6 +29,7 @@ import { QueryIndexes } from './QueryIndexes' import { QUERY_PERFORMANCE_COLUMNS, QUERY_PERFORMANCE_REPORT_TYPES, + QUERY_PERFORMANCE_ROLE_DESCRIPTION, } from './QueryPerformance.constants' import { useQueryPerformanceSort } from './hooks/useQueryPerformanceSort' @@ -138,14 +140,6 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance ) } - if (col.id === 'rolname') { - return ( -
-

{value || 'n/a'}

-
- ) - } - if (col.id === 'prop_total_time') { const percentage = props.row.prop_total_time || 0 const fillWidth = Math.min(percentage, 100) @@ -159,7 +153,13 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance opacity: 0.04, }} /> -

{value ? `${value.toFixed(1)}%` : 'n/a'}

+ {value ? ( +

+ {value.toFixed(1)}% +

+ ) : ( +

+ )} ) } @@ -175,8 +175,99 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance if (col.id === 'total_time') { return (
- {isTime && typeof value === 'number' && !isNaN(value) && isFinite(value) && ( -

{(value / 1000).toFixed(2) + 's' || 'n/a'}

+ {isTime && typeof value === 'number' && !isNaN(value) && isFinite(value) ? ( +

+ {(value / 1000).toFixed(2) + 's'} +

+ ) : ( +

+ )} +
+ ) + } + + if (col.id === 'calls') { + return ( +
+ {typeof value === 'number' && !isNaN(value) && isFinite(value) ? ( +

+ {value.toLocaleString()} +

+ ) : ( +

+ )} +
+ ) + } + + if (col.id === 'max_time' || col.id === 'mean_time' || col.id === 'min_time') { + return ( +
+ {typeof value === 'number' && !isNaN(value) && isFinite(value) ? ( +

+ {value.toFixed(0)}ms +

+ ) : ( +

+ )} +
+ ) + } + + if (col.id === 'rows_read') { + return ( +
+ {typeof value === 'number' && !isNaN(value) && isFinite(value) ? ( +

+ {value.toLocaleString()} +

+ ) : ( +

+ )} +
+ ) + } + + const cacheHitRateToNumber = (value: number | string) => { + if (typeof value === 'number') return value + return parseFloat(value.toString().replace('%', '')) || 0 + } + + if (col.id === 'cache_hit_rate') { + return ( +
+ {typeof value === 'string' ? ( +

+ {cacheHitRateToNumber(value).toFixed(2)}% +

+ ) : ( +

+ )} +
+ ) + } + + if (col.id === 'rolname') { + return ( +
+ {value ? ( + +

{value}

+ + { + QUERY_PERFORMANCE_ROLE_DESCRIPTION.find((role) => role.name === value) + ?.description + } + +
+ ) : ( +

)}
) diff --git a/apps/studio/components/interfaces/Reports/Reports.constants.ts b/apps/studio/components/interfaces/Reports/Reports.constants.ts index aea68a98aea..010d3096508 100644 --- a/apps/studio/components/interfaces/Reports/Reports.constants.ts +++ b/apps/studio/components/interfaces/Reports/Reports.constants.ts @@ -369,7 +369,17 @@ select -- min_time, -- max_time, -- mean_time, - statements.rows / statements.calls as avg_rows${ + statements.rows / statements.calls as avg_rows, + statements.rows as rows_read, + case + when (statements.shared_blks_hit + statements.shared_blks_read) > 0 + then round( + (statements.shared_blks_hit * 100.0) / + (statements.shared_blks_hit + statements.shared_blks_read), + 2 + ) + else 0 + end as cache_hit_rate${ runIndexAdvisor ? `, case @@ -513,6 +523,15 @@ select -- max_time, -- mean_time, statements.rows / statements.calls as avg_rows, + statements.rows as rows_read, + statements.shared_blks_hit as debug_hit, + statements.shared_blks_read as debug_read, + case + when (statements.shared_blks_hit + statements.shared_blks_read) > 0 + then (statements.shared_blks_hit::numeric * 100.0) / + (statements.shared_blks_hit + statements.shared_blks_read) + else 0 + end as cache_hit_rate, ((statements.total_exec_time + statements.total_plan_time)/sum(statements.total_exec_time + statements.total_plan_time) OVER()) * 100 as prop_total_time${ runIndexAdvisor ? `,