Edgewall Software
Modify

Opened 4 years ago

Closed 3 years ago

Last modified 3 years ago

#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 Ryan J Ollos)

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 Ryan J Ollos, 4 years ago

Milestone: next-dev-1.5.x1.5.3

comment:2 by Ryan J Ollos, 3 years ago

Milestone: 1.5.31.5.4

comment:3 by Ryan J Ollos, 3 years ago

Description: modified (diff)

comment:4 by Ryan J Ollos, 3 years ago

Owner: set to Ryan J Ollos
Status: newassigned

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

comment:5 by Ryan J Ollos, 3 years ago

Proposed change in [875cef97ac/rjollos.git].

comment:6 by Jun Omae, 3 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 Ryan J Ollos, 3 years ago

I suppose we could pass through dicts as well:

if isinstance(params, (list, tuple, dict)):
    return params

comment:8 by Ryan J Ollos, 3 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).

Last edited 3 years ago by Ryan J Ollos (previous) (diff)

comment:9 by Ryan J Ollos, 3 years ago

Milestone: 1.5.4
Resolution: wontfix
Status: assignedclosed

in reply to:  8 comment:10 by Ryan J Ollos, 3 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.

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain Ryan J Ollos.
The resolution will be deleted. Next status will be 'reopened'.
to The owner will be changed from Ryan J Ollos to the specified user.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.