import { ChevronDown } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { useParams } from 'common' import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import AlertError from 'components/ui/AlertError' import DatabaseSelector from 'components/ui/DatabaseSelector' import { DocsButton } from 'components/ui/DocsButton' import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query' import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { pluckObjectFields } from 'lib/helpers' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { useDatabaseSettingsStateSnapshot } from 'state/database-settings' import { CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, Collapsible_Shadcn_, Input, Separator, Tabs, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_, Tooltip_Shadcn_, cn, } from 'ui' import { DefaultSessionModeNotice, IPv4AddonDirectConnectionNotice, IPv4DeprecationNotice, } from '../DatabaseConnectionNotices' import { UsePoolerCheckbox } from '../UsePoolerCheckbox' import { constructConnStringSyntax, getConnectionStrings, getPoolerTld, } from './DatabaseSettings.utils' const CONNECTION_TYPES = [ { id: 'uri', label: 'URI' }, { id: 'psql', label: 'PSQL' }, { id: 'golang', label: 'Golang' }, { id: 'jdbc', label: 'JDBC' }, { id: 'dotnet', label: '.NET' }, { id: 'nodejs', label: 'Node.js' }, { id: 'php', label: 'PHP' }, { id: 'python', label: 'Python' }, ] interface DatabaseConnectionStringProps { appearance: 'default' | 'minimal' } /** * @deprecated Will be removed once `connectDialogUpdate` flag is persisted */ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStringProps) => { const project = useSelectedProject() const { ref: projectRef, connectionString } = useParams() const snap = useDatabaseSettingsStateSnapshot() const state = useDatabaseSelectorStateSnapshot() const { project: projectDetails, isLoading: isProjectLoading } = useProjectContext() const connectionStringsRef = useRef(null) const [poolingMode, setPoolingMode] = useState<'transaction' | 'session'>('transaction') const [selectedTab, setSelectedTab] = useState< 'uri' | 'psql' | 'golang' | 'jdbc' | 'dotnet' | 'nodejs' | 'php' | 'python' >('uri') const { data: poolingInfo, isSuccess: isSuccessPoolingInfo } = usePoolingConfigurationQuery({ projectRef, }) const poolingConfiguration = poolingInfo?.find((x) => x.identifier === state.selectedDatabaseId) const defaultPoolingMode = poolingConfiguration?.pool_mode const { data: databases, error: readReplicasError, isLoading: isLoadingReadReplicas, isError: isErrorReadReplicas, isSuccess: isSuccessReadReplicas, } = useReadReplicasQuery({ projectRef }) const selectedDatabase = (databases ?? []).find( (db) => db.identifier === state.selectedDatabaseId ) const { data: addons, isSuccess: isSuccessAddons } = useProjectAddonsQuery({ projectRef }) const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? []) const { mutate: sendEvent } = useSendEventMutation() const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at'] const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' } const connectionInfo = pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS) const connectionTld = projectDetails?.restUrl !== undefined ? new URL(projectDetails?.restUrl ?? '').hostname.split('.').pop() ?? 'co' : 'co' const handleCopy = (id: string) => { const labelValue = CONNECTION_TYPES.find((type) => type.id === id)?.label sendEvent({ category: 'settings', action: 'copy_connection_string', label: labelValue ? labelValue : '', }) } const connectionStrings = isSuccessPoolingInfo && poolingConfiguration !== undefined ? getConnectionStrings(connectionInfo, poolingConfiguration, { projectRef, usePoolerConnection: snap.usePoolerConnection, }) : { uri: '', psql: '', golang: '', jdbc: '', dotnet: '', nodejs: '', php: '', python: '' } const poolerTld = isSuccessPoolingInfo && poolingConfiguration !== undefined ? getPoolerTld(poolingConfiguration?.connectionString) : 'com' const poolerConnStringSyntax = isSuccessPoolingInfo && poolingConfiguration !== undefined ? constructConnStringSyntax(poolingConfiguration?.connectionString, { selectedTab, usePoolerConnection: snap.usePoolerConnection, ref: projectRef as string, cloudProvider: isProjectLoading ? '' : project?.cloud_provider || '', region: isProjectLoading ? '' : project?.region || '', tld: snap.usePoolerConnection ? poolerTld : connectionTld, portNumber: snap.usePoolerConnection ? poolingMode === 'transaction' ? poolingConfiguration?.db_port.toString() : '5432' : connectionInfo.db_port.toString(), }) : [] useEffect(() => { if (connectionString !== undefined && connectionStringsRef.current !== undefined) { state.setSelectedDatabaseId(connectionString) connectionStringsRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' }) } }, [connectionString]) useEffect(() => { if (poolingConfiguration?.pool_mode === 'session') { setPoolingMode(poolingConfiguration.pool_mode) } }, [poolingConfiguration?.pool_mode]) return (
div:nth-child(1)]:!border-0 [&>div:nth-child(1)]:!p-0', appearance === 'minimal' && 'border-0 shadow-none bg-transparent' )} titleClasses={cn(appearance === 'minimal' && 'bg-transparent')} title={
{appearance === 'default' && (
Connection string
)}
{CONNECTION_TYPES.map((type) => ( ))}
} > {isLoadingReadReplicas && } {isErrorReadReplicas && ( )} {isSuccessReadReplicas && (
{defaultPoolingMode === 'session' && poolingMode === 'transaction' && ( )} {isSuccessAddons && poolingMode === 'session' && ipv4Addon !== undefined && snap.usePoolerConnection && } {ipv4Addon === undefined && !snap.usePoolerConnection && } handleCopy(selectedTab)} />
)}
{poolerConnStringSyntax.length > 0 && ( <>

How to connect to a different database or switch to another user

You can use the following URI format to switch to a different database or user {snap.usePoolerConnection ? ' when using connection pooling' : ''}.

{poolerConnStringSyntax.map((x, idx) => { if (x.tooltip) { return ( {x.value} {x.tooltip} ) } else { return ( {x.value} ) } })}

{selectedTab === 'python' && (

Connecting to SQL Alchemy

Please use postgresql:// instead of postgres:// as your dialect when connecting via SQLAlchemy.

Example: create_engine("postgresql+psycopg2://...")

)}
)}
) }