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  -  DB-SIG@python.org
https://mail.python.org/mailman/listinfo/db-sig

Reply via email to