11"""
2- Copyright (c) 2023 , Oracle and/or its affiliates.
2+ Copyright (c) 2024 , Oracle and/or its affiliates.
33Copyright (c) 2020, Vitor Avancini
44
55 Licensed under the Apache License, Version 2.0 (the "License");
1616"""
1717import datetime
1818from typing import (
19- Optional , List , Set
19+ Optional , List , Set , FrozenSet , Tuple , Iterable
2020)
21- from itertools import chain
2221from typing import (
2322 Any ,
2423 Callable ,
2726import agate
2827import requests
2928
30- import dbt .exceptions
29+ import dbt_common .exceptions
30+ from dbt_common .contracts .constraints import ConstraintType
31+ from dbt_common .utils import filter_null_values
32+
33+ from dbt .adapters .base .connections import Connection
3134from dbt .adapters .base .relation import BaseRelation , InformationSchema
32- from dbt .adapters .base .impl import GET_CATALOG_MACRO_NAME , ConstraintSupport , GET_CATALOG_RELATIONS_MACRO_NAME , _expect_row_value
35+ from dbt .adapters .base .impl import ConstraintSupport , GET_CATALOG_RELATIONS_MACRO_NAME , _expect_row_value
36+ from dbt .adapters .contracts .relation import RelationConfig
37+ from dbt .adapters .events .logging import AdapterLogger
3338from dbt .adapters .sql import SQLAdapter
3439from dbt .adapters .base .meta import available
3540from dbt .adapters .capability import CapabilityDict , CapabilitySupport , Support , Capability
41+
3642from dbt .adapters .oracle import OracleAdapterConnectionManager
3743from dbt .adapters .oracle .column import OracleColumn
3844from dbt .adapters .oracle .relation import OracleRelation
39- from dbt .contracts .graph .manifest import Manifest
40- from dbt .contracts .graph .nodes import ConstraintType
41- from dbt .events import AdapterLogger
42-
43- from dbt .utils import filter_null_values
44-
4545from dbt .adapters .oracle .keyword_catalog import KEYWORDS
4646from dbt .adapters .oracle .python_submissions import OracleADBSPythonJob
47- from dbt .adapters .oracle .connections import AdapterResponse
47+
48+ from dbt_common .ui import warning_tag , yellow
4849
4950logger = AdapterLogger ("oracle" )
5051
8283LIST_RELATIONS_MACRO_NAME = 'list_relations_without_caching'
8384GET_DATABASE_MACRO_NAME = 'get_database_name'
8485
86+ MISSING_DATABASE_NAME_FOR_CATALOG_WARNING_MESSAGE = (
87+ "database key is missing from the target profile in the file profiles.yml "
88+ "\n Starting with dbt-oracle 1.8 database name is needed for catalog generation "
89+ "\n Without database key in the target profile the generated catalog will be empty "
90+ "\n i.e. `dbt docs generate` command will generate an empty catalog json "
91+ "\n Make the following entry in dbt profile.yml file for the target profile "
92+ "\n database: {0}"
93+ )
94+
8595
8696class OracleAdapter (SQLAdapter ):
8797 ConnectionManager = OracleAdapterConnectionManager
@@ -138,8 +148,8 @@ def verify_database(self, database):
138148 if database .startswith ('"' ):
139149 database = database .strip ('"' )
140150 expected = self .config .credentials .database
141- if expected and database .lower () != expected .lower ():
142- raise dbt .exceptions .DbtRuntimeError (
151+ if expected and database .lower () != 'none' and database . lower () != expected .lower ():
152+ raise dbt_common .exceptions .DbtRuntimeError (
143153 'Cross-db references not allowed in {} ({} vs {})'
144154 .format (self .type (), database , expected )
145155 )
@@ -206,71 +216,52 @@ def get_relation(self, database: str, schema: str, identifier: str) -> Optional[
206216 database = self .config .credentials .database
207217 return super ().get_relation (database , schema , identifier )
208218
209- def _get_one_catalog (
219+ def _get_one_catalog_by_relations (
210220 self ,
211221 information_schema : InformationSchema ,
212- schemas : Set [str ],
213- manifest : Manifest ,
214- ) -> agate .Table :
215-
216- kwargs = {"information_schema" : information_schema , "schemas" : schemas }
217- table = self .execute_macro (
218- GET_CATALOG_MACRO_NAME ,
219- kwargs = kwargs ,
220- # pass in the full manifest so we get any local project
221- # overrides
222- manifest = manifest ,
223- )
224- # In case database is not defined, we can use the the configured database which we set as part of credentials
225- for node in chain (manifest .nodes .values (), manifest .sources .values ()):
226- if not node .database or node .database == 'None' :
227- node .database = self .config .credentials .database
228-
229- results = self ._catalog_filter_table (table , manifest )
230- return results
231-
232- def _get_one_catalog_by_relations (
233- self ,
234- information_schema : InformationSchema ,
235- relations : List [BaseRelation ],
236- manifest : Manifest ,
237- ) -> agate .Table :
238-
222+ relations : List [BaseRelation ],
223+ used_schemas : FrozenSet [Tuple [str , str ]],
224+ ) -> "agate.Table" :
239225 kwargs = {
240226 "information_schema" : information_schema ,
241227 "relations" : relations ,
242228 }
243- table = self .execute_macro (
244- GET_CATALOG_RELATIONS_MACRO_NAME ,
245- kwargs = kwargs ,
246- # pass in the full manifest, so we get any local project
247- # overrides
248- manifest = manifest ,
249- )
250-
251- # In case database is not defined, we can use the the configured database which we set as part of credentials
252- for node in chain (manifest .nodes .values (), manifest .sources .values ()):
253- if not node .database or node .database == 'None' :
254- node .database = self .config .credentials .database
255-
256- results = self ._catalog_filter_table (table , manifest ) # type: ignore[arg-type]
229+ table = self .execute_macro (GET_CATALOG_RELATIONS_MACRO_NAME , kwargs = kwargs )
230+ results = self ._catalog_filter_table (table , used_schemas ) # type: ignore[arg-type]
257231 return results
258232
259233 def get_filtered_catalog (
260- self , manifest : Manifest , relations : Optional [Set [BaseRelation ]] = None
234+ self ,
235+ relation_configs : Iterable [RelationConfig ],
236+ used_schemas : FrozenSet [Tuple [str , str ]],
237+ relations : Optional [Set [BaseRelation ]] = None
261238 ):
262239 catalogs : agate .Table
240+
241+ def is_database_none (database ):
242+ return database is None or database == 'None'
243+
244+ def populate_database (database ):
245+ if not is_database_none (database ):
246+ return database
247+ return self .config .credentials .database
248+
249+ # In case database is not defined, we can use database set in credentials object
250+ if any (is_database_none (database ) for database , schema in used_schemas ):
251+ used_schemas = frozenset ([(populate_database (database ).casefold (), schema )
252+ for database , schema in used_schemas ])
253+
263254 if (
264255 relations is None
265256 or len (relations ) > 100
266257 or not self .supports (Capability .SchemaMetadataByRelations )
267258 ):
268259 # Do it the traditional way. We get the full catalog.
269- catalogs , exceptions = self .get_catalog (manifest )
260+ catalogs , exceptions = self .get_catalog (relation_configs , used_schemas )
270261 else :
271262 # Do it the new way. We try to save time by selecting information
272263 # only for the exact set of relations we are interested in.
273- catalogs , exceptions = self .get_catalog_by_relations (manifest , relations )
264+ catalogs , exceptions = self .get_catalog_by_relations (used_schemas , relations )
274265
275266 if relations and catalogs :
276267 relation_map = {
@@ -388,8 +379,8 @@ def quote_seed_column(
388379 elif quote_config is None :
389380 pass
390381 else :
391- raise dbt .exceptions .CompilationError (f'The seed configuration value of "quote_columns" '
392- f'has an invalid type { type (quote_config )} ' )
382+ raise dbt_common .exceptions .CompilationError (f'The seed configuration value of "quote_columns" '
383+ f'has an invalid type { type (quote_config )} ' )
393384
394385 if quote_columns :
395386 return self .quote (column )
@@ -417,7 +408,7 @@ def render_raw_columns_constraints(cls, raw_columns: Dict[str, Dict[str, Any]])
417408
418409 def get_oml_auth_token (self ) -> str :
419410 if self .config .credentials .oml_auth_token_uri is None :
420- raise dbt .exceptions .DbtRuntimeError ("oml_auth_token_uri should be set to run dbt-py models" )
411+ raise dbt_common .exceptions .DbtRuntimeError ("oml_auth_token_uri should be set to run dbt-py models" )
421412 data = {
422413 "grant_type" : "password" ,
423414 "username" : self .config .credentials .user ,
@@ -428,7 +419,7 @@ def get_oml_auth_token(self) -> str:
428419 json = data )
429420 r .raise_for_status ()
430421 except requests .exceptions .RequestException :
431- raise dbt .exceptions .DbtRuntimeError ("Error getting OML OAuth2.0 token" )
422+ raise dbt_common .exceptions .DbtRuntimeError ("Error getting OML OAuth2.0 token" )
432423 else :
433424 return r .json ()["accessToken" ]
434425
@@ -458,3 +449,15 @@ def submit_python_job(self, parsed_model: dict, compiled_code: str):
458449 response , _ = self .execute (sql = py_q_drop_script )
459450 logger .info (response )
460451 return response
452+
453+ def acquire_connection (self , name = None ) -> Connection :
454+ connection = self .connections .set_connection_name (name )
455+ if connection .credentials .database is None or connection .credentials .database .lower () == 'none' :
456+ with connection .handle .cursor () as cr :
457+ cr .execute ("select SYS_CONTEXT('userenv', 'DB_NAME') FROM DUAL" )
458+ r = cr .fetchone ()
459+ database = r [0 ]
460+ logger .warning (warning_tag (yellow (MISSING_DATABASE_NAME_FOR_CATALOG_WARNING_MESSAGE .format (database ))))
461+ self .config .credentials .database = database
462+ connection .credentials .database = database
463+ return connection
0 commit comments