Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  // security - hide paths
   3  if (!defined('ADODB_DIR')) die();
   4  
   5  global $ADODB_INCLUDED_LIB;
   6  $ADODB_INCLUDED_LIB = 1;
   7  
   8  /*
   9    @version   v5.21.0  2021-02-27
  10    @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  11    @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  12    Released under both BSD license and Lesser GPL library license.
  13    Whenever there is any discrepancy between the two licenses,
  14    the BSD license will take precedence. See License.txt.
  15    Set tabs to 4 for best viewing.
  16  
  17    Less commonly used functions are placed here to reduce size of adodb.inc.php.
  18  */
  19  
  20  function adodb_strip_order_by($sql)
  21  {
  22  	 $rez = preg_match_all('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr);
  23  	 if ($arr) 
  24  	 {
  25  	 	 $tmp = array_pop($arr);
  26  	 	 $arr = [1=>array_pop($tmp)];
  27  	 }
  28  	 if ($arr)
  29  	 	 if (strpos($arr[1], '(') !== false) {
  30  	 	 	 $at = strpos($sql, $arr[1]);
  31  	 	 	 $cntin = 0;
  32  	 	 	 for ($i=$at, $max=strlen($sql); $i < $max; $i++) {
  33  	 	 	 	 $ch = $sql[$i];
  34  	 	 	 	 if ($ch == '(') {
  35  	 	 	 	 	 $cntin += 1;
  36  	 	 	 	 } elseif($ch == ')') {
  37  	 	 	 	 	 $cntin -= 1;
  38  	 	 	 	 	 if ($cntin < 0) {
  39  	 	 	 	 	 	 break;
  40  	 	 	 	 	 }
  41  	 	 	 	 }
  42  	 	 	 }
  43  	 	 	 $sql = substr($sql,0,$at).substr($sql,$i);
  44  	 	 } else {
  45  	 	 	 $sql = str_replace($arr[1], '', $sql);
  46  	 	 }
  47  
  48  	 return $sql;
  49  }
  50  
  51  if (false) {
  52  	 $sql = 'select * from (select a from b order by a(b),b(c) desc)';
  53  	 $sql = '(select * from abc order by 1)';
  54  	 die(adodb_strip_order_by($sql));
  55  }
  56  
  57  function adodb_probetypes(&$array,&$types,$probe=8)
  58  {
  59  // probe and guess the type
  60  	 $types = array();
  61  	 if ($probe > sizeof($array)) $max = sizeof($array);
  62  	 else $max = $probe;
  63  
  64  
  65  	 for ($j=0;$j < $max; $j++) {
  66  	 	 $row = $array[$j];
  67  	 	 if (!$row) break;
  68  	 	 $i = -1;
  69  	 	 foreach($row as $v) {
  70  	 	 	 $i += 1;
  71  
  72  	 	 	 if (isset($types[$i]) && $types[$i]=='C') continue;
  73  
  74  	 	 	 //print " ($i ".$types[$i]. "$v) ";
  75  	 	 	 $v = trim($v);
  76  
  77  	 	 	 if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
  78  	 	 	 	 $types[$i] = 'C'; // once C, always C
  79  
  80  	 	 	 	 continue;
  81  	 	 	 }
  82  	 	 	 if ($j == 0) {
  83  	 	 	 // If empty string, we presume is character
  84  	 	 	 // test for integer for 1st row only
  85  	 	 	 // after that it is up to testing other rows to prove
  86  	 	 	 // that it is not an integer
  87  	 	 	 	 if (strlen($v) == 0) $types[$i] = 'C';
  88  	 	 	 	 if (strpos($v,'.') !== false) $types[$i] = 'N';
  89  	 	 	 	 else  $types[$i] = 'I';
  90  	 	 	 	 continue;
  91  	 	 	 }
  92  
  93  	 	 	 if (strpos($v,'.') !== false) $types[$i] = 'N';
  94  
  95  	 	 }
  96  	 }
  97  
  98  }
  99  
 100  function  adodb_transpose(&$arr, &$newarr, &$hdr, &$fobjs)
 101  {
 102  	 $oldX = sizeof(reset($arr));
 103  	 $oldY = sizeof($arr);
 104  
 105  	 if ($hdr) {
 106  	 	 $startx = 1;
 107  	 	 $hdr = array('Fields');
 108  	 	 for ($y = 0; $y < $oldY; $y++) {
 109  	 	 	 $hdr[] = $arr[$y][0];
 110  	 	 }
 111  	 } else
 112  	 	 $startx = 0;
 113  
 114  	 for ($x = $startx; $x < $oldX; $x++) {
 115  	 	 if ($fobjs) {
 116  	 	 	 $o = $fobjs[$x];
 117  	 	 	 $newarr[] = array($o->name);
 118  	 	 } else
 119  	 	 	 $newarr[] = array();
 120  
 121  	 	 for ($y = 0; $y < $oldY; $y++) {
 122  	 	 	 $newarr[$x-$startx][] = $arr[$y][$x];
 123  	 	 }
 124  	 }
 125  }
 126  
 127  
 128  function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
 129  {
 130  	 // Add Quote around table name to support use of spaces / reserved keywords
 131  	 $table=sprintf('%s%s%s', $zthis->nameQuote,$table,$zthis->nameQuote);
 132  
 133  	 if (count($fieldArray) == 0) return 0;
 134  
 135  	 if (!is_array($keyCol)) {
 136  	 	 $keyCol = array($keyCol);
 137  	 }
 138  	 $uSet = '';
 139  	 foreach($fieldArray as $k => $v) {
 140  	 	 if ($v === null) {
 141  	 	 	 $v = 'NULL';
 142  	 	 	 $fieldArray[$k] = $v;
 143  	 	 } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
 144  	 	 	 $v = $zthis->qstr($v);
 145  	 	 	 $fieldArray[$k] = $v;
 146  	 	 }
 147  	 	 if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
 148  
 149  	 	 // Add Quote around column name to support use of spaces / reserved keywords
 150  	 	 $uSet .= sprintf(',%s%s%s=%s',$zthis->nameQuote,$k,$zthis->nameQuote,$v);
 151  	 }
 152  	 $uSet = ltrim($uSet, ',');
 153  
 154  	 // Add Quote around column name in where clause
 155  	 $where = '';
 156  	 foreach ($keyCol as $v) {
 157  	 	 if (isset($fieldArray[$v])) {
 158  	 	 	 $where .= sprintf(' and %s%s%s=%s ', $zthis->nameQuote,$v,$zthis->nameQuote,$fieldArray[$v]);
 159  	 	 }
 160  	 }
 161  	 if ($where) {
 162  	 	 $where = substr($where, 5);
 163  	 }
 164  
 165  	 if ($uSet && $where) {
 166  	 	 $update = "UPDATE $table SET $uSet WHERE $where";
 167  	 	 $rs = $zthis->Execute($update);
 168  
 169  	 	 if ($rs) {
 170  	 	 	 if ($zthis->poorAffectedRows) {
 171  	 	 	 	 // The Select count(*) wipes out any errors that the update would have returned.
 172  	 	 	 	 // PHPLens Issue No: 5696
 173  	 	 	 	 if ($zthis->ErrorNo()<>0) return 0;
 174  
 175  	 	 	 	 // affected_rows == 0 if update field values identical to old values
 176  	 	 	 	 // for mysql - which is silly.
 177  	 	 	 	 $cnt = $zthis->GetOne("select count(*) from $table where $where");
 178  	 	 	 	 if ($cnt > 0) return 1; // record already exists
 179  	 	 	 } else {
 180  	 	 	 	 if (($zthis->Affected_Rows()>0)) return 1;
 181  	 	 	 }
 182  	 	 } else
 183  	 	 	 return 0;
 184  	 }
 185  
 186  	 $iCols = $iVals = '';
 187  	 foreach($fieldArray as $k => $v) {
 188  	 	 if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
 189  
 190  	 	 // Add Quote around Column Name
 191  	 	 $iCols .= sprintf(',%s%s%s',$zthis->nameQuote,$k,$zthis->nameQuote);
 192  	 	 $iVals .= ",$v";
 193  	 }
 194  	 $iCols = ltrim($iCols, ',');
 195  	 $iVals = ltrim($iVals, ',');
 196  
 197  	 $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
 198  	 $rs = $zthis->Execute($insert);
 199  	 return ($rs) ? 2 : 0;
 200  }
 201  
 202  function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
 203  	 	 	 $size=0, $selectAttr='',$compareFields0=true)
 204  {
 205  	 global $ADODB_FETCH_MODE;
 206  
 207  	 $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
 208  
 209  	 $hasvalue = $zthis->FieldCount() > 1;
 210  	 if (!$hasvalue) {
 211  	 	 $compareFields0 = true;
 212  	 }
 213  
 214  	 $value = '';
 215  	 while(!$zthis->EOF) {
 216  	 	 $zval = rtrim(reset($zthis->fields));
 217  
 218  	 	 if ($blank1stItem && $zval == "") {
 219  	 	 	 $zthis->MoveNext();
 220  	 	 	 continue;
 221  	 	 }
 222  
 223  	 	 if ($hasvalue) {
 224  	 	 	 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC) {
 225  	 	 	 	 // Get 2nd field's value regardless of its name
 226  	 	 	 	 $zval2 = current(array_slice($zthis->fields, 1, 1));
 227  	 	 	 } else {
 228  	 	 	 	 // With NUM or BOTH fetch modes, we have a numeric index
 229  	 	 	 	 $zval2 = $zthis->fields[1];
 230  	 	 	 }
 231  	 	 	 $zval2 = trim($zval2);
 232  	 	 	 $value = 'value="' . htmlspecialchars($zval2) . '"';
 233  	 	 }
 234  
 235  	 	 $s .= _adodb_getmenu_option($defstr, $compareFields0 ? $zval : $zval2, $value, $zval);
 236  
 237  	 	 $zthis->MoveNext();
 238  	 } // while
 239  
 240  	 return $s ."\n</select>\n";
 241  }
 242  
 243  function _adodb_getmenu_gp(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
 244  	 	 	 $size=0, $selectAttr='',$compareFields0=true)
 245  {
 246  	 global $ADODB_FETCH_MODE;
 247  
 248  	 $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
 249  
 250  	 $hasvalue = $zthis->FieldCount() > 1;
 251  	 $hasgroup = $zthis->FieldCount() > 2;
 252  	 if (!$hasvalue) {
 253  	 	 $compareFields0 = true;
 254  	 }
 255  
 256  	 $value = '';
 257  	 $optgroup = null;
 258  	 $firstgroup = true;
 259  	 while(!$zthis->EOF) {
 260  	 	 $zval = rtrim(reset($zthis->fields));
 261  	 	 $group = '';
 262  
 263  	 	 if ($blank1stItem && $zval=="") {
 264  	 	 	 $zthis->MoveNext();
 265  	 	 	 continue;
 266  	 	 }
 267  
 268  	 	 if ($hasvalue) {
 269  	 	 	 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC) {
 270  	 	 	 	 // Get 2nd field's value regardless of its name
 271  	 	 	 	 $fields = array_slice($zthis->fields, 1);
 272  	 	 	 	 $zval2 = current($fields);
 273  	 	 	 	 if ($hasgroup) {
 274  	 	 	 	 	 $group = trim(next($fields));
 275  	 	 	 	 }
 276  	 	 	 } else {
 277  	 	 	 	 // With NUM or BOTH fetch modes, we have a numeric index
 278  	 	 	 	 $zval2 = $zthis->fields[1];
 279  	 	 	 	 if ($hasgroup) {
 280  	 	 	 	 	 $group = trim($zthis->fields[2]);
 281  	 	 	 	 }
 282  	 	 	 }
 283  	 	 	 $zval2 = trim($zval2);
 284  	 	 	 $value = "value='".htmlspecialchars($zval2)."'";
 285  	 	 }
 286  
 287  	 	 if ($optgroup != $group) {
 288  	 	 	 $optgroup = $group;
 289  	 	 	 if ($firstgroup) {
 290  	 	 	 	 $firstgroup = false;
 291  	 	 	 } else {
 292  	 	 	 	 $s .="\n</optgroup>";
 293  	 	 	 }
 294  	 	 	 $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
 295  	 	 }
 296  
 297  	 	 $s .= _adodb_getmenu_option($defstr, $compareFields0 ? $zval : $zval2, $value, $zval);
 298  
 299  	 	 $zthis->MoveNext();
 300  	 } // while
 301  
 302  	 // closing last optgroup
 303  	 if($optgroup != null) {
 304  	 	 $s .= "\n</optgroup>";
 305  	 }
 306  	 return $s ."\n</select>\n";
 307  }
 308  
 309  /**
 310   * Generate the opening SELECT tag for getmenu functions.
 311   *
 312   * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
 313   *
 314   * @param string $name
 315   * @param string $defstr
 316   * @param bool   $blank1stItem
 317   * @param bool   $multiple
 318   * @param int    $size
 319   * @param string $selectAttr
 320   *
 321   * @return string HTML
 322   */
 323  function _adodb_getmenu_select($name, $defstr = '', $blank1stItem = true,
 324  	 	 	 	 	 	 	    $multiple = false, $size = 0, $selectAttr = '')
 325  {
 326  	 if ($multiple || is_array($defstr)) {
 327  	 	 if ($size == 0 ) {
 328  	 	 	 $size = 5;
 329  	 	 }
 330  	 	 $attr = ' multiple size="' . $size . '"';
 331  	 	 if (!strpos($name,'[]')) {
 332  	 	 	 $name .= '[]';
 333  	 	 }
 334  	 } elseif ($size) {
 335  	 	 $attr = ' size="' . $size . '"';
 336  	 } else {
 337  	 	 $attr = '';
 338  	 }
 339  
 340  	 $html = '<select name="' . $name . '"' . $attr . ' ' . $selectAttr . '>';
 341  	 if ($blank1stItem) {
 342  	 	 if (is_string($blank1stItem))  {
 343  	 	 	 $barr = explode(':',$blank1stItem);
 344  	 	 	 if (sizeof($barr) == 1) {
 345  	 	 	 	 $barr[] = '';
 346  	 	 	 }
 347  	 	 	 $html .= "\n<option value=\"" . $barr[0] . "\">" . $barr[1] . "</option>";
 348  	 	 } else {
 349  	 	 	 $html .= "\n<option></option>";
 350  	 	 }
 351  	 }
 352  
 353  	 return $html;
 354  }
 355  
 356  /**
 357   * Print the OPTION tags for getmenu functions.
 358   *
 359   * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
 360   *
 361   * @param string $defstr  Default values
 362   * @param string $compare Value to compare against defaults
 363   * @param string $value   Ready-to-print `value="xxx"` (or empty) string
 364   * @param string $display Display value
 365   *
 366   * @return string HTML
 367   */
 368  function _adodb_getmenu_option($defstr, $compare, $value, $display)
 369  {
 370  	 if (   is_array($defstr) && in_array($compare, $defstr)
 371  	 	 || !is_array($defstr) && strcasecmp($compare, $defstr) == 0
 372  	 ) {
 373  	 	 $selected = ' selected="selected"';
 374  	 } else {
 375  	 	 $selected = '';
 376  	 }
 377  
 378  	 return "\n<option $value$selected>" . htmlspecialchars($display) . '</option>';
 379  }
 380  
 381  /*
 382  	 Count the number of records this sql statement will return by using
 383  	 query rewriting heuristics...
 384  
 385  	 Does not work with UNIONs, except with postgresql and oracle.
 386  
 387  	 Usage:
 388  
 389  	 $conn->Connect(...);
 390  	 $cnt = _adodb_getcount($conn, $sql);
 391  
 392  */
 393  function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0)
 394  {
 395  	 $qryRecs = 0;
 396  
 397  	  if (!empty($zthis->_nestedSQL) || preg_match("/^\s*SELECT\s+DISTINCT/is", $sql) ||
 398  	  	 preg_match('/\s+GROUP\s+BY\s+/is',$sql) ||
 399  	 	 preg_match('/\s+UNION\s+/is',$sql)) {
 400  
 401  	 	 $rewritesql = adodb_strip_order_by($sql);
 402  
 403  	 	 // ok, has SELECT DISTINCT or GROUP BY so see if we can use a table alias
 404  	 	 // but this is only supported by oracle and postgresql...
 405  	 	 if ($zthis->dataProvider == 'oci8') {
 406  	 	 	 // Allow Oracle hints to be used for query optimization, Chris Wrye
 407  	 	 	 if (preg_match('#/\\*+.*?\\*\\/#', $sql, $hint)) {
 408  	 	 	 	 $rewritesql = "SELECT ".$hint[0]." COUNT(*) FROM (".$rewritesql.")";
 409  	 	 	 } else
 410  	 	 	 	 $rewritesql = "SELECT COUNT(*) FROM (".$rewritesql.")";
 411  
 412  	 	 } else if (strncmp($zthis->databaseType,'postgres',8) == 0
 413  	 	 	 || strncmp($zthis->databaseType,'mysql',5) == 0
 414  	 	 || strncmp($zthis->databaseType,'mssql',5) == 0
 415  	 	 	 || strncmp($zthis->dsnType,'sqlsrv',5) == 0
 416  	 	 	 || strncmp($zthis->dsnType,'mssql',5) == 0
 417  	 	 ){
 418  	 	 	 $rewritesql = "SELECT COUNT(*) FROM ($rewritesql) _ADODB_ALIAS_";
 419  	 	 } else {
 420  	 	 	 $rewritesql = "SELECT COUNT(*) FROM ($rewritesql)";
 421  	 	 }
 422  	 } else {
 423  	 	 // now replace SELECT ... FROM with SELECT COUNT(*) FROM
 424  	 	 if ( strpos($sql, '_ADODB_COUNT') !== FALSE ) {
 425  	 	 	 $rewritesql = preg_replace('/^\s*?SELECT\s+_ADODB_COUNT(.*)_ADODB_COUNT\s/is','SELECT COUNT(*) ',$sql);
 426  	 	 } else {
 427  	 	 	 $rewritesql = preg_replace('/^\s*SELECT\s.*\s+FROM\s/Uis','SELECT COUNT(*) FROM ',$sql);
 428  	 	 }
 429  	 	 // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails
 430  	 	 // with mssql, access and postgresql. Also a good speedup optimization - skips sorting!
 431  	 	 // also see PHPLens Issue No: 12752
 432  	 	 $rewritesql = adodb_strip_order_by($rewritesql);
 433  	 }
 434  
 435  	 if (isset($rewritesql) && $rewritesql != $sql) {
 436  	 	 if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[0];
 437  
 438  	 	 if ($secs2cache) {
 439  	 	 	 // we only use half the time of secs2cache because the count can quickly
 440  	 	 	 // become inaccurate if new records are added
 441  	 	 	 $qryRecs = $zthis->CacheGetOne($secs2cache/2,$rewritesql,$inputarr);
 442  
 443  	 	 } else {
 444  	 	 	 $qryRecs = $zthis->GetOne($rewritesql,$inputarr);
 445  	   	 }
 446  	 	 if ($qryRecs !== false) return $qryRecs;
 447  	 }
 448  	 //--------------------------------------------
 449  	 // query rewrite failed - so try slower way...
 450  
 451  
 452  	 // strip off unneeded ORDER BY if no UNION
 453  	 if (preg_match('/\s*UNION\s*/is', $sql)) $rewritesql = $sql;
 454  	 else $rewritesql = $rewritesql = adodb_strip_order_by($sql);
 455  
 456  	 if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) $rewritesql .= $limitarr[0];
 457  
 458  	 if ($secs2cache) {
 459  	 	 $rstest = $zthis->CacheExecute($secs2cache,$rewritesql,$inputarr);
 460  	 	 if (!$rstest) $rstest = $zthis->CacheExecute($secs2cache,$sql,$inputarr);
 461  	 } else {
 462  	 	 $rstest = $zthis->Execute($rewritesql,$inputarr);
 463  	 	 if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr);
 464  	 }
 465  	 if ($rstest) {
 466  	 	 $qryRecs = $rstest->RecordCount();
 467  	 	 if ($qryRecs == -1) {
 468  	 	 	 // some databases will return -1 on MoveLast() - change to MoveNext()
 469  	 	 	 while(!$rstest->EOF) {
 470  	 	 	 	 $rstest->MoveNext();
 471  	 	 	 }
 472  	 	 	 $qryRecs = $rstest->_currentRow;
 473  	 	 }
 474  	 	 $rstest->Close();
 475  	 	 if ($qryRecs == -1) return 0;
 476  	 }
 477  	 return $qryRecs;
 478  }
 479  
 480  /*
 481   	 Code originally from "Cornel G" <conyg@fx.ro>
 482  
 483  	 This code might not work with SQL that has UNION in it
 484  
 485  	 Also if you are using CachePageExecute(), there is a strong possibility that
 486  	 data will get out of synch. use CachePageExecute() only with tables that
 487  	 rarely change.
 488  */
 489  function _adodb_pageexecute_all_rows(&$zthis, $sql, $nrows, $page,
 490  	 	 	 	 	 	 $inputarr=false, $secs2cache=0)
 491  {
 492  	 $atfirstpage = false;
 493  	 $atlastpage = false;
 494  	 $lastpageno=1;
 495  
 496  	 // If an invalid nrows is supplied,
 497  	 // we assume a default value of 10 rows per page
 498  	 if (!isset($nrows) || $nrows <= 0) $nrows = 10;
 499  
 500  	 $qryRecs = false; //count records for no offset
 501  
 502  	 $qryRecs = _adodb_getcount($zthis,$sql,$inputarr,$secs2cache);
 503  	 $lastpageno = (int) ceil($qryRecs / $nrows);
 504  	 $zthis->_maxRecordCount = $qryRecs;
 505  
 506  
 507  
 508  	 // ***** Here we check whether $page is the last page or
 509  	 // whether we are trying to retrieve
 510  	 // a page number greater than the last page number.
 511  	 if ($page >= $lastpageno) {
 512  	 	 $page = $lastpageno;
 513  	 	 $atlastpage = true;
 514  	 }
 515  
 516  	 // If page number <= 1, then we are at the first page
 517  	 if (empty($page) || $page <= 1) {
 518  	 	 $page = 1;
 519  	 	 $atfirstpage = true;
 520  	 }
 521  
 522  	 // We get the data we want
 523  	 $offset = $nrows * ($page-1);
 524  	 if ($secs2cache > 0)
 525  	 	 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
 526  	 else
 527  	 	 $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
 528  
 529  
 530  	 // Before returning the RecordSet, we set the pagination properties we need
 531  	 if ($rsreturn) {
 532  	 	 $rsreturn->_maxRecordCount = $qryRecs;
 533  	 	 $rsreturn->rowsPerPage = $nrows;
 534  	 	 $rsreturn->AbsolutePage($page);
 535  	 	 $rsreturn->AtFirstPage($atfirstpage);
 536  	 	 $rsreturn->AtLastPage($atlastpage);
 537  	 	 $rsreturn->LastPageNo($lastpageno);
 538  	 }
 539  	 return $rsreturn;
 540  }
 541  
 542  // Iván Oliva version
 543  function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0)
 544  {
 545  
 546  	 $atfirstpage = false;
 547  	 $atlastpage = false;
 548  
 549  	 if (!isset($page) || $page <= 1) {
 550  	 	 // If page number <= 1, then we are at the first page
 551  	 	 $page = 1;
 552  	 	 $atfirstpage = true;
 553  	 }
 554  	 if ($nrows <= 0) {
 555  	 	 // If an invalid nrows is supplied, we assume a default value of 10 rows per page
 556  	 	 $nrows = 10;
 557  	 }
 558  
 559  	 $pagecounteroffset = ($page * $nrows) - $nrows;
 560  
 561  	 // To find out if there are more pages of rows, simply increase the limit or
 562  	 // nrows by 1 and see if that number of records was returned. If it was,
 563  	 // then we know there is at least one more page left, otherwise we are on
 564  	 // the last page. Therefore allow non-Count() paging with single queries
 565  	 // rather than three queries as was done before.
 566  	 $test_nrows = $nrows + 1;
 567  	 if ($secs2cache > 0) {
 568  	 	 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
 569  	 } else {
 570  	 	 $rsreturn = $zthis->SelectLimit($sql, $test_nrows, $pagecounteroffset, $inputarr, $secs2cache);
 571  	 }
 572  
 573  	 // Now check to see if the number of rows returned was the higher value we asked for or not.
 574  	 if ( $rsreturn->_numOfRows == $test_nrows ) {
 575  	 	 // Still at least 1 more row, so we are not on last page yet...
 576  	 	 // Remove the last row from the RS.
 577  	 	 $rsreturn->_numOfRows = ( $rsreturn->_numOfRows - 1 );
 578  	 } elseif ( $rsreturn->_numOfRows == 0 && $page > 1 ) {
 579  	 	 // Likely requested a page that doesn't exist, so need to find the last
 580  	 	 // page and return it. Revert to original method and loop through pages
 581  	 	 // until we find some data...
 582  	 	 $pagecounter = $page + 1;
 583  	 	 $pagecounteroffset = ($pagecounter * $nrows) - $nrows;
 584  
 585  	 	 $rstest = $rsreturn;
 586  	 	 if ($rstest) {
 587  	 	 	 while ($rstest && $rstest->EOF && $pagecounter > 0) {
 588  	 	 	 	 $atlastpage = true;
 589  	 	 	 	 $pagecounter--;
 590  	 	 	 	 $pagecounteroffset = $nrows * ($pagecounter - 1);
 591  	 	 	 	 $rstest->Close();
 592  	 	 	 	 if ($secs2cache>0) {
 593  	 	 	 	 	 $rstest = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
 594  	 	 	 	 }
 595  	 	 	 	 else {
 596  	 	 	 	 	 $rstest = $zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache);
 597  	 	 	 	 }
 598  	 	 	 }
 599  	 	 	 if ($rstest) $rstest->Close();
 600  	 	 }
 601  	 	 if ($atlastpage) {
 602  	 	 	 // If we are at the last page or beyond it, we are going to retrieve it
 603  	 	 	 $page = $pagecounter;
 604  	 	 	 if ($page == 1) {
 605  	 	 	 	 // We have to do this again in case the last page is the same as
 606  	 	 	 	 // the first page, that is, the recordset has only 1 page.
 607  	 	 	 	 $atfirstpage = true;
 608  	 	 	 }
 609  	 	 }
 610  	 	 // We get the data we want
 611  	 	 $offset = $nrows * ($page-1);
 612  	 	 if ($secs2cache > 0) {
 613  	 	 	 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
 614  	 	 }
 615  	 	 else {
 616  	 	 	 $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
 617  	 	 }
 618  	 } elseif ( $rsreturn->_numOfRows < $test_nrows ) {
 619  	 	 // Rows is less than what we asked for, so must be at the last page.
 620  	 	 $atlastpage = true;
 621  	 }
 622  
 623  	 // Before returning the RecordSet, we set the pagination properties we need
 624  	 if ($rsreturn) {
 625  	 	 $rsreturn->rowsPerPage = $nrows;
 626  	 	 $rsreturn->AbsolutePage($page);
 627  	 	 $rsreturn->AtFirstPage($atfirstpage);
 628  	 	 $rsreturn->AtLastPage($atlastpage);
 629  	 }
 630  	 return $rsreturn;
 631  }
 632  
 633  function _adodb_getupdatesql(&$zthis, &$rs, $arrFields, $forceUpdate=false, $force=2)
 634  {
 635  	 global $ADODB_QUOTE_FIELDNAMES;
 636  
 637  	 if (!$rs) {
 638  	 	 printf(ADODB_BAD_RS,'GetUpdateSQL');
 639  	 	 return false;
 640  	 }
 641  
 642  	 $fieldUpdatedCount = 0;
 643  	 if (is_array($arrFields))
 644  	 	 $arrFields = array_change_key_case($arrFields,CASE_UPPER);
 645  
 646  	 $hasnumeric = isset($rs->fields[0]);
 647  	 $setFields = '';
 648  
 649  	 // Loop through all of the fields in the recordset
 650  	 for ($i=0, $max=$rs->fieldCount(); $i < $max; $i++) {
 651  	 	 // Get the field from the recordset
 652  	 	 $field = $rs->fetchField($i);
 653  
 654  	 	 // If the recordset field is one
 655  	 	 // of the fields passed in then process.
 656  	 	 $upperfname = strtoupper($field->name);
 657  	 	 if (adodb_key_exists($upperfname, $arrFields, $force)) {
 658  
 659  	 	 	 // If the existing field value in the recordset
 660  	 	 	 // is different from the value passed in then
 661  	 	 	 // go ahead and append the field name and new value to
 662  	 	 	 // the update query.
 663  
 664  	 	 	 if ($hasnumeric) $val = $rs->fields[$i];
 665  	 	 	 else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname];
 666  	 	 	 else if (isset($rs->fields[$field->name])) $val =  $rs->fields[$field->name];
 667  	 	 	 else if (isset($rs->fields[strtolower($upperfname)])) $val =  $rs->fields[strtolower($upperfname)];
 668  	 	 	 else $val = '';
 669  
 670  	 	 	 if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) {
 671  	 	 	 	 // Set the counter for the number of fields that will be updated.
 672  	 	 	 	 $fieldUpdatedCount++;
 673  
 674  	 	 	 	 // Based on the datatype of the field
 675  	 	 	 	 // Format the value properly for the database
 676  	 	 	 	 $type = $rs->metaType($field->type);
 677  
 678  	 	 	 	 if ($type == 'null') {
 679  	 	 	 	 	 $type = 'C';
 680  	 	 	 	 }
 681  
 682  	 	 	 	 if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
 683  	 	 	 	 	 switch ($ADODB_QUOTE_FIELDNAMES) {
 684  	 	 	 	 	 	 case 'BRACKETS':
 685  	 	 	 	 	 	 	 $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break;
 686  	 	 	 	 	 	 case 'LOWER':
 687  	 	 	 	 	 	 	 $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
 688  	 	 	 	 	 	 case 'NATIVE':
 689  	 	 	 	 	 	 	 $fnameq = $zthis->nameQuote.$field->name.$zthis->nameQuote;break;
 690  	 	 	 	 	 	 case 'UPPER':
 691  	 	 	 	 	 	 default:
 692  	 	 	 	 	 	 	 $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break;
 693  	 	 	 	 	 }
 694  	 	 	 	 } else {
 695  	 	 	 	 	 $fnameq = $upperfname;
 696  	 	 	 	 }
 697  
 698  	 	 	 	 //********************************************************//
 699  	 	 	 	 if (is_null($arrFields[$upperfname])
 700  	 	 	 	 	 || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
 701  	 	 	 	 	 || $arrFields[$upperfname] === $zthis->null2null
 702  	 	 	 	 	 ) {
 703  
 704  	 	 	 	 	 switch ($force) {
 705  
 706  	 	 	 	 	 	 //case 0:
 707  	 	 	 	 	 	 //	 // Ignore empty values. This is already handled in "adodb_key_exists" function.
 708  	 	 	 	 	 	 //	 break;
 709  
 710  	 	 	 	 	 	 case 1:
 711  	 	 	 	 	 	 	 // set null
 712  	 	 	 	 	 	 	 $setFields .= $fnameq . " = null, ";
 713  	 	 	 	 	 	 	 break;
 714  
 715  	 	 	 	 	 	 case 2:
 716  	 	 	 	 	 	 	 // set empty
 717  	 	 	 	 	 	 	 $arrFields[$upperfname] = "";
 718  	 	 	 	 	 	 	 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
 719  	 	 	 	 	 	 	 break;
 720  
 721  	 	 	 	 	 	 default:
 722  	 	 	 	 	 	 case 3:
 723  	 	 	 	 	 	 	 // set the value that was given in array, so you can give both null and empty values
 724  	 	 	 	 	 	 	 if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
 725  	 	 	 	 	 	 	 	 $setFields .= $fnameq . " = null, ";
 726  	 	 	 	 	 	 	 } else {
 727  	 	 	 	 	 	 	 	 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
 728  	 	 	 	 	 	 	 }
 729  	 	 	 	 	 	 	 break;
 730  
 731  	 	 	 	 	 	 case ADODB_FORCE_NULL_AND_ZERO:
 732  
 733  	 	 	 	 	 	 	 switch ($type) {
 734  	 	 	 	 	 	 	 	 case 'N':
 735  	 	 	 	 	 	 	 	 case 'I':
 736  	 	 	 	 	 	 	 	 case 'L':
 737  	 	 	 	 	 	 	 	 	 $setFields .= $fnameq . ' = 0, ';
 738  	 	 	 	 	 	 	 	 	 break;
 739  	 	 	 	 	 	 	 	 default:
 740  	 	 	 	 	 	 	 	 	 $setFields .= $fnameq . ' = null, ';
 741  	 	 	 	 	 	 	 	 	 break;
 742  	 	 	 	 	 	 	 }
 743  	 	 	 	 	 	 	 break;
 744  
 745  	 	 	 	 	 }
 746  	 	 	 	 //********************************************************//
 747  	 	 	 	 } else {
 748  	 	 	 	 	 // we do this so each driver can customize the sql for
 749  	 	 	 	 	 // DB specific column types.
 750  	 	 	 	 	 // Oracle needs BLOB types to be handled with a returning clause
 751  	 	 	 	 	 // postgres has special needs as well
 752  	 	 	 	 	 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
 753  	 	 	 	 }
 754  	 	 	 }
 755  	 	 }
 756  	 }
 757  
 758  	 // If there were any modified fields then build the rest of the update query.
 759  	 if ($fieldUpdatedCount > 0 || $forceUpdate) {
 760  	 	 // Get the table name from the existing query.
 761  	 	 if (!empty($rs->tableName)) {
 762  	 	 	 $tableName = $rs->tableName;
 763  	 	 } else {
 764  	 	 	 preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName);
 765  	 	 	 $tableName = $tableName[1];
 766  	 	 }
 767  
 768  	 	 // Get the full where clause excluding the word "WHERE" from the existing query.
 769  	 	 preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause);
 770  
 771  	 	 $discard = false;
 772  	 	 // not a good hack, improvements?
 773  	 	 if ($whereClause) {
 774  	 	 	 #var_dump($whereClause);
 775  	 	 	 if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
 776  	 	 	 else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
 777  	 	 	 else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard));
 778  	 	 	 else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/
 779  	 	 } else {
 780  	 	 	 $whereClause = array(false, false);
 781  	 	 }
 782  
 783  	 	 if ($discard) {
 784  	 	 	 $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
 785  	 	 }
 786  
 787  	 	 $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
 788  	 	 if (strlen($whereClause[1]) > 0) {
 789  	 	 	 $sql .= ' WHERE '.$whereClause[1];
 790  	 	 }
 791  	 	 return $sql;
 792  	 } else {
 793  	 	 return false;
 794  	 }
 795  }
 796  
 797  function adodb_key_exists($key, &$arr,$force=2)
 798  {
 799  	 if ($force<=0) {
 800  	 	 // the following is the old behaviour where null or empty fields are ignored
 801  	 	 return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0);
 802  	 }
 803  
 804  	 if (isset($arr[$key])) 
 805  	 	 return true;
 806  	 ## null check below
 807  	 return array_key_exists($key,$arr);
 808  }
 809  
 810  /**
 811   * There is a special case of this function for the oci8 driver.
 812   * The proper way to handle an insert w/ a blob in oracle requires
 813   * a returning clause with bind variables and a descriptor blob.
 814   *
 815   *
 816   */
 817  function _adodb_getinsertsql(&$zthis, &$rs, $arrFields, $force=2)
 818  {
 819  static $cacheRS = false;
 820  static $cacheSig = 0;
 821  static $cacheCols;
 822  	 global $ADODB_QUOTE_FIELDNAMES;
 823  
 824  	 $tableName = '';
 825  	 $values = '';
 826  	 $fields = '';
 827  	 $recordSet = null;
 828  	 if (is_array($arrFields))
 829  	 	 $arrFields = array_change_key_case($arrFields,CASE_UPPER);
 830  	 $fieldInsertedCount = 0;
 831  
 832  	 if (is_string($rs)) {
 833  	 	 //ok we have a table name
 834  	 	 //try and get the column info ourself.
 835  	 	 $tableName = $rs;
 836  
 837  	 	 //we need an object for the recordSet
 838  	 	 //because we have to call MetaType.
 839  	 	 //php can't do a $rsclass::MetaType()
 840  	 	 $rsclass = $zthis->rsPrefix.$zthis->databaseType;
 841  	 	 $recordSet = new $rsclass(-1,$zthis->fetchMode);
 842  	 	 $recordSet->connection = $zthis;
 843  
 844  	 	 if (is_string($cacheRS) && $cacheRS == $rs) {
 845  	 	 	 $columns = $cacheCols;
 846  	 	 } else {
 847  	 	 	 $columns = $zthis->MetaColumns( $tableName );
 848  	 	 	 $cacheRS = $tableName;
 849  	 	 	 $cacheCols = $columns;
 850  	 	 }
 851  	 } else if (is_subclass_of($rs, 'adorecordset')) {
 852  	 	 if (isset($rs->insertSig) && is_integer($cacheRS) && $cacheRS == $rs->insertSig) {
 853  	 	 	 $columns = $cacheCols;
 854  	 	 } else {
 855  	 	 	 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
 856  	 	 	 	 $columns[] = $rs->FetchField($i);
 857  	 	 	 $cacheRS = $cacheSig;
 858  	 	 	 $cacheCols = $columns;
 859  	 	 	 $rs->insertSig = $cacheSig++;
 860  	 	 }
 861  	 	 $recordSet = $rs;
 862  
 863  	 } else {
 864  	 	 printf(ADODB_BAD_RS,'GetInsertSQL');
 865  	 	 return false;
 866  	 }
 867  
 868  	 // Loop through all of the fields in the recordset
 869  	 foreach( $columns as $field ) {
 870  	 	 $upperfname = strtoupper($field->name);
 871  	 	 if (adodb_key_exists($upperfname,$arrFields,$force)) {
 872  	 	 	 $bad = false;
 873  	 	 	 if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
 874  	 	 	 	 switch ($ADODB_QUOTE_FIELDNAMES) {
 875  	 	 	 	 case 'BRACKETS':
 876  	 	 	 	 	 $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break;
 877  	 	 	 	 case 'LOWER':
 878  	 	 	 	 	 $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
 879  	 	 	 	 case 'NATIVE':
 880  	 	 	 	 	 $fnameq = $zthis->nameQuote.$field->name.$zthis->nameQuote;break;
 881  	 	 	 	 case 'UPPER':
 882  	 	 	 	 default:
 883  	 	 	 	 	 $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break;
 884  	 	 	 	 }
 885  	 	 	 } else
 886  	 	 	 	 $fnameq = $upperfname;
 887  
 888  	 	 	 $type = $recordSet->MetaType($field->type);
 889  
 890              /********************************************************/
 891              if (is_null($arrFields[$upperfname])
 892                  || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
 893                  || $arrFields[$upperfname] === $zthis->null2null
 894  	 	 	 	 )
 895                 {
 896                      switch ($force) {
 897  
 898                          case ADODB_FORCE_IGNORE: // we must always set null if missing
 899  	 	 	 	 	 	 	 $bad = true;
 900  	 	 	 	 	 	 	 break;
 901  
 902                          case ADODB_FORCE_NULL:
 903                              $values  .= "null, ";
 904                          break;
 905  
 906                          case ADODB_FORCE_EMPTY:
 907                              //Set empty
 908                              $arrFields[$upperfname] = "";
 909  	 	 	 	 	 	 	 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
 910                          break;
 911  
 912  	 	 	 	 	 	 default:
 913                          case ADODB_FORCE_VALUE:
 914                              //Set the value that was given in array, so you can give both null and empty values
 915  	 	 	 	 	 	 	 if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
 916  	 	 	 	 	 	 	 	 $values  .= "null, ";
 917  	 	 	 	 	 	 	 } else {
 918  	 	 	 	 	 	 	 	 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
 919               	 	 	 	 }
 920                	 	 	 break;
 921  
 922  	 	 	 	 	 	 case ADODB_FORCE_NULL_AND_ZERO:
 923  	 	 	 	 	 	 	 switch ($type)
 924  	 	 	 	 	 	 	 {
 925  	 	 	 	 	 	 	 	 case 'N':
 926  	 	 	 	 	 	 	 	 case 'I':
 927  	 	 	 	 	 	 	 	 case 'L':
 928  	 	 	 	 	 	 	 	 	 $values .= '0, ';
 929  	 	 	 	 	 	 	 	 	 break;
 930  	 	 	 	 	 	 	 	 default:
 931  	 	 	 	 	 	 	 	 	 $values .= "null, ";
 932  	 	 	 	 	 	 	 	 	 break;
 933  	 	 	 	 	 	 	 }
 934  	 	 	 	 	 	 break;
 935  
 936               	 	 } // switch
 937  
 938              /*********************************************************/
 939  	 	 	 } else {
 940  	 	 	 	 //we do this so each driver can customize the sql for
 941  	 	 	 	 //DB specific column types.
 942  	 	 	 	 //Oracle needs BLOB types to be handled with a returning clause
 943  	 	 	 	 //postgres has special needs as well
 944  	 	 	 	 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
 945  	 	 	 }
 946  
 947  	 	 	 if ($bad) continue;
 948  	 	 	 // Set the counter for the number of fields that will be inserted.
 949  	 	 	 $fieldInsertedCount++;
 950  
 951  
 952  	 	 	 // Get the name of the fields to insert
 953  	 	 	 $fields .= $fnameq . ", ";
 954  	 	 }
 955  	 }
 956  
 957  
 958  	 // If there were any inserted fields then build the rest of the insert query.
 959  	 if ($fieldInsertedCount <= 0)  return false;
 960  
 961  	 // Get the table name from the existing query.
 962  	 if (!$tableName) {
 963  	 	 if (!empty($rs->tableName)) $tableName = $rs->tableName;
 964  	 	 else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName))
 965  	 	 	 $tableName = $tableName[1];
 966  	 	 else
 967  	 	 	 return false;
 968  	 }
 969  
 970  	 // Strip off the comma and space on the end of both the fields
 971  	 // and their values.
 972  	 $fields = substr($fields, 0, -2);
 973  	 $values = substr($values, 0, -2);
 974  
 975  	 // Append the fields and their values to the insert query.
 976  	 return 'INSERT INTO '.$tableName.' ( '.$fields.' ) VALUES ( '.$values.' )';
 977  }
 978  
 979  
 980  /**
 981   * This private method is used to help construct
 982   * the update/sql which is generated by GetInsertSQL and GetUpdateSQL.
 983   * It handles the string construction of 1 column -> sql string based on
 984   * the column type.  We want to do 'safe' handling of BLOBs
 985   *
 986   * @param string the type of sql we are trying to create
 987   *                'I' or 'U'.
 988   * @param string column data type from the db::MetaType() method
 989   * @param string the column name
 990   * @param array the column value
 991   *
 992   * @return string
 993   *
 994   */
 995  function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields)
 996  {
 997      $sql = '';
 998  
 999      // Based on the datatype of the field
1000      // Format the value properly for the database
1001      switch($type) {
1002      case 'B':
1003          //in order to handle Blobs correctly, we need
1004          //to do some magic for Oracle
1005  
1006          //we need to create a new descriptor to handle
1007          //this properly
1008          if (!empty($zthis->hasReturningInto)) {
1009              if ($action == 'I') {
1010                  $sql = 'empty_blob(), ';
1011              } else {
1012                  $sql = $fnameq. '=empty_blob(), ';
1013              }
1014              //add the variable to the returning clause array
1015              //so the user can build this later in
1016              //case they want to add more to it
1017              $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
1018          } else if (empty($arrFields[$fname])){
1019              if ($action == 'I') {
1020                  $sql = 'empty_blob(), ';
1021              } else {
1022                  $sql = $fnameq. '=empty_blob(), ';
1023              }
1024          } else {
1025              //this is to maintain compatibility
1026              //with older adodb versions.
1027  	 	 	 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
1028          }
1029          break;
1030  
1031      case "X":
1032          //we need to do some more magic here for long variables
1033          //to handle these correctly in oracle.
1034  
1035          //create a safe bind var name
1036          //to avoid conflicts w/ dupes.
1037         if (!empty($zthis->hasReturningInto)) {
1038              if ($action == 'I') {
1039                  $sql = ':xx'.$fname.'xx, ';
1040              } else {
1041                  $sql = $fnameq.'=:xx'.$fname.'xx, ';
1042              }
1043              //add the variable to the returning clause array
1044              //so the user can build this later in
1045              //case they want to add more to it
1046              $zthis->_returningArray[$fname] = ':xx'.$fname.'xx';
1047          } else {
1048              //this is to maintain compatibility
1049              //with older adodb versions.
1050  	 	 	 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
1051          }
1052          break;
1053  
1054      default:
1055  	 	 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq,  $arrFields, false);
1056          break;
1057      }
1058  
1059      return $sql;
1060  }
1061  
1062  function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $recurse=true)
1063  {
1064  
1065  	 if ($recurse) {
1066  	 	 switch($zthis->dataProvider)  {
1067  	 	 case 'postgres':
1068  	 	 	 if ($type == 'L') $type = 'C';
1069  	 	 	 break;
1070  	 	 case 'oci8':
1071  	 	 	 return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields);
1072  
1073  	 	 }
1074  	 }
1075  
1076  	 switch($type) {
1077  	 	 case "C":
1078  	 	 case "X":
1079  	 	 case 'B':
1080  	 	 	 $val = $zthis->qstr($arrFields[$fname]);
1081  	 	 	 break;
1082  
1083  	 	 case "D":
1084  	 	 	 $val = $zthis->DBDate($arrFields[$fname]);
1085  	 	 	 break;
1086  
1087  	 	 case "T":
1088  	 	 	 $val = $zthis->DBTimeStamp($arrFields[$fname]);
1089  	 	 	 break;
1090  
1091  	 	 case "N":
1092  	 	     $val = $arrFields[$fname];
1093  	 	 	 if (!is_numeric($val)) $val = str_replace(',', '.', (float)$val);
1094  	 	     break;
1095  
1096  	 	 case "I":
1097  	 	 case "R":
1098  	 	     $val = $arrFields[$fname];
1099  	 	 	 if (!is_numeric($val)) $val = (integer) $val;
1100  	 	     break;
1101  
1102  	 	 default:
1103  	 	 	 $val = str_replace(array("'"," ","("),"",$arrFields[$fname]); // basic sql injection defence
1104  	 	 	 if (empty($val)) $val = '0';
1105  	 	 	 break;
1106  	 }
1107  
1108  	 if ($action == 'I') return $val . ", ";
1109  
1110  	 return $fnameq . "=" . $val  . ", ";
1111  }
1112  
1113  
1114  
1115  function _adodb_debug_execute(&$zthis, $sql, $inputarr)
1116  {
1117  	 $ss = '';
1118  	 if ($inputarr) {
1119  	 	 foreach($inputarr as $kk=>$vv) {
1120  	 	 	 if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
1121  	 	 	 if (is_null($vv)) $ss .= "($kk=>null) ";
1122  	 	 	 else $ss .= "($kk=>'$vv') ";
1123  	 	 }
1124  	 	 $ss = "[ $ss ]";
1125  	 }
1126  	 $sqlTxt = is_array($sql) ? $sql[0] : $sql;
1127  	 /*str_replace(', ','##1#__^LF',is_array($sql) ? $sql[0] : $sql);
1128  	 $sqlTxt = str_replace(',',', ',$sqlTxt);
1129  	 $sqlTxt = str_replace('##1#__^LF', ', ' ,$sqlTxt);
1130  	 */
1131  	 // check if running from browser or command-line
1132  	 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1133  
1134  	 $dbt = $zthis->databaseType;
1135  	 if (isset($zthis->dsnType)) $dbt .= '-'.$zthis->dsnType;
1136  	 if ($inBrowser) {
1137  	 	 if ($ss) {
1138  	 	 	 $ss = '<code>'.htmlspecialchars($ss).'</code>';
1139  	 	 }
1140  	 	 if ($zthis->debug === -1)
1141  	 	 	 ADOConnection::outp( "<br>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<br>\n",false);
1142  	 	 else if ($zthis->debug !== -99)
1143  	 	 	 ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1144  	 } else {
1145  	 	 $ss = "\n   ".$ss;
1146  	 	 if ($zthis->debug !== -99)
1147  	 	 	 ADOConnection::outp("-----<hr>\n($dbt): ".$sqlTxt." $ss\n-----<hr>\n",false);
1148  	 }
1149  
1150  	 $qID = $zthis->_query($sql,$inputarr);
1151  
1152  	 /*
1153  	 	 Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
1154  	 	 because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion
1155  	 */
1156  	 if ($zthis->databaseType == 'mssql') {
1157  	 // ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
1158  
1159  	 	 if($emsg = $zthis->ErrorMsg()) {
1160  	 	 	 if ($err = $zthis->ErrorNo()) {
1161  	 	 	 	 if ($zthis->debug === -99)
1162  	 	 	 	 	 ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1163  
1164  	 	 	 	 ADOConnection::outp($err.': '.$emsg);
1165  	 	 	 }
1166  	 	 }
1167  	 } else if (!$qID) {
1168  
1169  	 	 if ($zthis->debug === -99)
1170  	 	 	 	 if ($inBrowser) ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." &nbsp; $ss\n<hr>\n",false);
1171  	 	 	 	 else ADOConnection::outp("-----<hr>\n($dbt): ".$sqlTxt."$ss\n-----<hr>\n",false);
1172  
1173  	 	 ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
1174  	 }
1175  
1176  	 if ($zthis->debug === 99) _adodb_backtrace(true,9999,2);
1177  	 return $qID;
1178  }
1179  
1180  # pretty print the debug_backtrace function
1181  function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0,$ishtml=null)
1182  {
1183  	 if (!function_exists('debug_backtrace')) return '';
1184  
1185  	 if ($ishtml === null) $html =  (isset($_SERVER['HTTP_USER_AGENT']));
1186  	 else $html = $ishtml;
1187  
1188  	 $fmt =  ($html) ? "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
1189  
1190  	 $MAXSTRLEN = 128;
1191  
1192  	 $s = ($html) ? '<pre align=left>' : '';
1193  
1194  	 if (is_array($printOrArr)) $traceArr = $printOrArr;
1195  	 else $traceArr = debug_backtrace();
1196  	 array_shift($traceArr);
1197  	 array_shift($traceArr);
1198  	 $tabs = sizeof($traceArr)-2;
1199  
1200  	 foreach ($traceArr as $arr) {
1201  	 	 if ($skippy) {$skippy -= 1; continue;}
1202  	 	 $levels -= 1;
1203  	 	 if ($levels < 0) break;
1204  
1205  	 	 $args = array();
1206  	 	 for ($i=0; $i < $tabs; $i++) $s .=  ($html) ? ' &nbsp; ' : "\t";
1207  	 	 $tabs -= 1;
1208  	 	 if ($html) $s .= '<font face="Courier New,Courier">';
1209  	 	 if (isset($arr['class'])) $s .= $arr['class'].'.';
1210  	 	 if (isset($arr['args']))
1211  	 	  foreach($arr['args'] as $v) {
1212  	 	 	 if (is_null($v)) $args[] = 'null';
1213  	 	 	 else if (is_array($v)) $args[] = 'Array['.sizeof($v).']';
1214  	 	 	 else if (is_object($v)) $args[] = 'Object:'.get_class($v);
1215  	 	 	 else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
1216  	 	 	 else {
1217  	 	 	 	 $v = (string) @$v;
1218  	 	 	 	 $str = htmlspecialchars(str_replace(array("\r","\n"),' ',substr($v,0,$MAXSTRLEN)));
1219  	 	 	 	 if (strlen($v) > $MAXSTRLEN) $str .= '...';
1220  	 	 	 	 $args[] = $str;
1221  	 	 	 }
1222  	 	 }
1223  	 	 $s .= $arr['function'].'('.implode(', ',$args).')';
1224  
1225  
1226  	 	 $s .= @sprintf($fmt, $arr['line'],$arr['file'],basename($arr['file']));
1227  
1228  	 	 $s .= "\n";
1229  	 }
1230  	 if ($html) $s .= '</pre>';
1231  	 if ($printOrArr) print $s;
1232  
1233  	 return $s;
1234  }
1235  /*
1236  function _adodb_find_from($sql)
1237  {
1238  
1239  	 $sql = str_replace(array("\n","\r"), ' ', $sql);
1240  	 $charCount = strlen($sql);
1241  
1242  	 $inString = false;
1243  	 $quote = '';
1244  	 $parentheseCount = 0;
1245  	 $prevChars = '';
1246  	 $nextChars = '';
1247  
1248  
1249  	 for($i = 0; $i < $charCount; $i++) {
1250  
1251      	 $char = substr($sql,$i,1);
1252  	     $prevChars = substr($sql,0,$i);
1253      	 $nextChars = substr($sql,$i+1);
1254  
1255  	 	 if((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === false) {
1256  	 	 	 $quote = $char;
1257  	 	 	 $inString = true;
1258  	 	 }
1259  
1260  	 	 elseif((($char == "'" || $char == '"' || $char == '`') && substr($prevChars,-1,1) != '\\') && $inString === true && $quote == $char) {
1261  	 	 	 $quote = "";
1262  	 	 	 $inString = false;
1263  	 	 }
1264  
1265  	 	 elseif($char == "(" && $inString === false)
1266  	 	 	 $parentheseCount++;
1267  
1268  	 	 elseif($char == ")" && $inString === false && $parentheseCount > 0)
1269  	 	 	 $parentheseCount--;
1270  
1271  	 	 elseif($parentheseCount <= 0 && $inString === false && $char == " " && strtoupper(substr($prevChars,-5,5)) == " FROM")
1272  	 	 	 return $i;
1273  
1274  	 }
1275  }
1276  */