Hi Skip,
sorry for the late reply. For some reason I did not see your
email to the list in my inbox.
On 13.03.2014 21:50, Skip Montanaro wrote:
> [...]
>
> After this episode, I was motivated to revisit PEP 249's discussion of
> stored procedures. I barely use SQL in my own work, relying heavily
> on the work of others, and rarely formulating sophisticated queries
> myself. Until I had to fix this bug, I didn't even realize that
> stored procedures had return statements. I thought they transmitted
> values out using some other syntax.
The DB-API syntax goes like this:
result_parameters = cursor.callproc(procname, parameters)
with procname being the procedure name and parameters
the sequence of parameters.
The returned result_parameters is a copy of the parameters
sequence with in/out and output parameters replaced with their
new values.
> So, now I know that, besides being optional, and being widely
> different across different databases, stored procedures have three
> ways of returning results:
>
> 1. Zero or more result sets
>
> 2. Output (or in/out) parameters
>
> 3. Return values
That last bullet is usually often to stored functions,
not procedures - even though there are databases which have
procedures return value, just as there are databases which don't
have procedures and instead call everything a function.
Additionally, some databases don't allow in/out and output
parameters for stored functions.
> Processing result sets is well-defined. Call the cursor's fetch*()
> and nextset() methods repeatedly until nextset() returns False. I
> think the specification of other two ways of transmitting values out
> of a stored procedure could use some work though. The solution to
> return values in the python-sybase module is to set a status_result
> attribute on the Cursor object. Structurally, it looks like a one-row
> one-element result set, e.g.: [(1,)] if the stored procedure executed
> "return 1". This seems like a reasonable way to go about this, though
> I have no idea how complex return values can be, or how best to
> indicate that a stored procedure didn't return a value (None or an
> empty list both seem reasonable).
>
> Python-sybase allows you to specify output parameters in parameter
> dictionaries using the OUTPUT function. For example, adapted from some
> code I maintain:
>
> args = {
> "@date1": today,
> "@date2": Sybase.OUTPUT(date),
> "@symbol": "IBM",
> }
> conn = db.pool.get_connection()
> output = conn.callproc(conn.cursor, "previous_trading_day", args)
Hmm, the method should be defined on the cursor, not the connection
for DB-API 2.0 compatibility. I guess the module still uses
the old and deprecated DB-API 1.0 approach.
> In this case, the @date2 key represents an output parameter, and our
> wrapper rewrites that value. I don't know if we do this in our
> wrapper because the python-sybase authors intended not to do this
> rewriting, or if I'm working around an actual bug. The input side of
> in/out parameters is (I presume) passed in through the call to
> Sybase.OUTPUT(...).
>
> In a separate email thread, Marc-Andre suggested that we could add a
> callfunc() method to Cursor objects. I'm not entirely sure that's
> necessary, as (at least in the Sybase case) a stored procedure can
> return all three types of values in the same call.
>
> So, to draw this exceedingly long mail to a close, I propose:
>
> 1. PEP 249 should document how stored procedure return values are made
> available to the caller of Cursor.callproc.
Agreed, we need to address this in some way.
Since the .callproc() signature is already defined to not return
a procedure/function return value, my proposal was to introduce a
new method .callfunc() which does support this.
There are a few ways this could be done. The most intuitive is
probably this one:
Variant A:
----------
return_value = cursor.callfunc(funcname, parameters)
Unlike the .callproc() method, this call would not support
in/out or output parameters. It would still support creating
result sets, though.
A less intuitive alternative would be this one:
Variant B:
----------
return_value_and_result_parameters = cursor.callfunc(funcname, parameters)
with return_value_and_result_parameters being a sequence of the form
[return_value, parameter0, parameter1, ...], i.e. the return_value
is prepended to the parameters list.
This would also support in/out and output parameters.
More Pythonic:
Variant C:
----------
(return_value, result_parameters) = cursor.callfunc(funcname, parameters)
This would also support in/out and output parameters and allow to
easily separate the return_value from the result_parameters.
> 2. PEP 249 should more precisely document how output and in/out
> parameters are specified and processed.
Agreed as well :-)
At the moment, the DB-API leaves these details to the database
modules to figure out.
--
Marc-Andre Lemburg
eGenix.com
Professional Python Services directly from the Source (#1, Mar 24 2014)
>>> Python Projects, Consulting and Support ... http://www.egenix.com/
>>> mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/
>>> mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
________________________________________________________________________
2014-03-29: PythonCamp 2014, Cologne, Germany ... 5 days to go
2014-04-09: PyCon 2014, Montreal, Canada ... 16 days to go
2014-04-29: Python Meeting Duesseldorf ... 36 days to go
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
Registered at Amtsgericht Duesseldorf: HRB 46611
http://www.egenix.com/company/contact/
_______________________________________________
DB-SIG maillist - [email protected]
https://mail.python.org/mailman/listinfo/db-sig