[DB-SIG] bind parameters and empty parameter lists
Chris Clark
Chris.Clark at actian.com
Fri May 24 17:54:13 CEST 2013
On Fri, 24 May 2013 10:23:41 +0200, M.-A. Lemburg <mal at egenix.com> wrote:
> On 23.05.2013 23:37, Chris Clark wrote:
>> On Thu, 23 May 2013 13:33:20 -0700, Chris Clark <Chris.Clark at actian.com> wrote:
>>> On Thu, 23 May 2013 16:59:27 +0200, M.-A. Lemburg <mal at egenix.com> wrote:
>>>> On 22.05.2013 00:52, Michael Bayer wrote:
>>>>> As a total aside, I also have issues with some DBAPIs that accept *only* a list for the
>>>>> "sequence", others that accept *only* a tuple (this may be only when the parameter list is
>>>>> empty, however). So specifying the significance of "empty" parameter lists as well as how to
>>>>> test for "sequence" would be nice too, so that I can hit the DBAPI authors with bug reports
>>>>> backed up by the spec.
>>>> The DB-API is already clear on this: any sequence should be
>>>> accepted for .execute() and any sequence of sequences for .executemany().
>>>>
>>>> Spelling out the special case of passing an empty sequence to
>>>> .execute() and .executemany() would probably be wise. For .execute()
>>>> this would only be valid for a statement that doesn't have parameter
>>>> markers. For .executemany() this would be the same as a no-op (and
>>>> only serve a purpose on the basis that it makes writing algorithms
>>>> easier).
>>>>
>>> I'd like to suggest that if the bind parameter is nonzero (i.e. __nonzero__ method call) then it
>>> is assumed there are no params. This way we don't have to get into is it an empty sequence, is it
>>> None, etc.
>> Apologies, I meant the reverse :-(
>>
>> I'd like to suggest that if the bind parameter is nonzero (i.e. __nonzero__ method call returns
>> True) then it is assumed there *are* params. This way we don't have to get into is it an empty
>> sequence, is it None, etc.
> There is no __nonzero__ in Python 3 anymore. The method was renamed
> to __bool__ :-)
>
> I'm also not sure whether we should allow any of these:
>
> .execute(sql, None)
> .executemany(sql, None)
Personally I'd like to allow the above. If a driver has:
def execute(self, sql_text, params=None):
if params:
# process params, more on this later
# else assume no params
this will be allowed but the param will be ignored
> .executemany(sql, [None])
This is OK too, this drops in the the "we have params logic", and it is
a single param that is NULL.
> In order to support your suggestion, database module would have to
> call the __nonzero__/__bool__ slot on the parameters, which could
> be an expensive operation with no real value in case the parameters
> are not None,
Do you have an example of then this would be expensive? I do not
understand the "real value in case the parameters
are not None" comment, could you expand (possibly with a short example)
on this please?
> or even be impossible in the case of iterators.
An iterator with values left is always going to be true, if empty it is
false:
>>> params = xrange(10)
>>> if params:
... print True
...
True
>>> params = xrange(0)
>>> if params:
... print True
...
>>>
this is true for generators too.
> Using the slot, we'd also allow things like these:
>
> .execute(sql, 0)
> .executemany(sql, 0.0)
I'm OK with these being allowed through, they would NOT be treated as
params. If there are param markers in the sql, a (DBMS) error should be
raised with "missing bind params" if the driver doe snot then pass in
params.
> .executemany(sql, [0])
Again, this is valid, a single tuple with an integer value of zero.
> which can hide programming errors.
The dbms should raise an error for this, this shouldn't be hidden.
> Using the __len__ slot would work, though, unless you have an
> iterator...
I'm not keen on that. For instance strings are sequences and if we have:
.execute(sql, 'hello')
it would be valid.
I don't have a good answer for this one, I've been toying with the idea
of a string check for param but that seems overly excessive.
Coming back to param checks:
def execute(self, sql_text, params=None):
if params:
for param in params:
do stuff....
# else assume no params
I've been toying with the idea of catching TypeErrors to detect cases
like .execute(sql, 1) in the driver (and wrappers) that I maintain. E.g.:
>>> for x in 1:
... print x
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
Is catching a type error a good way to check if something isn't a
sequence? What are other people doing? This doesn't handle users who
accidentally pass in a single string (instead of a tuple with a single
string).
> Which leads to another idea for DB-API 3: I think we should allow
> iterators for the parameter list in .executemany(), and perhaps
> even for the parameter sequence in .execute().
They should be allowed and I think you are correct we should be explicit
in the spec on this.
I actually have code that relies on passing a generator into executemany
(the easiest example is to pass a cursor in for instance or an ETL
function that takes a cursor as a param) .
Chris
More information about the DB-SIG
mailing list