[PyGreSQL] Patch: Quoting input for INSERT into a bytea field

Charlie Dyson charlie at charliedyson.net
Fri Sep 3 13:19:02 EDT 2004


Hi,
I'm currently working on a python interface to a photographic database,
and I needed a method of importing binary data for thumbnails. I didn't
want to use large objects, because of non-uniqueness of OIDs (I don't
know if it makes much difference in "normal" usage), and because the
thumbnails are not that big. I chose to use "bytea" fields. Quoting the
input in the required octet form in python turns out to be rather slow
(tight loops in python always are), so I humbly present a patch (my
first to an open source project) to enable such functionality in
pygresql, in the hope that other people might find it useful.

There may be "cleaner" ways of doing this, but the method I've employed
should be very fast, it seemed to be when I imported about 2000
thumbnails with it. For really large objects, this method would not be
efficient, because it relies on allocation of large strings, for the SQL
command, so large objects will still survive for data larger than
perhaps a few MB.

Anyway, here's the patch, against the latest beta. It's my first python
extension too - seems to work fine. Enjoy!
== CUT ==
diff -u -r PyGreSQL-3.6-pre040830/pgmodule.c
PyGreSQL-3.6-pre040830_modified/pgmodule.c
--- PyGreSQL-3.6-pre040830/pgmodule.c	2004-08-30 09:54:27.000000000
+0100
+++ PyGreSQL-3.6-pre040830_modified/pgmodule.c	2004-09-03
17:55:20.449597451 +0100
@@ -3171,10 +3171,45 @@
 }
 #endif   /* DEFAULT_VARS */
 
+/* Escape a string so that it can be used for an INSERT into a
+   bytea field */
+static char bytea_escape__doc__[] =
+"bytea_escape(string) -- Escape a (binary) string so that it can be
used for an INSERT into a\n"
+"bytea field. Note that quotes are not included.";
+
+static PyObject *bytea_escape(PyObject *self, PyObject *args) {
+	char *str; /* Our argument */
+	char *output; /* The returned escaped string */
+	int strlength, i; /* Length of string, iterator */
+	PyObject *ret; /* String object to return */
+
+	if (!PyArg_ParseTuple(args, "s#", &str, &strlength))
+		return NULL;
+	
+	output = (char*) PyMem_Malloc( 5*strlength + 1 );
+	if (!output)
+		return PyErr_NoMemory();
+
+	for(i=0; i < strlength; i++)
+		sprintf(output + 5*i, "\\\\%.3o", (unsigned char) str[i]);
+
+	output[5*strlength] = '\0';
+
+	ret = Py_BuildValue("s", output);
+
+	if(!ret)
+		/* Pass on exception */
+		return NULL;
+
+	PyMem_Free(output);
+	return ret;
+}
+
 /* List of functions defined in the module */
 
 static struct PyMethodDef pg_methods[] = {
 	{"connect", (PyCFunction) pgconnect, 3, connect__doc__},
+	{"bytea_escape", (PyCFunction) bytea_escape, 1, bytea_escape__doc__},
 
 #ifdef DEFAULT_VARS
 	{"get_defhost", pggetdefhost, 1, getdefhost__doc__},
== CUT ==

  _______            ___       ___                   
 / ___/ /  ___ _____/ (_)__   / _ \__ _____ ___  ___ 
/ /__/ _ \/ _ `/ __/ / / -_) / // / // (_-</ _ \/ _ \
\___/_//_/\_,_/_/ /_/_/\__/ /____/\_, /___/\___/_//_/
                                 /___/               
	charlie at charlie dyson dot net



More information about the PyGreSQL mailing list