On Mar 21, 2010, at 9:33 PM, Kevin Wormington wrote: > As a test I altered the compile_procedure and the call to cursor.callproc and > do get the values back from the stored procedure from the "print res" before > the return. I get exceptions about the cursor being close when I try to > access the ResultProxy object returned though. I think it's because the > callproc returns just the results and not a cursor/set.
ah well, the result proxy closes the cursor when there's no cursor.description, i.e. that theres no results to fetch. The workarounds are getting uglier here, but you can do this: result = cursor.callproc(<stuff>) context.callproc_result = result then on the outside when you get your result, context is there: result = conn.execute(myprocedure...) print result.context.callproc_result > > def compile_procedure(element, compiler, **kw): > return "%s" % (element.name) > > class ProcedureProxy(ConnectionProxy): > > def cursor_execute(self, execute, cursor, statement, parameters, context, > executemany): > """Intercept low-level cursor execute() events.""" > if context and isinstance(context.compiled.statement, procedure): > engine.logger.info("calling stored procedure: %s", statement) > if hasattr(cursor, 'callproc'): > subid = 1000 > balance = 0 > res = cursor.callproc(statement, (subid,balance)) > print res > return res > # return cursor.callproc(statement, parameters) > else: > return cursor.execute(statement, parameters) > else: > return execute(cursor, statement, parameters, context) > > > > Kevin Wormington wrote: >> I was able to get it working from just the ibm_db_dbi interface - the actual >> call has to be: >> cursor.callproc('BILLING.subscriber_balance',(subid,balance)) >> these both cause the sql errors: >> cursor.callproc('BILLING.subscriber_balance',(1000,0)) >> cursor.callproc('BILLING.subscriber_balance',(1000,balance)) >> The compiler.process(literal you suggested results in "CALL >> BILLING.subscriber_balance(?,?)(?,?)". I tried changing it to just >> return element.name and get just "CALL BILLING.subscriber_balance( )" sent >> to the DB. How can I get the return cursor.callproc(statement, parameters) >> to actually have the (subid,balance) in the parameters? >> Kevin >> Michael Bayer wrote: >>> On Mar 21, 2010, at 8:07 PM, Kevin Wormington wrote: >>> >>>> I just modified the compile to return just the procedure name and the >>>> cursor.callproc to send the statement and the two parameters as a tuple >>>> and the DB2 receives the correct SQL: >>>> >>>> CALL BILLING.subscriber_balance(?,?) >>>> >>>> But I get the following back from ibm_db_dbi: >>>> >>>> return cursor.callproc(statement, (1000,0)) >>>> File >>>> "/usr/local/lib/python2.6/dist-packages/ibm_db-1.0-py2.6-linux-i686.egg/ibm_db_dbi.py", >>>> line 973, in callproc >>>> result = self._callproc_helper(procname, parameters) >>>> File >>>> "/usr/local/lib/python2.6/dist-packages/ibm_db-1.0-py2.6-linux-i686.egg/ibm_db_dbi.py", >>>> line 951, in _callproc_helper >>>> raise _get_exception(inst) >>>> ibm_db_dbi.DatabaseError: ibm_db_dbi::DatabaseError: Describe Param >>>> Failed: [IBM][CLI Driver] CLI0150E Driver not capable. SQLSTATE=HYC00 >>>> SQLCODE=-99999 >>>> >>>> Looks like it doesn't like my values perhaps not being actual variables to >>>> bind. >>> >>> heres how to get the binds to come through, in case thats the problem: >>> >>> from sqlalchemy import literal >>> >>> @compiles(procedure) >>> def compile_procedure(element, compiler, **kw): >>> return "%s(%s)" % (element.name, >>> ",".join(compiler.process(literal(expr)) for expr in element.args)) >>> >>> "literal" is basically the same as "bindparam". when you send it to the >>> compiler the value becomes part of the compiled's params, and the generated >>> string is "?" or whatever paramstyle is. >>> >>> usually in SQLA core we do the conversion to bindparam in the constructor >>> of the clause construct. >>> >>> >>> >>> >>> >>> >>>> >>>> Kevin Wormington wrote: >>>>> 2010-03-21 18:40:49,656 INFO sqlalchemy.engine.base.Engine.0x...cb0c >>>>> calling stored procedure: BILLING.subscriber_balance(1000,?) >>>>> INFO:sqlalchemy.engine.base.Engine.0x...cb0c:calling stored procedure: >>>>> BILLING.subscriber_balance(1000,?) >>>>> It looks correct there but all that is getting logged is the statement >>>>> which has had all the arguments combined together by the compile. I >>>>> think that the 1000,? should remain as parameters to cursor.callproc >>>>> instead of everything going into statement. ibm_db_dbi.callproc is >>>>> probably adding the second set of () since it doesn't think there are any >>>>> arguments. Just not sure how to fix it. >>>>> Michael Bayer wrote: >>>>>> On Mar 21, 2010, at 7:34 PM, Kevin Wormington wrote: >>>>>> >>>>>>> From DB2 logs it appears that the following is what's getting sent to >>>>>>> DB2 and resulting in the error: >>>>>>> >>>>>>> CALL BILLING.subscriber_balance(1000,?)( ) >>>>>>> >>>>>>> >>>>>>> This is using: >>>>>>> >>>>>>> print engine.execute(procedure("BILLING.subscriber_balance", subid, >>>>>>> "?")) >>>>>>> >>>>>>> Any idea how I can get rid of the extra set of ()'s? >>>>>> what does the logging at the Python level say ? whatever goes into >>>>>> callproc() is the most you can control. that recipe is allowing you to >>>>>> control the string output exactly. >>>>>> >>>>>> >>>>>>> Thanks, >>>>>>> >>>>>>> Kevin >>>>>>> >>>>>>> Michael Bayer wrote: >>>>>>>> On Mar 21, 2010, at 12:50 PM, Kevin Wormington wrote: >>>>>>>>> Hi, >>>>>>>>> >>>>>>>>> I am using SQLAlchemy 0.5.8 with the ibm_db_sa (DB2) adapter and I am >>>>>>>>> wanting to add simple session.callproc support so that I can get >>>>>>>>> results from stored procedures that don't use a select or table >>>>>>>>> format. I haven't done any development on SA before but at first >>>>>>>>> glance it appears that I would just need to add the method in the ibm >>>>>>>>> dialect class and into engines/base.py to raise an exception for >>>>>>>>> other dialects that don't support it. Can it really be that easy? >>>>>>>>> Also, if I implement this will it be a possibility to get it >>>>>>>>> integrated into future releases? >>>>>>>> So here's the things i ask/state about this: >>>>>>>> 1. IBM's dialect works with 0.5 ? it says its only for 0.4. >>>>>>>> 2. the callproc requirement here assumes that there's absolutely no >>>>>>>> way to make this work using plain execute(). For example with >>>>>>>> cx_oracle you can use outparams with plain execute(). I don't know >>>>>>>> the details here for DB2, but if callproc() really provides >>>>>>>> functionality that is impossible otherwise, then yes we need to >>>>>>>> examine ways to call it. >>>>>>>> 3. the way this feature would work would be: >>>>>>>> from sqlalchemy import procedure >>>>>>>> result = engine.execute(procedure.name_of_my_procedure(arg1, arg2, >>>>>>>> ...)) >>>>>>>> or with plain text or such, you can do this: >>>>>>>> result = >>>>>>>> engine.connect().execution_options(callproc=True).execute("my >>>>>>>> procedure...") >>>>>>>> 4. execution_options is only availble in 0.6. Similarly, any "built >>>>>>>> in" version of this feature would be for 0.6. >>>>>>>> 5. I'd like to have IBM's dialect on 0.6. Two ways to do this would >>>>>>>> either be to write a new dialect from scratch using their DBAPI, or to >>>>>>>> just port their 0.4/0.5 dialect to 0.6. I'd prefer the latter, but I >>>>>>>> don't know the licensing details. If IBM were to give me the "green >>>>>>>> light" I'd just wrap their dialect into SQLAlchemy core where all the >>>>>>>> other ones are. >>>>>>>> 6. in 0.5 or any, the functionality of #3 can be achieved using public >>>>>>>> API points, here's a demo: >>>>>>>> from sqlalchemy.ext.compiler import compiles >>>>>>>> from sqlalchemy.interfaces import ConnectionProxy >>>>>>>> from sqlalchemy.sql.expression import ClauseElement >>>>>>>> from sqlalchemy import create_engine >>>>>>>> class procedure(ClauseElement): >>>>>>>> supports_execution = True >>>>>>>> _autocommit = False >>>>>>>> _execution_options = {} >>>>>>>> def __init__(self, name, *args): >>>>>>>> self.name = name >>>>>>>> self.args = args >>>>>>>> @compiles(procedure) >>>>>>>> def compile_procedure(element, compiler, **kw): >>>>>>>> return "%s(%s)" % (element.name, ",".join(str(expr) for expr in >>>>>>>> element.args)) >>>>>>>> class ProcedureProxy(ConnectionProxy): >>>>>>>> def cursor_execute(self, execute, cursor, statement, parameters, >>>>>>>> context, executemany): >>>>>>>> """Intercept low-level cursor execute() events.""" >>>>>>>> if context and isinstance(context.compiled.statement, >>>>>>>> procedure): >>>>>>>> engine.logger.info("calling stored procedure: %s", statement) >>>>>>>> if hasattr(cursor, 'callproc'): >>>>>>>> return cursor.callproc(statement, parameters) >>>>>>>> else: >>>>>>>> return cursor.execute(statement, parameters) >>>>>>>> else: >>>>>>>> return execute(cursor, statement, parameters, context) >>>>>>>> engine = create_engine('sqlite://', proxy=ProcedureProxy(), echo=True) >>>>>>>> print engine.execute(procedure("pragma table_info", "foo")) >>>>>>>> >>>>>>>>> Kevin >>>>>>>>> >>>>>>>>> -- >>>>>>>>> You received this message because you are subscribed to the Google >>>>>>>>> Groups "sqlalchemy" group. >>>>>>>>> To post to this group, send email to sqlalch...@googlegroups.com. >>>>>>>>> To unsubscribe from this group, send email to >>>>>>>>> sqlalchemy+unsubscr...@googlegroups.com. >>>>>>>>> For more options, visit this group at >>>>>>>>> http://groups.google.com/group/sqlalchemy?hl=en. >>>>>>>>> >>>>>>> -- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "sqlalchemy" group. >>>>>>> To post to this group, send email to sqlalch...@googlegroups.com. >>>>>>> To unsubscribe from this group, send email to >>>>>>> sqlalchemy+unsubscr...@googlegroups.com. >>>>>>> For more options, visit this group at >>>>>>> http://groups.google.com/group/sqlalchemy?hl=en. >>>>>>> >>>> -- >>>> You received this message because you are subscribed to the Google Groups >>>> "sqlalchemy" group. >>>> To post to this group, send email to sqlalch...@googlegroups.com. >>>> To unsubscribe from this group, send email to >>>> sqlalchemy+unsubscr...@googlegroups.com. >>>> For more options, visit this group at >>>> http://groups.google.com/group/sqlalchemy?hl=en. >>>> >>> > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalch...@googlegroups.com. > To unsubscribe from this group, send email to > sqlalchemy+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalch...@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.