Skip to content

Commit a9921bf

Browse files
Added support for setting the edition when connecting to Oracle
Database.
1 parent 791c399 commit a9921bf

15 files changed

+123
-25
lines changed

doc/src/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Thin Mode Changes
4646
operation.
4747
#) Added attribute :data:`PipelineOpResult.columns` to provide information
4848
about any query column metadata returned from a pipeline operation.
49+
#) Added support for setting the :ref:`edition <ebr>` when connecting to
50+
Oracle Database.
4951
#) Fixed bug causing some pooled connections to be permanently marked as busy
5052
and unavailable for reuse
5153
(`issue 392 <https://github.com/oracle/python-oracledb/issues/392>`__).

doc/src/user_guide/appendix_a.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ see :ref:`driverdiff` and :ref:`compatibility`.
183183
- Yes
184184
- Yes
185185
* - Edition Based Redefinition (EBR) (see :ref:`ebr`)
186-
- No - not at connect time. ALTER SESSION can be used.
186+
- Yes
187187
- Yes
188188
- Yes
189189
* - SQL execution (see :ref:`sqlexecution`)

doc/src/user_guide/plsql_execution.rst

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -422,38 +422,34 @@ or eliminating down time. This feature allows multiple versions of views,
422422
synonyms, PL/SQL objects and SQL Translation profiles to be used concurrently.
423423
Different versions of the database objects are associated with an "edition".
424424

425-
.. note::
426-
427-
Setting the Edition-Based Redefinition (EBR) edition at connection time is
428-
only supported in the python-oracledb Thick mode. See
429-
:ref:`enablingthick`. In python-oracledb Thin mode, the edition can be
430-
changed with ALTER SESSION after connecting.
431-
432-
The simplest way to set an edition is to pass the ``edition`` parameter to
433-
:meth:`oracledb.connect()` or :meth:`oracledb.create_pool()`:
425+
The simplest way to set the edition used by your applications is to pass the
426+
``edition`` parameter to :meth:`oracledb.connect()` or
427+
:meth:`oracledb.create_pool()`:
434428

435429
.. code-block:: python
436430
437431
connection = oracledb.connect(user="hr", password=userpwd,
438432
dsn="dbhost.example.com/orclpdb",
439-
edition="newsales", encoding="UTF-8")
433+
edition="newsales")
440434
441435
442-
The edition could also be set by setting the environment variable
443-
``ORA_EDITION`` or by executing the SQL statement:
436+
The edition can also be set by executing the SQL statement:
444437

445438
.. code-block:: sql
446439
447440
alter session set edition = <edition name>;
448441
449-
Regardless of which method is used to set the edition, the value that is in use
450-
can be seen by examining the attribute :attr:`Connection.edition`. If no value
451-
has been set, the value will be None. This corresponds to the database default
442+
You can also set the environment variable ``ORA_EDITION`` to your edition name.
443+
444+
Regardless of which method sets the edition, the value that is in use can be
445+
seen by examining the attribute :attr:`Connection.edition`. If no value has
446+
been set, the value will be None. This corresponds to the database default
452447
edition ``ORA$BASE``.
453448

454449
Consider an example where one version of a PL/SQL function ``Discount`` is
455450
defined in the database default edition ``ORA$BASE`` and the other version of
456-
the same function is defined in a user created edition ``DEMO``.
451+
the same function is defined in a user created edition ``DEMO``. In your SQL
452+
editor run:
457453

458454
.. code-block:: sql
459455
@@ -494,14 +490,13 @@ The ``Discount`` function for the demo edition is as follows:
494490
END;
495491
/
496492
497-
The Python application can then call the required version of the PL/SQL
498-
function as shown:
493+
A Python application can then call the required version of the PL/SQL function
494+
as shown:
499495

500496
.. code-block:: python
501497
502498
connection = oracledb.connect(user=user, password=password,
503-
dsn="dbhost.example.com/orclpdb",
504-
encoding="UTF-8")
499+
dsn="dbhost.example.com/orclpdb")
505500
print("Edition is:", repr(connection.edition))
506501
507502
cursor = connection.cursor()
@@ -511,7 +506,7 @@ function as shown:
511506
# Use the edition parameter for the connection
512507
connection = oracledb.connect(user=user, password=password,
513508
dsn="dbhost.example.com/orclpdb",
514-
edition="demo", encoding="UTF-8")
509+
edition="demo")
515510
print("Edition is:", repr(connection.edition))
516511
517512
cursor = connection.cursor()

samples/editioning.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -----------------------------------------------------------------------------
2-
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2016, 2024, Oracle and/or its affiliates.
33
#
44
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
55
#
@@ -41,8 +41,9 @@
4141
import oracledb
4242
import sample_env
4343

44-
# this script is currently only supported in python-oracledb thick mode
45-
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
44+
# determine whether to use python-oracledb thin mode or thick mode
45+
if not sample_env.get_is_thin():
46+
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
4647

4748
# connect to the editions user and create a procedure
4849
edition_connect_string = sample_env.get_edition_connect_string()

src/oracledb/impl/base/connect_params.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cdef class ConnectParamsImpl:
5555
self.description_list = DescriptionList()
5656
self.description_list.children.append(self._default_description)
5757
self.debug_jdwp = os.getenv("ORA_DEBUG_JDWP")
58+
self.edition = os.getenv("ORA_EDITION")
5859
address_list = AddressList()
5960
address_list.children.append(self._default_address)
6061
self._default_description.children.append(address_list)

src/oracledb/impl/thin/messages.pyx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,7 @@ cdef class AuthMessage(Message):
14581458
str machine
14591459
str osuser
14601460
str driver_name
1461+
str edition
14611462

14621463
cdef int _encrypt_passwords(self) except -1:
14631464
"""
@@ -1644,6 +1645,7 @@ cdef class AuthMessage(Message):
16441645
self.conn_impl.server_version = self._get_version_tuple(buf)
16451646
self.conn_impl.supports_bool = \
16461647
buf._caps.ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1
1648+
self.conn_impl._edition = self.edition
16471649

16481650
cdef int _set_params(self, ConnectParamsImpl params,
16491651
Description description) except -1:
@@ -1664,6 +1666,7 @@ cdef class AuthMessage(Message):
16641666
self.driver_name = params.driver_name
16651667
if self.driver_name is None:
16661668
self.driver_name = f"{DRIVER_NAME} thn : {DRIVER_VERSION}"
1669+
self.edition = params.edition
16671670

16681671
# if drcp is used, use purity = NEW as the default purity for
16691672
# standalone connections and purity = SELF for connections that belong
@@ -1765,6 +1768,8 @@ cdef class AuthMessage(Message):
17651768
num_pairs += 2
17661769
if self.encoded_jdwp_data is not None:
17671770
num_pairs += 1
1771+
if self.edition is not None:
1772+
num_pairs += 1
17681773

17691774
# write basic data to packet
17701775
self._write_function_code(buf)
@@ -1829,6 +1834,8 @@ cdef class AuthMessage(Message):
18291834
if self.encoded_jdwp_data is not None:
18301835
self._write_key_value(buf, "AUTH_ORA_DEBUG_JDWP",
18311836
self.encoded_jdwp_data)
1837+
if self.edition is not None:
1838+
self._write_key_value(buf, "AUTH_ORA_EDITION", self.edition)
18321839

18331840

18341841
@cython.final

tests/create_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
main_password=test_env.get_main_password(),
4848
proxy_user=test_env.get_proxy_user(),
4949
proxy_password=test_env.get_proxy_password(),
50+
edition_name=test_env.get_edition_name(),
5051
)
5152
if test_env.get_server_version() >= (21, 0):
5253
test_env.run_sql_script(

tests/drop_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def drop_schema(conn):
4242
"drop_schema",
4343
main_user=test_env.get_main_user(),
4444
proxy_user=test_env.get_proxy_user(),
45+
edition_name=test_env.get_edition_name(),
4546
)
4647

4748

tests/sql/create_schema.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ begin
7777
end;
7878
/
7979

80+
-- create edition
81+
create edition &edition_name
82+
/
83+
84+
grant use on edition &edition_name to &main_user
85+
/
86+
8087
-- create types
8188
create type &main_user..udt_SubObject as object (
8289
SubNumberValue number,

tests/sql/drop_schema.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,13 @@ begin
4040
execute immediate 'drop user ' || r.username || ' cascade';
4141
end loop;
4242

43+
for r in
44+
( select edition_name
45+
from dba_editions
46+
where edition_name in (upper('&edition_name'))
47+
) loop
48+
execute immediate 'drop edition ' || r.edition_name || ' cascade';
49+
end loop;
50+
4351
end;
4452
/

tests/test_1100_connection.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,32 @@ def hook(passed_protocol, passed_protocol_arg, passed_params):
915915
finally:
916916
oracledb.register_protocol(protocol, None)
917917

918+
def test_1154(self):
919+
"1154 - test altering connection edition"
920+
conn = test_env.get_connection()
921+
self.assertIsNone(conn.edition)
922+
cursor = conn.cursor()
923+
sql = "select sys_context('USERENV', 'CURRENT_EDITION_NAME') from dual"
924+
default_edition = "ORA$BASE"
925+
test_edition = test_env.get_edition_name()
926+
for edition in [test_edition, default_edition]:
927+
with self.subTest(edition=edition):
928+
cursor.execute(f"alter session set edition = {edition}")
929+
cursor.execute(sql)
930+
self.assertEqual(conn.edition, cursor.fetchone()[0])
931+
self.assertEqual(conn.edition, edition.upper())
932+
933+
def test_1155(self):
934+
"1155 - test connect() with edition"
935+
edition = test_env.get_edition_name()
936+
conn = test_env.get_connection(edition=edition)
937+
cursor = conn.cursor()
938+
cursor.execute(
939+
"select sys_context('USERENV', 'CURRENT_EDITION_NAME') from dual"
940+
)
941+
self.assertEqual(cursor.fetchone()[0], edition.upper())
942+
self.assertEqual(conn.edition, edition)
943+
918944

919945
if __name__ == "__main__":
920946
test_env.run_test_cases()

tests/test_2400_pool.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,13 @@ def hook(passed_protocol, passed_protocol_arg, passed_params):
979979
finally:
980980
oracledb.register_protocol(protocol, None)
981981

982+
def test_2448(self):
983+
"2448 - test create_pool() with edition"
984+
edition = test_env.get_edition_name()
985+
pool = test_env.get_pool(edition=edition)
986+
conn = pool.acquire()
987+
self.assertEqual(conn.edition, edition)
988+
982989

983990
if __name__ == "__main__":
984991
test_env.run_test_cases()

tests/test_5300_connection_async.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,34 @@ def hook(passed_protocol, passed_protocol_arg, passed_params):
690690
finally:
691691
oracledb.register_protocol(protocol, None)
692692

693+
async def test_5354(self):
694+
"5354 - test altering connection edition"
695+
conn = await test_env.get_admin_connection_async()
696+
self.assertIsNone(conn.edition)
697+
cursor = conn.cursor()
698+
sql = "select sys_context('USERENV', 'CURRENT_EDITION_NAME') from dual"
699+
default_edition = "ORA$BASE"
700+
test_edition = test_env.get_edition_name()
701+
for edition in [test_edition, default_edition]:
702+
with self.subTest(edition=edition):
703+
await cursor.execute(f"alter session set edition = {edition}")
704+
await cursor.execute(sql)
705+
(fetched_edition,) = await cursor.fetchone()
706+
self.assertEqual(fetched_edition, edition.upper())
707+
self.assertEqual(conn.edition, edition.upper())
708+
709+
async def test_5355(self):
710+
"5355 - test connect() with edition"
711+
edition = test_env.get_edition_name()
712+
conn = await test_env.get_connection_async(edition=edition)
713+
cursor = conn.cursor()
714+
await cursor.execute(
715+
"select sys_context('USERENV', 'CURRENT_EDITION_NAME') from dual"
716+
)
717+
(fetched_edition,) = await cursor.fetchone()
718+
self.assertEqual(fetched_edition, edition.upper())
719+
self.assertEqual(conn.edition, edition)
720+
693721

694722
if __name__ == "__main__":
695723
test_env.run_test_cases()

tests/test_5500_pool_async.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,14 @@ def hook(passed_protocol, passed_protocol_arg, passed_params):
581581
finally:
582582
oracledb.register_protocol(protocol, None)
583583

584+
async def test_5536(self):
585+
"5536 - test create_pool() with edition"
586+
edition = test_env.get_edition_name()
587+
pool = test_env.get_pool_async(edition=edition)
588+
async with pool.acquire() as conn:
589+
self.assertEqual(conn.edition, edition)
590+
await pool.close()
591+
584592

585593
if __name__ == "__main__":
586594
test_env.run_test_cases()

tests/test_env.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
# PYO_TEST_WALLET_PASSWORD: password for wallet file (thin mode, mTLS)
4242
# PYO_TEST_DRIVER_MODE: python-oracledb mode (thick or thin) to use
4343
# PYO_TEST_EXTERNAL_USER: user for testing external authentication
44+
# PYO_TEST_EDITION_NAME: name of edition for editioning tests
4445
#
4546
# PYO_TEST_CONNECT_STRING can be set to an Easy Connect string, or a
4647
# Net Service Name from a tnsnames.ora file or external naming service,
@@ -74,6 +75,7 @@
7475
DEFAULT_MAIN_USER = "pythontest"
7576
DEFAULT_PROXY_USER = "pythontestproxy"
7677
DEFAULT_CONNECT_STRING = "localhost/orclpdb1"
78+
DEFAULT_EDITION_NAME = "pythonedition"
7779

7880
# dictionary containing all parameters; these are acquired as needed by the
7981
# methods below (which should be used instead of consulting this dictionary
@@ -242,6 +244,10 @@ def get_connect_string():
242244
)
243245

244246

247+
def get_edition_name():
248+
return get_value("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME)
249+
250+
245251
def get_is_drcp():
246252
value = PARAMETERS.get("IS_DRCP")
247253
if value is None:

0 commit comments

Comments
 (0)