From ea77efbad13dbe26ee1c123be56f3cbdec59df40 Mon Sep 17 00:00:00 2001
From: "Stephen R. van den Berg" <srb@cuci.nl>
Date: Fri, 4 Jul 2008 10:25:39 +0200
Subject: [PATCH] Enable true bindings for Postgres

Rev: lib/modules/Sql.pmod/postgres.pike:1.30
Rev: src/modules/Postgres/postgres.c:1.62
---
 lib/modules/Sql.pmod/postgres.pike | 42 +++++++++++++-
 src/modules/Postgres/postgres.c    | 89 ++++++++++++++++++++++++++----
 2 files changed, 117 insertions(+), 14 deletions(-)

diff --git a/lib/modules/Sql.pmod/postgres.pike b/lib/modules/Sql.pmod/postgres.pike
index f9428b1fd4..21b51ce8ad 100644
--- a/lib/modules/Sql.pmod/postgres.pike
+++ b/lib/modules/Sql.pmod/postgres.pike
@@ -1,7 +1,7 @@
 /*
  * This is part of the Postgres module for Pike.
  *
- * $Id: postgres.pike,v 1.29 2008/06/28 16:49:55 nilsson Exp $
+ * $Id: postgres.pike,v 1.30 2008/07/04 08:25:39 srb Exp $
  *
  */
 
@@ -384,9 +384,47 @@ array(mapping(string:mixed)) list_fields (string table, void|string wild)
 //!   @[Sql.Sql], @[Sql.sql_result]
 int|object big_query(object|string q, mapping(string|int:mixed)|void bindings)
 {  
+  if(stringp(q) && String.width(q)>8)
+    q=string_to_utf8(q);
   if (!bindings)
     return ::big_query(q);
-  return ::big_query(.sql_util.emulate_bindings(q, bindings, this));
+  int pi=0,rep=0;
+  array(string|int) paramValues=allocate(sizeof(bindings)*2);
+  array(string) from=allocate(sizeof(bindings));
+  array(string) to=allocate(sizeof(bindings));
+  foreach(bindings; mixed name; mixed value) {
+    // Throws if mapping key is empty string.
+    if(stringp(name)) {
+      if(name[0]!=':')
+        name=":"+name;
+      if(name[1]=='_') {
+        // Special parameter
+        continue;
+      }
+    }
+    from[rep]=name;
+    string rval;
+    if(multisetp(value)) {
+      rval=sizeof(value) ? indices(value)[0] : "";
+    }
+    else {
+      paramValues[pi++]=name[sizeof(name)-1]=='_'; // Force binary mode
+      if(zero_type(value))			   // when name ends in _
+        paramValues[pi++]=UNDEFINED;
+      else {
+        if(stringp(value) && String.width(value)>8)
+          value=string_to_utf8(value);
+        paramValues[pi++]=(string)value;
+      }
+      rval="$"+(string)(pi/2);
+    }
+    to[rep++]=rval;
+  }
+  paramValues= pi ? paramValues[..pi-1] : UNDEFINED;
+  if(rep--) {
+    q=replace(q,from[..rep],to[..rep]);
+  }
+  return ::big_query(q, paramValues);
 }
 
 #else
diff --git a/src/modules/Postgres/postgres.c b/src/modules/Postgres/postgres.c
index 4c8b9bb4b5..02d467556a 100644
--- a/src/modules/Postgres/postgres.c
+++ b/src/modules/Postgres/postgres.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: postgres.c,v 1.61 2008/06/30 12:10:48 srb Exp $
+|| $Id: postgres.c,v 1.62 2008/07/04 08:25:39 srb Exp $
 */
 
 /*
@@ -29,6 +29,7 @@
 #include <string.h>
 
 /* Pike includes */
+#include "svalue.h"
 #include "las.h"
 #include "machine.h"
 #include "pike_memory.h"
@@ -381,21 +382,75 @@ static void f_big_query(INT32 args)
 	PGconn *conn = THIS->dblink;
 	PGresult * res;
 	PGnotify * notification;
-	char *query;
+        struct array *bnds = 0;
+	char *query = 0;
 	int lastcommit,docommit,dofetch;
+        int cnt;
+        int nParams = 0;
+        const char** paramValues = 0;
+        int* paramLengths = 0;
+        int* paramFormats = 0;
+        int resultFormat = 0;
+        
 	PQ_FETCH();
 	docommit=dofetch=0;
 	lastcommit=THIS->lastcommit;
 
-	check_all_args("Postgres->big_query",args,BIT_STRING,0);
+	check_all_args("Postgres->big_query",args,
+	  BIT_STRING,
+	  BIT_VOID | BIT_ARRAY,
+	  0);
 
 	if (!conn)
 		Pike_error ("Not connected.\n");
 
-	if (Pike_sp[-args].u.string->len)
+        switch(args)
+        {
+          default:
+            if(Pike_sp[1-args].type == PIKE_T_ARRAY)
+              bnds=Pike_sp[1-args].u.array;
+      
+          case 1:
+	    query=" ";
+	    if(Pike_sp[-args].u.string->len)
 		query=Pike_sp[-args].u.string->str;
-	else
-		query=" ";
+        }
+
+        if(bnds && (cnt=bnds->size)) {
+          int i;
+	  struct svalue *item;
+
+	  if(cnt & 1)
+	    Pike_error ("Uneven number of arrayelements.\n");
+
+	  nParams=cnt=cnt/2;
+	  
+	  paramValues = xalloc(cnt*sizeof*paramValues);
+	  paramLengths = xalloc(cnt*sizeof*paramLengths);
+	  paramFormats = xalloc(cnt*sizeof*paramFormats);
+
+	  for (i=0,item=bnds->item; cnt--; item++,i++) {
+
+	    if (item->type != PIKE_T_INT)
+	      Pike_error ("Expected integer element.\n");
+	    paramFormats[i] = item->u.integer ? 1 : 0;
+	    switch((++item)->type)
+	    { case PIKE_T_STRING:
+	          paramValues[i] = item->u.string->str;
+		  paramLengths[i] = item->u.string->len;
+	        break;
+	      case PIKE_T_INT:
+	      case T_VOID:
+	          paramValues[i] = 0;	     /* NULL */
+		  paramLengths[i] = 0;
+	        break;
+	      default:
+                  Pike_error ("Expected string or UNDEFINED element, Got %d.\n",
+ item->type);
+	        break;
+	    }
+	  }
+        }
 
 	THREADS_ALLOW();
 	PQ_LOCK();
@@ -431,11 +486,13 @@ static void f_big_query(INT32 args)
 	    strcpy(nquery+CPREFLEN-1,query);
 	    if(lastcommit)
 	      goto yupbegin;
-	    res=PQexec(conn,nquery);
+	    res=PQexecParams(conn,nquery,
+             nParams,0,paramValues,paramLengths,paramFormats,resultFormat);
 	    if(PQstatus(conn) != CONNECTION_OK) {
 	      PQclear(res);
 	      PQreset(conn);
-	      res=PQexec(conn,nquery);
+	      res=PQexecParams(conn,nquery,
+               nParams,0,paramValues,paramLengths,paramFormats,resultFormat);
 	    }
 	    if(res)
 	      switch(PQresultStatus(res)) {
@@ -444,7 +501,8 @@ static void f_big_query(INT32 args)
 yupbegin:       res=PQexec(conn,"BEGIN");
 		if(res && PQresultStatus(res)==PGRES_COMMAND_OK) {
 		  PQclear(res);
-		  res=PQexec(conn,nquery);
+	          res=PQexecParams(conn,nquery,nParams,0,
+		   paramValues,paramLengths,paramFormats,resultFormat);
 		  if(res && PQresultStatus(res)==PGRES_COMMAND_OK)
 		    docommit=1;
 		  else {
@@ -470,7 +528,8 @@ yupbegin:       res=PQexec(conn,"BEGIN");
 	}
 	lastcommit=0;
 	if(!res)
-	  res=PQexec(conn,query);
+	  res=PQexecParams(conn,query,
+           nParams,0,paramValues,paramLengths,paramFormats,resultFormat);
 	/* A dirty hack to fix the reconnect bug.
 	 * we don't need to store the host/user/pass/db... etc..
 	 * PQreset() does all the job.
@@ -481,7 +540,8 @@ yupbegin:       res=PQexec(conn,"BEGIN");
 	   (PQresultStatus(res) == PGRES_BAD_RESPONSE)) {
 	  PQclear(res);
 	  PQreset(conn);
-	  res=PQexec(conn,query);
+	  res=PQexecParams(conn,query,
+           nParams,0,paramValues,paramLengths,paramFormats,resultFormat);
 	}
 
 	notification=PQnotifies(conn);
@@ -491,6 +551,10 @@ yupbegin:       res=PQexec(conn,"BEGIN");
 	THIS->dofetch=dofetch;
 	THIS->lastcommit=lastcommit;
 
+        if (bnds) {
+	  xfree(paramValues); xfree(paramLengths); xfree(paramFormats);
+        }
+
 	pop_n_elems(args);
 	if (notification!=NULL) {
 		pgdebug("Incoming notification: \"%s\"\n",notification->relname);
@@ -764,7 +828,8 @@ PIKE_MODULE_INIT
   ADD_FUNCTION("select_db", f_select_db, tFunc(tStr,tVoid), 0);
 
   /* function(string:int|object) */
-  ADD_FUNCTION("big_query", f_big_query, tFunc(tStr,tOr(tInt,tObj)), 0);
+  ADD_FUNCTION("big_query", f_big_query,
+       tFunc(tStr tOr(tVoid,tArr(tOr3(tInt,tStr,tVoid))), tOr(tInt,tObj)), 0);
 
   /* function(void:string) */
   ADD_FUNCTION("error", f_error, tFunc(tVoid,tStr), 0);
-- 
GitLab