See Release Notes
Long Term Support Release
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)." $ss\n<br>\n",false); 1204 else if ($zthis->debug !== -99) 1205 ADOConnection::outp( "<hr>\n($dbt): ".htmlspecialchars($sqlTxt)." $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)." $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)." $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) ? ' ' : "\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 */
title
Description
Body
title
Description
Body
title
Description
Body
title
Body