Find attached patch with proof of concept for minimal implementation of
prepared statements.

I hacked query_formatted to accept an prepare=False.
The difficulty is in replacing %s with $1, which needs to either have a list of
parameters over which to iterate, or at least an integer determining the number
of dollar parameters in literals.  Since PREPARE doesn't send parameters
(that's quite the point), I passed a range() list as a hack to show that it
could work.

And for PQexecPrepared, I added a conditional in query(), since that entire
function appears to be exactly what's needed for the prepared case, too.
Here, the hack is that I determine wether to call exec or execPrepared by
running strstr(sql, "prep").  I'll solicit suggestions for a better way to do
that now.

[pryzbyj@telsasoft PyGreSQL]$ PYTHONPATH=build/lib.linux-x86_64-2.7/ command 
time -v python2.7 -c "import pg; d=pg.DB('ts'); d.query_formatted('prepare 
prepq AS SELECT * FROM generate_series(1,%s)', range(1), prepare=True); print 
d.query('prepq', [99]).getresult()" 
[(1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,), (10,), (11,), (12,), 
(13,), (14,), (15,), (16,), (17,), (18,), (19,), (20,), (21,), (22,), (23,), 
(24,), (25,), (26,), (27,), (28,), (29,), (30,), (31,), (32,), (33,), (34,), 
(35,), (36,), (37,), (38,), (39,), (40,), (41,), (42,), (43,), (44,), (45,), 
(46,), (47,), (48,), (49,), (50,), (51,), (52,), (53,), (54,), (55,), (56,), 
(57,), (58,), (59,), (60,), (61,), (62,), (63,), (64,), (65,), (66,), (67,), 
(68,), (69,), (70,), (71,), (72,), (73,), (74,), (75,), (76,), (77,), (78,), 
(79,), (80,), (81,), (82,), (83,), (84,), (85,), (86,), (87,), (88,), (89,), 
(90,), (91,), (92,), (93,), (94,), (95,), (96,), (97,), (98,), (99,)]

Justin
diff --git a/pg.py b/pg.py
index e830a66..ec15631 100644
--- a/pg.py
+++ b/pg.py
@@ -678,14 +678,23 @@ class Adapter:
         params.adapt = self.adapt
         return params
 
-    def format_query(self, command, values=None, types=None, inline=False):
+    def format_query(self, command, values=None, types=None, inline=False, prepare=False):
         """Format a database query using the given values and types."""
-        if not values:
+        if not values and not prepare:
             return command, []
         if inline and types:
             raise ValueError('Typed parameters must be sent separately')
         params = self.parameter_list()
-        if isinstance(values, (list, tuple)):
+        if prepare:
+            # In this case, "inline" doesn't make any sense ?
+            literals = []
+            for _ in values:
+                literals.append(params.add(None))
+            params=[] # Do not send params during PREPARE
+            # Or just do this:
+            literals = ['$%s'%x for x in range(1,1+len(values))]
+            command %= tuple(literals)
+        elif isinstance(values, (list, tuple)):
             if inline:
                 adapt = self.adapt_inline
                 literals = [adapt(value) for value in values]
@@ -1854,7 +1863,7 @@ class DB:
         return self.db.query(command)
 
     def query_formatted(self, command,
-            parameters=None, types=None, inline=False):
+            parameters=None, types=None, inline=False, prepare=False):
         """Execute a formatted SQL command string.
 
         Similar to query, but using Python format placeholders of the form
@@ -1867,7 +1876,7 @@ class DB:
         embedded in the SQL command, otherwise they will be sent separately.
         """
         return self.query(*self.adapter.format_query(
-            command, parameters, types, inline))
+            command, parameters, types, inline, prepare))
 
     def pkey(self, table, composite=False, flush=False):
         """Get or set the primary key of a table.
diff --git a/pgmodule.c b/pgmodule.c
index 08ed188..ff2d90d 100644
--- a/pgmodule.c
+++ b/pgmodule.c
@@ -2287,8 +2287,17 @@ connQuery(connObject *self, PyObject *args)
 		}
 
 		Py_BEGIN_ALLOW_THREADS
-		result = PQexecParams(self->cnx, query, nparms,
-			NULL, parms, NULL, NULL, 0);
+		if (strstr(query, "prep")==query) {
+			// In this case, the "query" is just the name of the
+			// prepared statement to execute.
+			// Note, no argument is passed for paramTypes.
+			result = PQexecPrepared(self->cnx, query, nparms,
+				parms, NULL, NULL, 0);
+		} else {
+			result = PQexecParams(self->cnx, query, nparms,
+				NULL/*paramTypes*/, parms, NULL, NULL, 0);
+		}
+
 		Py_END_ALLOW_THREADS
 
 		PyMem_Free((void *)parms);
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo/pygresql

Reply via email to