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