1+ from typing import Optional
2+ import os
3+ from .config import DBConfig
4+ from .logger import logger
5+
6+ from dotenv import load_dotenv
7+
8+ from .base_connector import BaseConnector
9+
10+ from .clickhouse_connector import ClickHouseConnector
11+ from .postgres_connector import PostgresConnector
12+ from .mysql_connector import MySQLConnector
13+ from .mariadb_connector import MariaDBConnector
14+ from .oracle_connector import OracleConnector
15+ from .duckdb_connector import DuckDBConnector
16+ from .databricks_connector import DatabricksConnector
17+ from .snowflake_connector import SnowflakeConnector
18+
19+ env_path = os .path .join (os .getcwd (), ".env" )
20+
21+ if os .path .exists (env_path ):
22+ load_dotenv (env_path , override = True )
23+ print (f"✅ 환경변수 파일(.env)이 { os .getcwd ()} 에 로드되었습니다!" )
24+ else :
25+ print (f"⚠️ 환경변수 파일(.env)이 { os .getcwd ()} 에 없습니다!" )
26+
27+ def get_db_connector (db_type : Optional [str ] = None , config : Optional [DBConfig ] = None ):
28+ """
29+ Return the appropriate DB connector instance.
30+ - If db_type is not provided, loads from environment variable DB_TYPE
31+ - If config is not provided, loads from environment using db_type
32+
33+ Parameters:
34+ db_type (Optional[str]): Database type (e.g., 'postgresql', 'mysql')
35+ config (Optional[DBConfig]): Connection config
36+
37+ Returns:
38+ BaseConnector: Initialized DB connector instance
39+
40+ Raises:
41+ ValueError: If type/config is missing or invalid
42+ """
43+ if db_type is None :
44+ db_type = os .getenv ("DB_TYPE" )
45+ if not db_type :
46+ raise ValueError ("DB type must be provided or set in environment as DB_TYPE." )
47+
48+ db_type = db_type .lower ()
49+
50+ if config is None :
51+ config = load_config_from_env (db_type .upper ())
52+
53+ connector_map = {
54+ "clickhouse" : ClickHouseConnector ,
55+ "postgresql" : PostgresConnector ,
56+ "mysql" : MySQLConnector ,
57+ "mariadb" : MariaDBConnector ,
58+ "oracle" : OracleConnector ,
59+ "duckdb" : DuckDBConnector ,
60+ "databricks" : DatabricksConnector ,
61+ "snowflake" : SnowflakeConnector ,
62+ }
63+
64+ if db_type not in connector_map :
65+ logger .error (f"Unsupported DB type: { db_type } " )
66+ raise ValueError (f"Unsupported DB type: { db_type } " )
67+
68+ required_fields = {
69+ "oracle" : ["extra.service_name" ],
70+ "databricks" : ["extra.http_path" , "extra.access_token" ],
71+ "snowflake" : ["extra.account" ],
72+ }
73+
74+ missing = []
75+ for path in required_fields .get (db_type , []):
76+ cur = config
77+ for key in path .split ("." ):
78+ cur = cur .get (key ) if isinstance (cur , dict ) else None
79+ if cur is None :
80+ missing .append (path )
81+ break
82+
83+ if missing :
84+ logger .error (f"Missing required fields for { db_type } : { ', ' .join (missing )} " )
85+ raise ValueError (f"Missing required fields for { db_type } : { ', ' .join (missing )} " )
86+
87+ return connector_map [db_type ](config )
88+
89+
90+
91+ def load_config_from_env (prefix : str ) -> DBConfig :
92+ """
93+ Load DBConfig from environment variables with a given prefix.
94+ Standard keys are extracted, all other prefixed keys go to 'extra'.
95+
96+ Example:
97+ If prefix = 'SNOWFLAKE', loads:
98+ - SNOWFLAKE_HOST
99+ - SNOWFLAKE_USER
100+ - SNOWFLAKE_PASSWORD
101+ - SNOWFLAKE_PORT
102+ - SNOWFLAKE_DATABASE
103+ Other keys like SNOWFLAKE_ACCOUNT, SNOWFLAKE_WAREHOUSE -> extra
104+ """
105+ base_keys = {"HOST" , "PORT" , "USER" , "PASSWORD" , "DATABASE" }
106+
107+ # Extract standard values
108+ config = {
109+ "host" : os .getenv (f"{ prefix } _HOST" ),
110+ "port" : int (os .getenv (f"{ prefix } _PORT" )) if os .getenv (f"{ prefix } _PORT" ) else None ,
111+ "user" : os .getenv (f"{ prefix } _USER" ),
112+ "password" : os .getenv (f"{ prefix } _PASSWORD" ),
113+ "database" : os .getenv (f"{ prefix } _DATABASE" ),
114+ }
115+
116+ # Auto-detect extra keys
117+ extra = {}
118+ for key , value in os .environ .items ():
119+ if key .startswith (f"{ prefix } _" ):
120+ suffix = key [len (f"{ prefix } _" ):]
121+ if suffix .upper () not in base_keys :
122+ extra [suffix .lower ()] = value
123+
124+ if extra :
125+ config ["extra" ] = extra
126+
127+ return DBConfig (** config )
0 commit comments