Hi everyone.
I've made a change which allows queries to be made like this, using the
classic interface:
db.query("insert into x (a,b,c) values ($3,$1,$2)", 'bravo', 3, 'alpha')
db.query("select * from x where a = $1", 'alpha')
The benefit of this is you don't have to quote input strings, you don't have
to worry about SQL injection because you forgot to quote input strings, and
you don't have to worry about the setting of standard_conforming_strings if
your data has back-slashes in it.
The attached patch is against 4.0 and hence is for demonstration purposes
only. If this is of interest to the project, I'll redo it against the head
revision and submit a diff against SVN.
The changes to pgmodule.c and the first two blocks vs pg.py are necessary for
the functionality described above. The changes starting at line 604 are the
changes to DB.update() to use this rather than quoting.
In the final patch, I'd propose to make essentially the same changes I have
here, plus equivalent changes to DB.insert(), DB.delete() and anywhere else I
find calls to _quote in pg.py. I also will do some testing with unicode which
might result in other changes.
I don't propose to touch the dbapi code or to introduce prepared statements at
this time.
--
Patrick TJ McPhee <[email protected]>
--- pgmodule.c.orig 2009-01-01 08:05:02.000000000 -0500
+++ pgmodule.c 2012-09-20 19:24:40.000000000 -0400
@@ -2222,13 +2222,14 @@
/* database query */
static char pg_query__doc__[] =
-"query(sql) -- creates a new query object for this connection,"
-" using sql (string) request.";
+"query(sql, *args) -- creates a new query object for this connection,"
+" using sql (string) request and optinally one or more arguments.";
static PyObject *
pg_query(pgobject * self, PyObject * args)
{
char *query;
+ PyObject *oargs = NULL;
PGresult *result;
pgqueryobject *npgobj;
int status;
@@ -2240,9 +2241,9 @@
}
/* get query args */
- if (!PyArg_ParseTuple(args, "s", &query))
+ if (!PyArg_ParseTuple(args, "s|O:query", &query, &oargs))
{
- PyErr_SetString(PyExc_TypeError, "query(sql), with sql (string).");
+ PyErr_SetString(PyExc_TypeError, "query(sql, *args), with sql (string).");
return NULL;
}
@@ -2254,9 +2255,48 @@
}
/* gets result */
- Py_BEGIN_ALLOW_THREADS
- result = PQexec(self->cnx, query);
- Py_END_ALLOW_THREADS
+ if (!oargs)
+ {
+ Py_BEGIN_ALLOW_THREADS
+ result = PQexec(self->cnx, query);
+ Py_END_ALLOW_THREADS
+ }
+
+ else
+ {
+ int nparms, *parml;
+ char **parms;
+ register int i;
+
+ nparms = PyTuple_Size(oargs);
+ parms = (char **)alloca(nparms * sizeof(*parms));
+ parml = (int *)alloca(nparms * sizeof(*parml));
+
+ /* convert optional args to a list of strings -- this allows
+ * the caller to pass whatever they like, and prevents us
+ * from having to map types to OIDs */
+ for (i = 0; i < nparms; i++)
+ {
+ PyObject * obj = PyTuple_GetItem(oargs, i);
+ PyObject * str;
+
+ if (obj == Py_None)
+ {
+ parml[i] = 0;
+ parms[i] = NULL;
+ }
+ else
+ {
+ str = = PyObject_Str(obj);
+ parml[i] = PyString_Size(str);
+ parms[i] = PyString_AsString(str);
+ }
+ }
+ Py_BEGIN_ALLOW_THREADS
+ result = PQexecParams(self->cnx, query, nparms,
+ NULL, parms, parml, NULL, 0);
+ Py_END_ALLOW_THREADS
+ }
/* checks result validity */
if (!result)
--- pg.py.orig 2009-01-01 08:05:02.000000000 -0500
+++ pg.py 2012-09-20 02:35:34.000000000 -0400
@@ -291,7 +291,7 @@
self.db.close()
self.db = db
- def query(self, qstr):
+ def query(self, qstr, *args):
"""Executes a SQL command string.
This method simply sends a SQL query to the database. If the query is
@@ -310,7 +310,7 @@
if not self.db:
raise InternalError('Connection is not valid')
self._do_debug(qstr)
- return self.db.query(qstr)
+ return self.db.query(qstr, args)
def pkey(self, cl, newpkey=None):
"""This method gets or sets the primary key of a class.
@@ -594,7 +594,8 @@
d.update(kw)
attnames = self.get_attnames(qcl)
if qoid in d:
- where = 'oid = %s' % d[qoid]
+ where = 'oid = $1'
+ params = [d[qoid]]
keyname = ()
else:
try:
@@ -604,14 +605,18 @@
if isinstance(keyname, basestring):
keyname = (keyname,)
try:
- where = ' AND '.join(['%s = %s'
- % (k, self._quote(d[k], attnames[k])) for k in keyname])
+ where = ' AND '.join(['%s = $%s'
+ % (k, i+1) for i,k in enumerate(keyname)])
+ params = [d[k] for k in keyname]
except KeyError:
raise ProgrammingError('Update needs primary key or oid.')
values = []
+ i = len(params)
for n in attnames:
if n in d and n not in keyname:
- values.append('%s = %s' % (n, self._quote(d[n], attnames[n])))
+ i += 1
+ values.append('%s = $%s' % (n, i))
+ params.append(d[n])
if not values:
return d
values = ', '.join(values)
@@ -622,13 +627,12 @@
ret = ''
q = 'UPDATE %s SET %s WHERE %s%s' % (qcl, values, where, ret)
self._do_debug(q)
- res = self.db.query(q)
+ res = self.db.query(q, tuple(params))
if ret:
- res = self.db.query(q).dictresult()
+ res = res.dictresult()
for att, value in res[0].iteritems():
d[att == 'oid' and qoid or att] = value
else:
- self.db.query(q)
if selectable:
if qoid in d:
self.get(qcl, d, 'oid')
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo.cgi/pygresql