import { ChevronDown } from 'lucide-react' import { HTMLAttributes, ReactNode, useState } from 'react' import { useParams } from 'common' import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils' import AlertError from 'components/ui/AlertError' import DatabaseSelector from 'components/ui/DatabaseSelector' 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 { TelemetryActions } from 'lib/constants/telemetry' import { pluckObjectFields } from 'lib/helpers' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { CodeBlock, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, Collapsible_Shadcn_, DIALOG_PADDING_X, SelectContent_Shadcn_, SelectItem_Shadcn_, SelectTrigger_Shadcn_, SelectValue_Shadcn_, Select_Shadcn_, Separator, cn, } from 'ui' import { Admonition } from 'ui-patterns' import { CONNECTION_PARAMETERS, DATABASE_CONNECTION_TYPES, DatabaseConnectionType, } from './Connect.constants' import { CodeBlockFileHeader, ConnectionPanel } from './ConnectionPanel' import { getConnectionStrings } from './DatabaseSettings.utils' import examples, { Example } from './DirectConnectionExamples' const StepLabel = ({ number, children, ...props }: { number: number; children: ReactNode } & HTMLAttributes) => (
{number}
{children}
) export const DatabaseConnectionString = () => { const { ref: projectRef } = useParams() const state = useDatabaseSelectorStateSnapshot() const [selectedTab, setSelectedTab] = useState('uri') const { data: poolingInfo, error: poolingInfoError, isLoading: isLoadingPoolingInfo, isError: isErrorPoolingInfo, isSuccess: isSuccessPoolingInfo, } = usePoolingConfigurationQuery({ projectRef, }) const poolingConfiguration = poolingInfo?.find((x) => x.identifier === state.selectedDatabaseId) const { data: databases, error: readReplicasError, isLoading: isLoadingReadReplicas, isError: isErrorReadReplicas, isSuccess: isSuccessReadReplicas, } = useReadReplicasQuery({ projectRef }) const error = poolingInfoError || readReplicasError const isLoading = isLoadingPoolingInfo || isLoadingReadReplicas const isError = isErrorPoolingInfo || isErrorReadReplicas const isSuccess = isSuccessPoolingInfo && isSuccessReadReplicas const selectedDatabase = (databases ?? []).find( (db) => db.identifier === state.selectedDatabaseId ) const { data: addons } = 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 handleCopy = ( connectionTypeId: string, connectionMethod: 'direct' | 'transaction_pooler' | 'session_pooler' ) => { const connectionInfo = DATABASE_CONNECTION_TYPES.find((type) => type.id === connectionTypeId) const connectionType = connectionInfo?.label ?? 'Unknown' const lang = connectionInfo?.lang ?? 'Unknown' sendEvent({ action: TelemetryActions.CONNECTION_STRING_COPIED, properties: { connectionType, lang, connectionMethod }, }) } const connectionStrings = isSuccessPoolingInfo && poolingConfiguration !== undefined ? getConnectionStrings(connectionInfo, poolingConfiguration, { projectRef, }) : { direct: { uri: '', psql: '', golang: '', jdbc: '', dotnet: '', nodejs: '', php: '', python: '', sqlalchemy: '', }, pooler: { uri: '', psql: '', golang: '', jdbc: '', dotnet: '', nodejs: '', php: '', python: '', sqlalchemy: '', }, } // @mildtomato - Possible reintroduce later // // 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: `[5432 or 6543]`, // }) // : [] // useEffect(() => { // // if (poolingConfiguration?.pool_mode === 'session') { // // setPoolingMode(poolingConfiguration.pool_mode) // // } // }, [poolingConfiguration?.pool_mode]) const lang = DATABASE_CONNECTION_TYPES.find((type) => type.id === selectedTab)?.lang ?? 'bash' const contentType = DATABASE_CONNECTION_TYPES.find((type) => type.id === selectedTab)?.contentType ?? 'input' const example: Example | undefined = examples[selectedTab as keyof typeof examples] const exampleFiles = example?.files const exampleInstallCommands = example?.installCommands const examplePostInstallCommands = example?.postInstallCommands const hasCodeExamples = exampleFiles || exampleInstallCommands const fileTitle = DATABASE_CONNECTION_TYPES.find((type) => type.id === selectedTab)?.fileTitle // [Refactor] See if we can do this in an immutable way, technically not a good practice to do this let stepNumber = 0 return (
Type setSelectedTab(connectionType) } > {DATABASE_CONNECTION_TYPES.map((type) => ( {type.label} ))}
{isLoading && (
)} {isError && (
)} {isSuccess && (
{/* // handle non terminal examples */} {hasCodeExamples && (
Install the following {exampleInstallCommands?.map((cmd, i) => ( {cmd} ))}
{exampleFiles && exampleFiles?.length > 0 && (
Add file to project {exampleFiles?.map((file, i) => (
))}
)}
)}
{hasCodeExamples && (
Choose type of connection
)}
handleCopy(selectedTab, 'direct')} /> handleCopy(selectedTab, 'transaction_pooler')} /> {ipv4Addon && (

If you are using Session Pooler, we recommend switching to Direct Connection.

)} handleCopy(selectedTab, 'session_pooler')} />
{examplePostInstallCommands && (
Add the configuration package to read the settings {examplePostInstallCommands?.map((cmd, i) => ( {cmd} ))}
)}
)} {/* Possibly reintroduce later - @mildtomato */} {/*

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://...")

)}
) }