diff --git a/apps/studio/components/interfaces/QueryPerformance/useQueryPerformanceQuery.ts b/apps/studio/components/interfaces/QueryPerformance/useQueryPerformanceQuery.ts index 317205ab773..706af70ce59 100644 --- a/apps/studio/components/interfaces/QueryPerformance/useQueryPerformanceQuery.ts +++ b/apps/studio/components/interfaces/QueryPerformance/useQueryPerformanceQuery.ts @@ -138,7 +138,7 @@ export const useQueryPerformanceInfiniteQuery = ( const { data: databases } = useReadReplicasQuery({ projectRef: project?.ref }) const connectionString = (databases || []).find( (db) => db.identifier === state.selectedDatabaseId - )?.connectionString + )?.connection_string_read_only // default to read_only connection string // Clamp pageSize the same way generateQueryPerformanceSql does so getNextPageParam // and the queryKey are always consistent with the SQL actually executed. diff --git a/apps/studio/hooks/analytics/useDbQuery.tsx b/apps/studio/hooks/analytics/useDbQuery.tsx index c080a115758..5d83ce266f5 100644 --- a/apps/studio/hooks/analytics/useDbQuery.tsx +++ b/apps/studio/hooks/analytics/useDbQuery.tsx @@ -10,6 +10,7 @@ import { import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query' import { executeSql } from '@/data/sql/execute-sql-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' +import { IS_PLATFORM } from '@/lib/constants' import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector' export interface DbQueryHook { @@ -43,9 +44,16 @@ const useDbQuery = ({ const { data: databases } = useReadReplicasQuery({ projectRef: project?.ref }) const connectionString = (databases || []).find( (db) => db.identifier === state.selectedDatabaseId - )?.connectionString + )?.connection_string_read_only // default to using the read_only string const identifier = state.selectedDatabaseId + // When a read-replica is selected, require its connection string before fetching. + // Falling back to the primary's connection string would silently query the wrong database. + const isPrimarySelected = !state.selectedDatabaseId || state.selectedDatabaseId === project?.ref + const effectiveConnectionString = isPrimarySelected + ? (connectionString ?? project?.connectionString) + : connectionString + const resolvedSql = typeof sql === 'function' ? sql([]) : sql const { @@ -59,7 +67,7 @@ const useDbQuery = ({ 'projects', project?.ref, 'db', - { ...params, sql: resolvedSql, identifier }, + { ...params, sql: resolvedSql, identifier, connectionString: effectiveConnectionString }, where, orderBy, ], @@ -67,13 +75,17 @@ const useDbQuery = ({ return executeSql( { projectRef: project?.ref, - connectionString: connectionString || project?.connectionString, + connectionString: effectiveConnectionString, sql: resolvedSql, }, signal ).then((res) => res.result) as Promise }, - enabled: Boolean(resolvedSql), + // Don't run until we have a connection string for the selected database. + // For replicas this prevents a silent fallback to the primary before replicas load. + // In self-hosted mode (IS_PLATFORM=false) there is no real connection string, so we + // skip the check — executeSql works fine without one on self-hosted deployments. + enabled: Boolean(resolvedSql) && (!IS_PLATFORM || Boolean(effectiveConnectionString)), refetchOnWindowFocus: false, refetchOnReconnect: false, })