On Mar 22, 2010, at 3:48 PM, Kevin Wormington wrote:
> That's getting closer. Now if I could just figure out how to get it to use
> the parameters that I'm actually passing in engine.execute. It appears
> ibm_db_sa botches the sql (to "CALL BILLING.subscriber_balance()") if use the
> commented out cursor.callproc(statement, parameters).
well you're right there so figure out what is different about "parameters"
versus your tuple there. a good candidate is, "parameters" is probably a
list, not a tuple.
>
>
> from sqlalchemy.ext.compiler import compiles
> from sqlalchemy.interfaces import ConnectionProxy
> from sqlalchemy.sql.expression import ClauseElement
> from sqlalchemy import create_engine
> from sqlalchemy import literal
>
> 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" % (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
> result = cursor.callproc(statement, (subid,balance))
> # result = cursor.callproc(statement, parameters)
> context.callproc_result = result
> return result
> else:
> return cursor.execute(statement, parameters)
> else:
> return execute(cursor, statement, parameters, context)
>
>
> engine =
> create_engine('ibm_db_sa://db2inst1:[email protected]:xxx/BILLING',
> proxy=ProcedureProxy(), echo=True)
>
> subid=1000
> balance=0
> res = engine.execute(procedure("BILLING.subscriber_balance", subid, balance))
> print res.context.callproc_result
>
>
> Michael Bayer wrote:
>> 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 [email protected].
>>>>>>>>>>> To unsubscribe from this group, send email to
>>>>>>>>>>> [email protected].
>>>>>>>>>>> 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 [email protected].
>>>>>>>>> To unsubscribe from this group, send email to
>>>>>>>>> [email protected].
>>>>>>>>> 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 [email protected].
>>>>>> To unsubscribe from this group, send email to
>>>>>> [email protected].
>>>>>> 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 [email protected].
>>> To unsubscribe from this group, send email to
>>> [email protected].
>>> 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 [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.