#13264 closed enhancement (wontfix)
Allow single argument passed to DbContextManager.execute
| Reported by: | Ryan J Ollos | Owned by: | Ryan J Ollos |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | database backend | Version: | |
| Severity: | normal | Keywords: | |
| Cc: | Branch: | ||
| Release Notes: | |||
| API Changes: | |||
| Internal Changes: | |||
Description (last modified by )
A common programming error with the Trac API is to pass a single argument without wrapping it in a tuple or list:
self.env.db_query("SELECT * FROM ticket WHERE id=%s", id)
rather than:
self.env.db_query("SELECT * FROM ticket WHERE id=%s", (id,))
Could we allow non-tuple/list arguments and have DbContextManager.execute wrap the argument in a tuple?
Attachments (0)
Change History (10)
comment:1 by , 5 years ago
| Milestone: | next-dev-1.5.x → 1.5.3 |
|---|
comment:2 by , 5 years ago
| Milestone: | 1.5.3 → 1.5.4 |
|---|
comment:3 by , 4 years ago
| Description: | modified (diff) |
|---|
comment:4 by , 4 years ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:6 by , 4 years ago
I think _to_list() could be simplified like this:
def _to_list(self, params): - if not isinstance(params, (list, tuple)): - return [params] if params is not None else [] - return params + if params is None: + return [] + if isinstance(params, (list, tuple)): + return params + return [params]
Minor thing, psycopg2 and mysqldb library accept %(name)s-style markers with a dict parameter. The changes of API would break the behaviors.
psycopg2
>>> env = Environment('/home/jun66j5/var/trac/1.0-postgres')
>>> env.db_query('SELECT %(name)s', {'name': 42})
[(42,)]
>>> env.db_query('SELECT %(name)s', {'name': "aa'bb"})
[("aa'bb",)]
>>> env.db_query('SELECT %(name)s', {'name': 'aa"bb'})
[('aa"bb',)]
MySQLdb
>>> env = Environment('/home/jun66j5/var/trac/1.0-mysql')
>>> env.db_query('SELECT %(name)s', {'name': 42})
[(42L,)]
>>> env.db_query('SELECT %(name)s', {'name': "aa'bb"})
[(u"aa'bb",)]
>>> env.db_query('SELECT %(name)s', {'name': 'aa"bb'})
[(u'aa"bb',)]
comment:7 by , 4 years ago
I suppose we could pass through dicts as well:
if isinstance(params, (list, tuple, dict)):
return params
follow-up: 10 comment:8 by , 4 years ago
Further thought, another way this could be misinterpreted is the developer expecting to be able to pass multiple arguments like:
env.db_query(query, arg1, arg2, ...)
If we were implementing the API now, it might make more sense to have the function signature:
def execute(self, query, *params):
I'm considering it might be better to just leave the behavior as it is.
Anyway, my test case is not very good and fails on MySQL because system is a reserved word (#13128).
comment:9 by , 4 years ago
| Milestone: | 1.5.4 |
|---|---|
| Resolution: | → wontfix |
| Status: | assigned → closed |
comment:10 by , 4 years ago
Replying to Ryan J Ollos:
If we were implementing the API now, it might make more sense to have the function signature:
def execute(self, query, *params):
For example, the former seems more readable and less error prone:
db(""" UPDATE wiki SET text=%s WHERE name=%s AND version=%s """, self.text, self.name, self.version) db(""" UPDATE wiki SET text=%s WHERE name=%s AND version=%s """, (self.text, self.name, self.version))
But it would likely be a bad idea to differ from the Python sqlite API, which requires that parameters be a sequence or dict.



I'd like to address the programming error described in SO:53904305/121694.