Index: wp-includes/wp-db.php
===================================================================
--- wp-includes/wp-db.php	(revision 3923)
+++ wp-includes/wp-db.php	(working copy)
@@ -83,7 +83,177 @@
 		else
 			return mysql_real_escape_string( $string, $this->dbh );
 	}
+	
+	/**
+ *  Builds a properly formatted and escaped SQL statement using an SQL template and a list of arguments.
+ *
+ *  The function scans the template for query marks ? which are placeholders for the arguments
+ *  Query marks are to be followed by format descriptors.
+ *
+ *  The first argument, the template, is mandatory.  If the template contains no query marks
+ *  and no argument is given, the function does nothing.
+ *
+ *  Placeholders have the following format and are not case sensitive:
+ *
+ *  <b>?[nn][m]t</b>
+ *
+ *  Where:
+ *
+ *  <b>?</b> Begining of placeholder for argument
+ *
+ *  <b>nn - <i>position</i></b> number of the argument to be replaced.
+ *      Argument 0 is the template itself and is not valid.
+ *  	The first argument after the SQL template is number 1
+ * 		If no number is given, arguments are taken sequentially.
+ *		Numbered replacements do not move the sequential argument pointer.
+ *		Arguments beyond the actual number present are considered null
+ *
+ *  <b>m - <i>modifier</i></b> [optional] indicates what to do if the argument is null
+ *  - m: mandatory, if the argument contains null, it will give a fatal error.
+ *  - z: null, if the argument is 0 or an empty string, it will be replaced by null
+ *
+ *  <b>t - <i>data type</i></b> the placeholder will be replaced by the argument as follows
+ *  - s: string, if not null, it will be escaped and enclosed in quotes
+ *  - i: integer, the integer value (intval() function) of the argument
+ *  - f: float, the floating point value (floatval() function) of the argument
+ *  - d: date, the argument will be assumed to represent a timestamp and it will be converted to yyyy-mm-dd and quoted
+ *  - b: boolean, anything evaluated to false will be 0, otherwise 1
+ *  - t: table prefix, the value of the global variable <i>$table_prefix</i>, escaped and unquoted
+ *       It takes no argument from the argument list
+ *
+ *  Example:
+ *  <code>
+ *  echo BuildSql('Insert into ?ttable (?s,?ns,?mi,?d,?ni,?i)','Something','',5,time(),0,null);
+ *  </code>
+ *  will return:
+ *  <pre>
+ *  Insert into wp_table ('Something',null,5,'2006-05-15',null,0)
+ *  </pre>
+ *
+ *  Note that placeholders do not need to be quoted, if quotes are required (strings or dates) they will be provided
+ *
+ *  @param string $query Template of SQL statement
+ *
+ *
+ *  @param mixed $value,... Values to be replaced into placeholders, sequentially unless stated otherwise
+ *
+ *  @return string properly formated and escaped SQL statement
+ *
+ *  The function will call bail if an unknown formatting character is found.
+ *  Unused arguments will produce warnings.
+ *  Missing arguments will be assumed null and will trigger a fatal error
+ *    if the placeholder has the mandatory modifier m.
+ *  There is no provision to put a literal ? into the SQL statement since the ? is not a valid SQL operator,
+ *    the only valid place for query marks are in literal string constants, which can be passed to this
+ *    function in an argument
+ */
 
+	function BuildSql($query) {
+		global $table_prefix;
+
+		$num_args = func_num_args();  // number of arguments available
+		$args_used = (1 << $num_args) -2; // bit mask to check if arguments are used
+	    /*                      +---------Anything up to first query mark
+	                            |      +------ query mark, start of placeholder
+	                            |      |    +---- position of argument
+	                            |      |    |     +----- modifier
+	                            |      |    |     |       +--- data type
+								|      |    |     |       |                     */
+		if (preg_match_all('|([^\?]*)(\?(\d?\d)?([mn]?)([sifdbt])?)*|i',$query,$matches,PREG_SET_ORDER)) {
+			$arg_pointer = 1; // sequential pointer to arguments
+			$s = '';  // output SQL statement
+			foreach($matches as $match) {
+				$NullIfEmpty = false;
+				$s .= $match[1];    //concatenate everything up to question mark
+				$type = strtolower($match[5]);  // read datatype
+	
+				// read the value of the argument
+				if ($type =='t') {
+					$value = $table_prefix;  	// t is a special case, it takes no argument from the list
+					$n = 0;
+				} elseif (intval($match[3])) {  // if there is a nn position modifier, read it 
+												// (null evaluates to 0, which is an invalid position anyhow)
+					$n = intval($match[3]);
+				} else {						// else, read the next sequential argument an increment the argument pointer
+					$n = $arg_pointer++;
+				}
+				if ($n) {		// if there was an argument to be read (notice t does not ask for an argument)
+					$args_used &= ~ (1 << $n);   // some bitwise magic to unset the bit position representing the argument
+					if ($n >= $num_args) $value = null;   // if argument requeste beyond those available, assume it null
+					else $value = func_get_arg($n);      // otherwise, read it
+				}
+	
+				switch ($match[4]) {  // read modifier
+					case 'm':			// mandatory?
+					case 'M':
+						if (is_null($value)) $this->bail("<p>Missing value for argument $n near: ... ${match[0]} ...</p>");
+						break;
+					case 'n':		// null if empty?
+					case 'N':
+						$NullIfEmpty = true;  // set flag
+						break;
+				}
+				switch($type) { // now we process $value according to datatype and $NullIfEmpty flag
+			       	case 's':
+					case 'S':
+						if ($NullIfEmpty and strlen($value) == 0) {
+							$s .='null';
+						} else {
+				       		$s .= "'" . $this->escape($value) . "'";  
+						}
+			       		break;
+			       	case 'i':
+					case 'I':
+						if ($NullIfEmpty and $value == 0) {
+							$s .='null';
+						} else {
+				       		$s .= intval($value);
+						}
+			       		break;
+			       	case 'f':
+					case 'F':
+						if ($NullIfEmpty and $value == 0) {
+							$s .='null';
+						} else {
+				       		$s .= floatval($value);
+						}
+			       		break;
+					case 'd':
+					case 'D':
+						if ($NullIfEmpty and empty($value)) {
+							$s .='null';
+						} else {
+							$s .= "'" . date('Y-m-d', $value) . "'";
+						}
+						break;
+					case 'b':
+					case 'B':  // booleans cannot be null
+						$s .= intval($value!=false);
+						break;
+					case 't':
+					case 'T':
+						$s .=  $this->escape($value);
+						break;
+					case null:
+						break;
+			       	default:
+						$this->bail('<p>Unknown formatting character in BuildSql: ' . $match[0] . " en SQL <br />[$query]</p>");
+				}
+	
+			}
+		}
+		// if all arguments used, $args_used will be zero
+		if ($args_used) {
+			for($i=1;$i<$num_args;$i++) {
+				$args_used >>=1; // bitmaks is shift right
+				// if right most bit still 1, it means it has not been used.
+				if ($args_used & 1) $this->bail("Argument $i not used");
+			}
+		}
+	
+		return $s;
+	}
+
 	// ==================================================================
 	//	Print SQL/DB error.
 
