Skip to main content

Extension to cx_Oracle to handle Oracle object types

Project description

cy_Oracle is a simple Python module that improves the handling of Oracle object types compared to cx_Oracle. cy_Oracle supports a central declaration of a mapping between Python classes and Oracle object types. This mapping can be used in DML (SELECT, UPDATE, INSERT) as well as with PL/SQL functions and procedures. Details are described in the online help for the function “addTypemap”.

>>> import cy_Oracle
>>> help(cy_Oracle.OracleDB.addTypemap)

The entry point for the client is the class cy_Oracle.OracleDB. Here you define the connection parameter to the Oracle database (User, Password, URL).

>>> import cy_Oracle
>>> help(cy_Oracle.OracleDB) for details.

The class cy_Oracle.Connection derives from cx_Oracle.Connection. It does not add any new methods, but override the method “cursor()”. Here we return an object of the class cy_Oracle.Cursor.

>>> import cy_Oracle
>>> help(cy_Oracle.Connection.cursor) for details.

The class cy_Oracle.Cursor derives from cx_Oracle.Cursor. Here we setup the input and output handlers to map between Oracle object types and Python classes.

>>> import cy_Oracle
>>> help(cy_Oracle.Cursor) for details.

Example 1

This ‘Hello World’ example for the module cy_Oracle will list the names and the creation date of objects in the database schema “book”:

1    from cy_Oracle import OracleDB
2    # setup the DB connection
3    myDB = OracleDB(user='book', password='book', databaseURL='bookdb')
4    # connect to DB
5    con = myDB.connect()
6    # select some data from the view 'USER_OBJECTS'
7    cur = con.cursor()
8    cur.execute('SELECT object_name, created from user_objects')
9    # loop the result
10   for row in cur:
11           print ('%s --> %s' % (row.object_name, row.created))
12   # close down
13   cur.close()
14   con.close()

In this example, cy_Oracle works just as cx_Oracle - the only difference is line 11. Here, cy_Oracle gives you an object with names of the selected columns as attributes whereas in cx_Oracle you will just get a collection.

Example 2

Oracle object types are mostly used in complex database application that are build from a mixture of SQL and PL/SQL. So, this “simple” example using object types is one that is not very useful. It will list all columns of all tables an views in the database schema “book”:

1   from cy_Oracle import OracleDB
2   from cy_Oracle import Attribute
3
4   # class to receive the result - one object of type T_COL_INFO.
5   class ColInfo(object):
6           def __init__(self):
7                   self.name=None
8                   self.oracleDataType=None
9                   self.oracleDataLenght=None
10
11         def __str__(self):
12                 return 'name=%s type=%s(%d)' % \
13                         (self.name,self.oracleDataType,self.oracleDataLenght)
14
15  myDB = OracleDB(user='book', password='book', databaseURL='orcl')
16  con = myDB.connect()
17  cur = con.cursor()
18  # Create a new object type in the database
19  cur.execute(""" create or replace type T_COL_INFO as object(
20                   NAME           varchar2(85 char)
21                  ,DATA_TYPE      varchar2(106 char)
22                  ,DATA_LENGTH    number
23                  ,COMMENTS       varchar2(4000 char))
24              """)
25  # create a view containing objects of the type T_COL_INFO
26  cur.execute(""" create or replace view V_COL_INFO as
27                    select
28                       c.TABLE_NAME as TABLE_NAME
29                      ,T_COL_INFO(c.COLUMN_NAME,c.DATA_TYPE,c.DATA_LENGTH,m.COMMENTS)
30                       as COL_INFO
31                    from USER_TAB_COLUMNS c ,USER_COL_COMMENTS m
32                    where c.COLUMN_NAME = m.COLUMN_NAME and c.TABLE_NAME = m.TABLE_NAME
33              """)
34  # Create a type-mapping between T_COL_INFO and the Python object ColInfo
35  myDB.addTypemap(oracleTypename='T_COL_INFO',pythonClass=ColInfo,attrDict={
36          'name'                    : Attribute('NAME'),
37          'oracleDataType'          : Attribute('DATA_TYPE'),
38          'oracleDataLenght'        : Attribute('DATA_LENGTH'),
39          })
40  # select some objects from the database
41  for row in cur.execute("""  select TABLE_NAME,COL_INFO from V_COL_INFO order by 1 """):
42   print 'Table::%s "%s"::%s' % \
43   (row.table_name,row.col_info.__class__.__name__,row.col_info)
43  cur.close()
44  con.close()

First of all, we need a Python class to receive the data from the Oracle object type. In line 5, we create the class “ColInfo”. To use the class with cy_Oracle, it needs a constructor without parameters an “object” somewhere in its base classes.

Now, we need an Oracle object type. We are using the DDL-statement in line 19 to create the type T_COL_INFO. It stores information about one colunm of a table or a view in the database.

Next, we want to be able to receive objects of the type T_COL_INFO from the database. In this example, we are creating the database view V_COL_INFO that contains this object in one of its columns - see line 26.

The interesting part of this example the code in line 35. In cx_Oracle, a SELECT from the view V_COL_INFO would return an object of the Python class cx_Oracle.Object. This is a generic class, used for all Oracle object types with the following advantages and disadvantages:

  • it is generic and contains all attributes of the Oracle object

  • all instances become inaccessable when closing the database connection

  • you can not derive your own Python class from cx_Oracle.Object

With the method “addTypemap” we tell cy_Oracle that:

  • we want an object of the Python class “ColInfo” each time a Oracle object of the object type “T_COL_INFO” shows up

  • we want an Oracle object of the object type “T_COL_INFO” each time a Python object of the class “ColInfo” shows up

  • we tell cy_Oracle how the attributes of the two types are mapped.

The output from the example depends on the objects in your database schema, but it should be something like this:

Table::BOOK "ColInfo"::name=BK_ISBN type=NUMBER(22)
Table::BOOK "ColInfo"::name=BK_BOOK type=T_BOOK(1)

It shows, that:

  • we got an object of the Python class “ColInfo” for each entry in view “V_COL_INFO”.

  • We can add or override methods in “ColInfo” as usual

Example 3

Oracle object types can be build as a composition using other object types. The following example shows how cy_Oracle handles compositions and collections of objects.

We are extending the example 2 to get one Python object for each table in our schema containing:

  • the name of the table

  • the remaining free space in the table

  • a collection of “ColInfo” (see Example 2) objects describing each of the columns.

The exmple also shows that cy_Oracle is able to deal with composit Object object types:

1  """ Example using Oracle Object Types
2
3  This example shows how to map composit
4  Oracle object types as well collections
5  to Python objects using cy_Oracle.
6  """
7  from cy_Oracle import OracleDB
8  from cy_Oracle import Attribute
9  from cy_Oracle import Collection
10
11  # class to receive the result - one object of type T_COL_INFO.
12  class ColInfo(object):
13     def __init__(self):
14             self.name=None
15             self.oracleDataType=None
16             self.oracleDataLenght=None
17     def __str__(self):
18             return 'name=%s type=%s(%d)' % \
19                     (self.name,self.oracleDataType,self.oracleDataLenght)
20
21  # Class the receive the result - one object of type T_TAB_INFO
22  class TabInfo(object):
23     def __init__(self):
24             self.name=None
25             self.pctFree=None
26             self.colInfo=None
27     def __str__(self):
28             result = 'name=%s free=%d(%%)' % (self.name,self.pctFree)
29             for col in self.colInfo:
30                     result = '%s\n    %s' % (result,col)
31             return result
32
33  myDB = OracleDB(user='book', password='book', databaseURL='bookdb')
34  con = myDB.connect()
35  cur = con.cursor()
36
37  # to be able to run this program more then once we must drop the existing types
38  try:
39     cur.execute(""" drop type T_TAB_INFO """)
40  except:
41     pass
42  try:
43     cur.execute(""" drop type T_COL_INFO_NT """)
44  except:
45     pass
46
47  # Create a object type in the database
48  cur.execute(""" create or replace type T_COL_INFO as object(
49              NAME           varchar2(85 char)
50             ,DATA_TYPE      varchar2(106 char)
51             ,DATA_LENGTH    number
52             ,COMMENTS       varchar2(4000 char))
53         """)
54  # Create a collection type in the database
55  cur.execute(""" create or replace type T_COL_INFO_NT as table of T_COL_INFO """)
56  # Create the type that describes one table
57  cur.execute(""" create or replace type T_TAB_INFO as object(
58              NAME varchar2(30 char),
59              PCT_FREE number,
60              COL_INFO T_COL_INFO_NT)
61         """)
62  # create a view containing objects of the type T_COL_INFO
63  cur.execute(""" create or replace view V_TAB_INFO as
64                  select T_TAB_INFO(
65                     t.TABLE_NAME
66                    ,t.PCT_FREE
67                    ,cast(MULTISET(
68                     SELECT T_COL_INFO(c.COLUMN_NAME,c.DATA_TYPE,c.DATA_LENGTH
69                     ,m.COMMENTS) from
70                     USER_TAB_COLUMNS c,USER_COL_COMMENTS m
71                     where c.TABLE_NAME=t.TABLE_NAME and c.TABLE_NAME=m.TABLE_NAME)
72                     as T_COL_INFO_NT )
73                  ) as TAB_INFO
74                  from user_tables t
75              """)
76
77  # Create a type-mapping between T_COL_INFO and the Python object ColInfo
78  myDB.addTypemap(oracleTypename='T_COL_INFO',pythonClass=ColInfo,attrDict={
79     'name'                  : Attribute('NAME'),
80     'oracleDataType'        : Attribute('DATA_TYPE'),
81     'oracleDataLenght'      : Attribute('DATA_LENGTH'),
82     })
83  # Create a type-mapping between T_TAB_INFO and the Python object ColInfo
84  myDB.addTypemap(oracleTypename='T_TAB_INFO',pythonClass=TabInfo,attrDict={
85     'name'                  : Attribute('NAME'),
86     'pctFree'               : Attribute('PCT_FREE'),
87     'colInfo'               : Collection('COL_INFO','T_COL_INFO_NT')
88     })
89
90  # select some objects from the database
91  for row in cur.execute("""  select TAB_INFO  from V_TAB_INFO """):
92     print '\n%s' %  (row.tab_info)
93  cur.close()
94  con.close()

The output from the example depends on the objects in your database schema, but it should be something like this:

name=BOOK free=10(%)
    name=BK_BOOK type=T_BOOK(1)
    name=BK_ISBN type=NUMBER(22)
    name=BK_BOOK type=T_BOOK(1)
    name=BK_ISBN type=NUMBER(22)
...
...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cy_Oracle-0.3.1.tar.gz (17.6 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page