2323from contextlib import asynccontextmanager
2424from typing import Literal
2525from urllib .parse import ParseResult as UrlLibParseResult
26- from urllib .parse import urlparse , urlunparse
26+ from urllib .parse import parse_qsl , urlencode , urlparse , urlunparse
2727
2828import boto3
2929import polars as pl
@@ -963,6 +963,63 @@ def _get_secret_args() -> str:
963963DEFAULT_UTA_DB_URL = "postgresql://uta_admin@localhost:5432/uta?options=-csearch_path%3Duta_20241220,public"
964964
965965
966+ def _normalize_uta_db_url (db_url : str ) -> str :
967+ """Normalize supported UTA DB URLs to psycopg-compatible URI form.
968+
969+ Deprecated legacy form:
970+ postgresql://user@host:5432/uta/uta_20241220
971+
972+ Preferred form:
973+ postgresql://user@host:5432/uta?options=-csearch_path%3Duta_20241220,public
974+ """
975+ parsed = urlparse (db_url )
976+ path_parts = [part for part in parsed .path .split ("/" ) if part ]
977+
978+ if len (path_parts ) <= 1 :
979+ return db_url
980+
981+ database = path_parts [0 ]
982+ schema = path_parts [1 ]
983+
984+ if len (path_parts ) > 2 : # noqa: PLR2004
985+ msg = f"Unsupported DB URL path: { parsed .path !r} "
986+ raise ValueError (msg )
987+
988+ warnings .warn (
989+ (
990+ "UTA DB URLs of the form "
991+ "'postgresql://user:password@host:port/database/schema' are deprecated. "
992+ "Use "
993+ "'postgresql://user:password@host:port/database"
994+ "?options=-csearch_path%3Dschema,public' instead."
995+ ),
996+ DeprecationWarning ,
997+ stacklevel = 2 ,
998+ )
999+
1000+ query = dict (parse_qsl (parsed .query , keep_blank_values = True ))
1001+
1002+ if "options" in query :
1003+ msg = (
1004+ "Legacy UTA DB URL includes both a schema path segment and existing "
1005+ "'options' query parameter. Please use only the modern search_path form."
1006+ )
1007+ raise ValueError (msg )
1008+
1009+ query ["options" ] = f"-csearch_path={ schema } ,public"
1010+
1011+ return urlunparse (
1012+ (
1013+ parsed .scheme ,
1014+ parsed .netloc ,
1015+ f"/{ database } " ,
1016+ parsed .params ,
1017+ urlencode (query ),
1018+ parsed .fragment ,
1019+ )
1020+ )
1021+
1022+
9661023async def create_uta_connection_pool (
9671024 db_url : str | None = None , initialize_genomic_table : bool = True
9681025) -> AsyncConnectionPool :
@@ -989,6 +1046,7 @@ async def create_uta_connection_pool(
9891046 db_url = _get_secret_args ()
9901047 elif db_url is None :
9911048 db_url = os .environ .get ("UTA_DB_URL" , DEFAULT_UTA_DB_URL )
1049+ db_url = _normalize_uta_db_url (db_url )
9921050 _logger .info (
9931051 "Creating connection pool with db_uri '%s'" ,
9941052 ParseResult (urlparse (db_url )).sanitized_url ,
0 commit comments