Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

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