diff --git a/api/configs/middleware/vdb/oracle_config.py b/api/configs/middleware/vdb/oracle_config.py index 5d2cf67ba3..ea39909ef4 100644 --- a/api/configs/middleware/vdb/oracle_config.py +++ b/api/configs/middleware/vdb/oracle_config.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import Field, PositiveInt +from pydantic import Field from pydantic_settings import BaseSettings @@ -9,16 +9,6 @@ class OracleConfig(BaseSettings): Configuration settings for Oracle database """ - ORACLE_HOST: Optional[str] = Field( - description="Hostname or IP address of the Oracle database server (e.g., 'localhost' or 'oracle.example.com')", - default=None, - ) - - ORACLE_PORT: PositiveInt = Field( - description="Port number on which the Oracle database server is listening (default is 1521)", - default=1521, - ) - ORACLE_USER: Optional[str] = Field( description="Username for authenticating with the Oracle database", default=None, @@ -29,7 +19,28 @@ class OracleConfig(BaseSettings): default=None, ) - ORACLE_DATABASE: Optional[str] = Field( - description="Name of the Oracle database or service to connect to (e.g., 'ORCL' or 'pdborcl')", + ORACLE_DSN: Optional[str] = Field( + description="Oracle database connection string. For traditional database, use format 'host:port/service_name'. " + "For autonomous database, use the service name from tnsnames.ora in the wallet", default=None, ) + + ORACLE_CONFIG_DIR: Optional[str] = Field( + description="Directory containing the tnsnames.ora configuration file. Only used in thin mode connection", + default=None, + ) + + ORACLE_WALLET_LOCATION: Optional[str] = Field( + description="Oracle wallet directory path containing the wallet files for secure connection", + default=None, + ) + + ORACLE_WALLET_PASSWORD: Optional[str] = Field( + description="Password to decrypt the Oracle wallet, if it is encrypted", + default=None, + ) + + ORACLE_IS_AUTONOMOUS: bool = Field( + description="Flag indicating whether connecting to Oracle Autonomous Database", + default=False, + ) diff --git a/api/core/rag/datasource/vdb/oracle/oraclevector.py b/api/core/rag/datasource/vdb/oracle/oraclevector.py index a58df7eb9f..8262c219b4 100644 --- a/api/core/rag/datasource/vdb/oracle/oraclevector.py +++ b/api/core/rag/datasource/vdb/oracle/oraclevector.py @@ -23,25 +23,30 @@ oracledb.defaults.fetch_lobs = False class OracleVectorConfig(BaseModel): - host: str - port: int user: str password: str - database: str + dsn: str + config_dir: str | None = None + wallet_location: str | None = None + wallet_password: str | None = None + is_autonomous: bool = False @model_validator(mode="before") @classmethod def validate_config(cls, values: dict) -> dict: - if not values["host"]: - raise ValueError("config ORACLE_HOST is required") - if not values["port"]: - raise ValueError("config ORACLE_PORT is required") if not values["user"]: raise ValueError("config ORACLE_USER is required") if not values["password"]: raise ValueError("config ORACLE_PASSWORD is required") - if not values["database"]: - raise ValueError("config ORACLE_DB is required") + if not values["dsn"]: + raise ValueError("config ORACLE_DSN is required") + if values.get("is_autonomous", False): + if not values.get("config_dir"): + raise ValueError("config_dir is required for autonomous database") + if not values.get("wallet_location"): + raise ValueError("wallet_location is required for autonomous database") + if not values.get("wallet_password"): + raise ValueError("wallet_password is required for autonomous database") return values @@ -56,7 +61,7 @@ CREATE TABLE IF NOT EXISTS {table_name} ( SQL_CREATE_INDEX = """ CREATE INDEX IF NOT EXISTS idx_docs_{table_name} ON {table_name}(text) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS -('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER sys.my_chinese_vgram_lexer') +('FILTER CTXSYS.NULL_FILTER SECTION GROUP CTXSYS.HTML_SECTION_GROUP LEXER multilingual_lexer') """ @@ -103,14 +108,25 @@ class OracleVector(BaseVector): ) def _create_connection_pool(self, config: OracleVectorConfig): - return oracledb.create_pool( - user=config.user, - password=config.password, - dsn="{}:{}/{}".format(config.host, config.port, config.database), - min=1, - max=50, - increment=1, - ) + pool_params = { + "user": config.user, + "password": config.password, + "dsn": config.dsn, + "min": 1, + "max": 50, + "increment": 1, + } + + if config.is_autonomous: + pool_params.update( + { + "config_dir": config.config_dir, + "wallet_location": config.wallet_location, + "wallet_password": config.wallet_password, + } + ) + + return oracledb.create_pool(**pool_params) @contextmanager def _get_cursor(self): @@ -287,10 +303,12 @@ class OracleVectorFactory(AbstractVectorFactory): return OracleVector( collection_name=collection_name, config=OracleVectorConfig( - host=dify_config.ORACLE_HOST or "localhost", - port=dify_config.ORACLE_PORT, user=dify_config.ORACLE_USER or "system", password=dify_config.ORACLE_PASSWORD or "oracle", - database=dify_config.ORACLE_DATABASE or "orcl", + dsn=dify_config.ORACLE_DSN or "oracle:1521/freepdb1", + config_dir=dify_config.ORACLE_CONFIG_DIR, + wallet_location=dify_config.ORACLE_WALLET_LOCATION, + wallet_password=dify_config.ORACLE_WALLET_PASSWORD, + is_autonomous=dify_config.ORACLE_IS_AUTONOMOUS, ), ) diff --git a/api/tests/integration_tests/vdb/oracle/test_oraclevector.py b/api/tests/integration_tests/vdb/oracle/test_oraclevector.py index 3252b04276..76e8b7bccd 100644 --- a/api/tests/integration_tests/vdb/oracle/test_oraclevector.py +++ b/api/tests/integration_tests/vdb/oracle/test_oraclevector.py @@ -13,11 +13,9 @@ class OracleVectorTest(AbstractVectorTest): self.vector = OracleVector( collection_name=self.collection_name, config=OracleVectorConfig( - host="localhost", - port=1521, user="dify", password="dify", - database="FREEPDB1", + dsn="localhost:1521/FREEPDB1", ), ) diff --git a/docker/.env.example b/docker/.env.example index afd2caf501..438af9fc52 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -483,11 +483,13 @@ CHROMA_AUTH_PROVIDER=chromadb.auth.token_authn.TokenAuthClientProvider CHROMA_AUTH_CREDENTIALS= # Oracle configuration, only available when VECTOR_STORE is `oracle` -ORACLE_HOST=oracle -ORACLE_PORT=1521 ORACLE_USER=dify ORACLE_PASSWORD=dify -ORACLE_DATABASE=FREEPDB1 +ORACLE_DSN=oracle:1521/FREEPDB1 +ORACLE_CONFIG_DIR=/app/api/storage/wallet +ORACLE_WALLET_LOCATION=/app/api/storage/wallet +ORACLE_WALLET_PASSWORD=dify +ORACLE_IS_AUTONOMOUS=false # relyt configurations, only available when VECTOR_STORE is `relyt` RELYT_HOST=db diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index b8a239076d..72a615263f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -197,11 +197,13 @@ x-shared-env: &shared-api-worker-env CHROMA_DATABASE: ${CHROMA_DATABASE:-default_database} CHROMA_AUTH_PROVIDER: ${CHROMA_AUTH_PROVIDER:-chromadb.auth.token_authn.TokenAuthClientProvider} CHROMA_AUTH_CREDENTIALS: ${CHROMA_AUTH_CREDENTIALS:-} - ORACLE_HOST: ${ORACLE_HOST:-oracle} - ORACLE_PORT: ${ORACLE_PORT:-1521} ORACLE_USER: ${ORACLE_USER:-dify} ORACLE_PASSWORD: ${ORACLE_PASSWORD:-dify} - ORACLE_DATABASE: ${ORACLE_DATABASE:-FREEPDB1} + ORACLE_DSN: ${ORACLE_DSN:-oracle:1521/FREEPDB1} + ORACLE_CONFIG_DIR: ${ORACLE_CONFIG_DIR:-/app/api/storage/wallet} + ORACLE_WALLET_LOCATION: ${ORACLE_WALLET_LOCATION:-/app/api/storage/wallet} + ORACLE_WALLET_PASSWORD: ${ORACLE_WALLET_PASSWORD:-dify} + ORACLE_IS_AUTONOMOUS: ${ORACLE_IS_AUTONOMOUS:-false} RELYT_HOST: ${RELYT_HOST:-db} RELYT_PORT: ${RELYT_PORT:-5432} RELYT_USER: ${RELYT_USER:-postgres} diff --git a/docker/startupscripts/init_user.script b/docker/startupscripts/init_user.script index 7aa7c28049..55e8510d2f 100755 --- a/docker/startupscripts/init_user.script +++ b/docker/startupscripts/init_user.script @@ -5,6 +5,6 @@ create user dify identified by dify DEFAULT TABLESPACE users quota unlimited on grant DB_DEVELOPER_ROLE to dify; BEGIN -CTX_DDL.CREATE_PREFERENCE('my_chinese_vgram_lexer','CHINESE_VGRAM_LEXER'); +CTX_DDL.CREATE_PREFERENCE('dify.multilingual_lexer','CHINESE_VGRAM_LEXER'); END; /