Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Oracle] Add port to DSN #15589

Merged
merged 8 commits into from May 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 26 additions & 8 deletions airflow/providers/oracle/hooks/oracle.py
Expand Up @@ -51,12 +51,24 @@ def get_conn(self) -> 'OracleHook':
(from the Oracle names server or tnsnames.ora file)
or is a string like the one returned from makedsn().

:param dsn: the host address for the Oracle server
:param dsn: the data source name for the Oracle server
:param service_name: the db_unique_name of the database
that you are connecting to (CONNECT_DATA part of TNS)
:param sid: Oracle System ID that identifies a particular
database on a system

You can set these parameters in the extra fields of your connection
as in ``{ "dsn":"some.host.address" , "service_name":"some.service.name" }``
as in

.. code-block:: python

{
"dsn": (
"(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)"
"(HOST=host)(PORT=1521))(CONNECT_DATA=(SID=sid)))"
)
}

see more param detail in
`cx_Oracle.connect <https://cx-oracle.readthedocs.io/en/latest/module.html#cx_Oracle.connect>`_

Expand All @@ -66,18 +78,24 @@ def get_conn(self) -> 'OracleHook':
self.oracle_conn_id # type: ignore[attr-defined] # pylint: disable=no-member
)
conn_config = {'user': conn.login, 'password': conn.password}
dsn = conn.extra_dejson.get('dsn')
sid = conn.extra_dejson.get('sid')
mod = conn.extra_dejson.get('module')

service_name = conn.extra_dejson.get('service_name')
port = conn.port if conn.port else 1521
if dsn and sid and not service_name:
conn_config['dsn'] = cx_Oracle.makedsn(dsn, port, sid)
elif dsn and service_name and not sid:
conn_config['dsn'] = cx_Oracle.makedsn(dsn, port, service_name=service_name)
if conn.host and sid and not service_name:
conn_config['dsn'] = cx_Oracle.makedsn(conn.host, port, sid)
elif conn.host and service_name and not sid:
conn_config['dsn'] = cx_Oracle.makedsn(conn.host, port, service_name=service_name)
else:
conn_config['dsn'] = conn.host
dsn = conn.extra_dejson.get('dsn')
if dsn is None:
dsn = conn.host
if conn.port is not None:
dsn += ":" + str(conn.port)
if service_name or conn.schema:
dsn += "/" + (service_name or conn.schema)
conn_config['dsn'] = dsn

if 'encoding' in conn.extra_dejson:
conn_config['encoding'] = conn.extra_dejson.get('encoding')
Expand Down
32 changes: 13 additions & 19 deletions docs/apache-airflow-providers-oracle/connections/oracle.rst
Expand Up @@ -25,25 +25,17 @@ The Oracle connection type provides connection to a Oracle database.

Configuring the Connection
--------------------------
Dsn (required)
The Data Source Name. The host address for the Oracle server.

Host(optional)
Connect descriptor string for the data source name.
Host (optional)
The host to connect to.

Sid (optional)
The Oracle System ID. The uniquely identify a particular database on a system.
Schema (optional)
Specify the schema name to be used in the database.

Service_name (optional)
The db_unique_name of the database.

Port (optional)
The port for the Oracle server, Default ``1521``.

Login (required)
Login (optional)
Specify the user name to connect.

Password (required)
Password (optional)
Specify the password to connect.

Extra (optional)
Expand All @@ -64,8 +56,10 @@ Extra (optional)
which are defined at the module level, Default mode is connecting.
* ``purity`` - one of ``new``, ``self``, ``default``. Specify the session acquired from the pool.
configuration parameter.
* ``dsn``. Specify a Data Source Name (and ignore Host).
* ``sid`` or ``service_name``. Use to form DSN instead of Schema.

Connect using Dsn and Sid, Dsn and Service_name, or only Host `(OracleHook.getconn Documentation) <https://airflow.apache.org/docs/apache-airflow-providers-oracle/stable/_modules/airflow/providers/oracle/hooks/oracle.html#OracleHook.get_conn>`_.
Connect using `dsn`, Host and `sid`, Host and `service_name`, or only Host `(OracleHook.getconn Documentation) <https://airflow.apache.org/docs/apache-airflow-providers-oracle/stable/_modules/airflow/providers/oracle/hooks/oracle.html#OracleHook.get_conn>`_.

For example:

Expand All @@ -77,15 +71,15 @@ Extra (optional)

.. code-block:: python

Dsn = "dbhost.example.com"
Service_name = "orclpdb1"
Host = "dbhost.example.com"
Schema = "orclpdb1"

or

.. code-block:: python

Dsn = "dbhost.example.com"
Sid = "orcl"
Host = "dbhost.example.com"
Schema = "orcl"


More details on all Oracle connect parameters supported can be found in `cx_Oracle documentation
Expand Down
25 changes: 19 additions & 6 deletions tests/providers/oracle/hooks/test_oracle.py
Expand Up @@ -39,7 +39,9 @@ class TestOracleHookConn(unittest.TestCase):
def setUp(self):
super().setUp()

self.connection = Connection(login='login', password='password', host='host', port=1521)
self.connection = Connection(
login='login', password='password', host='host', schema='schema', port=1521
)

self.db_hook = OracleHook()
self.db_hook.get_connection = mock.Mock()
Expand All @@ -53,28 +55,39 @@ def test_get_conn_host(self, mock_connect):
assert args == ()
assert kwargs['user'] == 'login'
assert kwargs['password'] == 'password'
assert kwargs['dsn'] == 'host'
assert kwargs['dsn'] == 'host:1521/schema'

@mock.patch('airflow.providers.oracle.hooks.oracle.cx_Oracle.connect')
def test_get_conn_host_alternative_port(self, mock_connect):
self.connection.port = 1522
self.db_hook.get_conn()
assert mock_connect.call_count == 1
args, kwargs = mock_connect.call_args
assert args == ()
assert kwargs['user'] == 'login'
assert kwargs['password'] == 'password'
assert kwargs['dsn'] == 'host:1522/schema'

@mock.patch('airflow.providers.oracle.hooks.oracle.cx_Oracle.connect')
def test_get_conn_sid(self, mock_connect):
dsn_sid = {'dsn': 'dsn', 'sid': 'sid'}
dsn_sid = {'dsn': 'ignored', 'sid': 'sid'}
self.connection.extra = json.dumps(dsn_sid)
self.db_hook.get_conn()
assert mock_connect.call_count == 1
args, kwargs = mock_connect.call_args
assert args == ()
assert kwargs['dsn'] == cx_Oracle.makedsn(dsn_sid['dsn'], self.connection.port, dsn_sid['sid'])
assert kwargs['dsn'] == cx_Oracle.makedsn("host", self.connection.port, dsn_sid['sid'])

@mock.patch('airflow.providers.oracle.hooks.oracle.cx_Oracle.connect')
def test_get_conn_service_name(self, mock_connect):
dsn_service_name = {'dsn': 'dsn', 'service_name': 'service_name'}
dsn_service_name = {'dsn': 'ignored', 'service_name': 'service_name'}
self.connection.extra = json.dumps(dsn_service_name)
self.db_hook.get_conn()
assert mock_connect.call_count == 1
args, kwargs = mock_connect.call_args
assert args == ()
assert kwargs['dsn'] == cx_Oracle.makedsn(
dsn_service_name['dsn'], self.connection.port, service_name=dsn_service_name['service_name']
"host", self.connection.port, service_name=dsn_service_name['service_name']
)

@mock.patch('airflow.providers.oracle.hooks.oracle.cx_Oracle.connect')
Expand Down