Skip to content

Commit 2c107d4

Browse files
authored
Merge pull request #146 from oracle/dev/v1.8.0
v1.8.0
2 parents 7cd6f7c + 2b74573 commit 2c107d4

File tree

17 files changed

+132
-119
lines changed

17 files changed

+132
-119
lines changed

.github/workflows/oracle-xe-adapter-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Install dbt-oracle with core dependencies
4949
run: |
5050
python -m pip install --upgrade pip
51-
pip install pytest 'dbt-tests-adapter~=1.7,<1.8'
51+
pip install pytest 'dbt-tests-adapter~=1.8,<1.9'
5252
pip install -r requirements.txt
5353
pip install -e .
5454

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Configuration variables
2-
VERSION=1.7.5
2+
VERSION=1.8.0
33
PROJ_DIR?=$(shell pwd)
44
VENV_DIR?=${PROJ_DIR}/.bldenv
55
BUILD_DIR=${PROJ_DIR}/build

dbt/adapters/oracle/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright (c) 2022, Oracle and/or its affiliates.
2+
Copyright (c) 2024, Oracle and/or its affiliates.
33
Copyright (c) 2020, Vitor Avancini
44
55
Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
1717
from dbt.adapters.oracle.connections import OracleAdapterConnectionManager
1818
from dbt.adapters.oracle.connections import OracleAdapterCredentials
1919
from dbt.adapters.oracle.impl import OracleAdapter
20-
2120
from dbt.adapters.base import AdapterPlugin
2221
from dbt.include import oracle
2322

dbt/adapters/oracle/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
See the License for the specific language governing permissions and
1515
limitations under the License.
1616
"""
17-
version = "1.7.14"
17+
version = "1.8.0"

dbt/adapters/oracle/connection_helper.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright (c) 2023, Oracle and/or its affiliates.
2+
Copyright (c) 2024, Oracle and/or its affiliates.
33
Copyright (c) 2020, Vitor Avancini
44
55
Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,10 @@
1717
import enum
1818
import os
1919

20-
import dbt.exceptions
21-
from dbt.events import AdapterLogger
20+
import dbt_common.exceptions
21+
from dbt.adapters.events.logging import AdapterLogger
2222

23-
from dbt.ui import warning_tag, yellow, red
23+
from dbt_common.ui import warning_tag, yellow, red
2424

2525
logger = AdapterLogger("oracle")
2626

@@ -129,5 +129,5 @@ class OracleDriverType(str, enum.Enum):
129129
SQLNET_ORA_CONFIG = OracleNetConfig.from_env()
130130
logger.info("Running in thin mode")
131131
else:
132-
raise dbt.exceptions.DbtRuntimeError("Invalid value set for ORA_PYTHON_DRIVER_TYPE\n"
133-
"Use any one of 'cx', 'thin', or 'thick'")
132+
raise dbt_common.exceptions.DbtRuntimeError("Invalid value set for ORA_PYTHON_DRIVER_TYPE\n"
133+
"Use any one of 'cx', 'thin', or 'thick'")

dbt/adapters/oracle/connections.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323
import uuid
2424
import platform
2525

26-
import dbt.exceptions
27-
from dbt.adapters.base import Credentials
26+
import dbt_common.exceptions
27+
from dbt.adapters.contracts.connection import AdapterResponse, Credentials
28+
from dbt.adapters.exceptions.connection import FailedToConnectError
2829
from dbt.adapters.sql import SQLConnectionManager
29-
from dbt.contracts.connection import AdapterResponse
30-
from dbt.events.functions import fire_event
31-
from dbt.events.types import ConnectionUsed, SQLQuery, SQLCommit, SQLQueryStatus
32-
from dbt.events import AdapterLogger
33-
from dbt.events.contextvars import get_node_info
34-
from dbt.utils import cast_to_str
30+
from dbt.adapters.events.types import ConnectionUsed, SQLQuery, SQLCommit, SQLQueryStatus
31+
from dbt.adapters.events.logging import AdapterLogger
32+
33+
from dbt_common.events.functions import fire_event
34+
from dbt_common.events.contextvars import get_node_info
35+
from dbt_common.utils import cast_to_str
3536

3637
from dbt.version import __version__ as dbt_version
3738
from dbt.adapters.oracle.connection_helper import oracledb, SQLNET_ORA_CONFIG
@@ -256,7 +257,7 @@ def open(cls, connection):
256257
connection.handle = None
257258
connection.state = 'fail'
258259

259-
raise dbt.exceptions.FailedToConnectError(str(e))
260+
raise FailedToConnectError(str(e))
260261

261262
return connection
262263

@@ -302,18 +303,18 @@ def exception_handler(self, sql):
302303
logger.info("Failed to release connection!")
303304
pass
304305

305-
raise dbt.exceptions.DbtDatabaseError(str(e).strip()) from e
306+
raise dbt_common.exceptions.DbtDatabaseError(str(e).strip()) from e
306307

307308
except Exception as e:
308309
logger.info("Rolling back transaction.")
309310
self.release()
310-
if isinstance(e, dbt.exceptions.DbtRuntimeError):
311+
if isinstance(e, dbt_common.exceptions.DbtRuntimeError):
311312
# during a sql query, an internal to dbt exception was raised.
312313
# this sounds a lot like a signal handler and probably has
313314
# useful information, so raise it without modification.
314315
raise e
315316

316-
raise dbt.exceptions.DbtRuntimeError(str(e)) from e
317+
raise dbt_common.exceptions.DbtRuntimeError(str(e)) from e
317318

318319
@classmethod
319320
def get_credentials(cls, credentials):

dbt/adapters/oracle/impl.py

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright (c) 2023, Oracle and/or its affiliates.
2+
Copyright (c) 2024, Oracle and/or its affiliates.
33
Copyright (c) 2020, Vitor Avancini
44
55
Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,8 @@
1616
"""
1717
import datetime
1818
from typing import (
19-
Optional, List, Set
19+
Optional, List, Set, FrozenSet, Tuple, Iterable
2020
)
21-
from itertools import chain
2221
from typing import (
2322
Any,
2423
Callable,
@@ -27,24 +26,26 @@
2726
import agate
2827
import 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
3134
from 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
3338
from dbt.adapters.sql import SQLAdapter
3439
from dbt.adapters.base.meta import available
3540
from dbt.adapters.capability import CapabilityDict, CapabilitySupport, Support, Capability
41+
3642
from dbt.adapters.oracle import OracleAdapterConnectionManager
3743
from dbt.adapters.oracle.column import OracleColumn
3844
from 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-
4545
from dbt.adapters.oracle.keyword_catalog import KEYWORDS
4646
from 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

4950
logger = AdapterLogger("oracle")
5051

@@ -82,6 +83,15 @@
8283
LIST_RELATIONS_MACRO_NAME = 'list_relations_without_caching'
8384
GET_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

8696
class 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

Comments
 (0)