the you email it to me as an attachament . the indentation is messed
up and hard to read.

Massimo

On Jun 13, 12:21 am, Mariano Mara <mariano.m...@gmail.com> wrote:
> Hi everyone.
>
>  I'm currently trying to migrate a legacy oracle database to web2py and
> on the past weeks I stumbled upon several issues related to how the database
> was originally designed and how web2py expects to interact with it.
>
> Among them:
> * I need support for ref cursors
> * I need to write some complex sql queries that rely on oracle's
>   advanced functions
> * A "bug" (??) on the web2py oracle's implementation (see
>  http://tinyurl.com/kjs2pk)
>
> I wanted to be able to interact freely between DAL objects and ad hocs
> queries using web2py's ORM syntax. I came up with a subclassed version of 
> SQLDB that
> so far helped me with this and I wanted to share it with everyone hoping 
> somebody
> might find it useful.  This might be redundant, 
> soon-to-be-or-already-obsolete (I have
> high hopes on new DALs although I couldn't try it yet)or just a showcase of 
> bad programming
> but since I already spent a few hours with it, I thought I can share :)
>
> Put it simple, what I did was to create a decorator that, applied to a
> method that returns a list of tuples, will convert the result to a SQLRows 
> object mapped to
> a "virtual" table.
> This "virtual" table (one for manual queries, one for ref cursors) will
> hold the results until a new ad-hoc query is executed. Of course you cannot 
> use them to
> update, delete or insert any records in the db, but if you have a special 
> query and you
> don't want to loose DAL's syntax when iteracting with it, this might help 
> you. People using
> a database other than oracle can make it work with minimal patching.
> Following, a full example of a refcursor (in case you don't know what it
> is), an ad-hoc query example. Finally, the proper code to make it work.
>
> --- models/db.py
>
> db.define_table("Food", db.Field("name", type='string', unique=True),
>                         db.Field("fav_food", type='string'))
> db.Food.insert(name='Mariano', fav_food='spaghettini carbonara')
> db.Food.insert(name='Gregorio', fav_food='hot dogs')
> db.Food.insert(name='Justina', fav_food='hot dogs')
> db.commit()
>
> --- store procedure returning a ref cursor:
>
> create procedure test_food (food_name in food.fav_food%type,
>                             rc_out out sys_refcursor)
> is
> begin
>  open rc_out for
>    select * from food where fav_food=food_name;
>
> end test_food;
> /
>
> -- in action:
> KAL> variable rc refcursor
> KAL> begin test_food('hot dogs', :rc); end;
>   2  /
>
> PL/SQL procedure successfully completed.
>
> KAL> print rc
>          2 Gregorio hot dogs
>
>          3 Justina hot dogs
>
> -- accesing it from web2py itself (ipython) and getting SQLRows object:
> In [8]: rows = db.executerc("test_food", ['hot dogs'])
>
> In [9]: for row in rows: print(row.NAME, row.FAV_FOOD)
>    ....:
> ('Gregorio', 'hot dogs')
> ('Justina', 'hot dogs')
>
> -- executing ad hoc queries with executesql and getting SQLRows object:
> In [10]: rows = db.executesql("Select name, fav_food from food where
> name like 'Mar%'")
>
> In [11]: for row in rows: print row
>    ...:
> <SQLStorage {'FAV_FOOD': 'spaghettini carbonara', 'NAME': 'Mariano'}>
>
> In [12]: type(rows)
> Out[12]: <class 'gluon.sql.SQLRows'>
>
> -- my modified db.py
> from gluon.sql import SQLDB, SQLRows
>
> # not sure why I imported these but here they are
> from cx_Oracle import BFILE, BINARY, BLOB, CLOB, DATETIME, FIXED_CHAR,
> LOB
> from cx_Oracle import LONG_BINARY, LONG_STRING, NATIVE_FLOAT, NCLOB,
> NUMBER
> from cx_Oracle import STRING, TIMESTAMP
>
> # this is the inverse version of SQL_DIALECTS in gluon/sql.py
> # we're mapping oracle's datatypes to web2py's
> oracle_dialect = dict(
>     NUMBER='integer',
>     STRING='string',
>     DATETIME='datetime',
>     BLOB='blob',
>     CLOB='text',
>     FIXED_CHAR='boolean',
>     NATIVE_FLOAT='double',
>     TIMESTAMP='datetime')
>
> class mySQLDB(SQLDB):
>
>     def __init__(self, uri='sqlite://dummy.db', pool_size=0, pools=0):
>         super(mySQLDB, self).__init__(uri, pool_size, pools)
>
>     def buildSQLRow(fnc):
>         """Decorator to return a SQLRows object.
>         It will receive the result of the query, transform the db native
>         datatypes into ones understood by DAL and return the result"""
>
>         def wrapper(self, *args):
>             rt1, rt2, rt3 = fnc(self, *args)
>             pargs = []
>             try:
>                 self.pop(rt3)
>                 self['tables'].remove(rt3)
>             except KeyError:
>                 pass
>             for ev in rt2:
>                 pargs.append(SQLField(ev[0],
>                                   oracle_dialect[ev[1].__name__]))
>             self.define_table(rt3, *tuple(pargs), **dict(migrate=False))
>             return SQLRows(self, rt1, *map(lambda x: "%s.%s" % (rt3,
> x[0]),
>                                      rt2))
>         return wrapper
>
>     @buildSQLRow
>     def executesql(self, query, *args):
>         """Execute a query and return the result. If you have two
> columns with the
>         same name, make sure you create different aliases for them"""
>         self['_lastsql'] = query
>         self._cursor.execute(query, args)
>         return self._cursor.fetchall(), self._cursor.description,
> 'manquery'
>
>     @buildSQLRow
>     def executerc(self, st_name, parameters):
>         """Execute a procedure with a ref cursor as result.
>         In the procedure, the ref cursor output parameter must be the
> last one
>         parameter and the only one out value
>         Parameters:
>             st_name: the name of the [package.]procedure to execute
>             parameters: a list of parameters to pass to the procedure.
> You
>             don't need to pass a ref cursor variable as the method will
> append
>             it by default"""
>         rc = self._connection.cursor()
>         parameters.append(rc)
>         self._cursor.callproc(st_name, parameters)
>         return rc.fetchall(), rc.description, 'refcursor'
>
> db = mySQLDB('oracle://kal/aldos...@myora')
>
> # web2py models stuff...
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"web2py Web Framework" group.
To post to this group, send email to web2py@googlegroups.com
To unsubscribe from this group, send email to 
web2py+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/web2py?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to