5 * Less commonly used functions are placed here to reduce size of adodb.inc.php.
7 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
10 * @link https://adodb.org Project's web site and documentation
11 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
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
20 * @copyright 2000-2013 John Lim
21 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
24 // security - hide paths
25 if (!defined('ADODB_DIR')) die();
27 global $ADODB_INCLUDED_LIB;
28 $ADODB_INCLUDED_LIB = 1;
31 * Strip the ORDER BY clause from the outer SELECT.
37 function adodb_strip_order_by($sql)
39 $num = preg_match_all('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $matches, PREG_OFFSET_CAPTURE
);
42 list($last_order_by, $offset) = array_pop($matches[1]);
44 // If we find a ')' after the last order by, then it belongs to a
45 // sub-query, not the outer SQL statement and should not be stripped
46 if (strpos($sql, ')', $offset) === false) {
47 $sql = str_replace($last_order_by, '', $sql);
53 function adodb_probetypes($array,&$types,$probe=8)
55 // probe and guess the type
57 if ($probe > sizeof($array)) $max = sizeof($array);
61 for ($j=0;$j < $max; $j++
) {
68 if (isset($types[$i]) && $types[$i]=='C') continue;
70 //print " ($i ".$types[$i]. "$v) ";
73 if (!preg_match('/^[+-]{0,1}[0-9\.]+$/',$v)) {
74 $types[$i] = 'C'; // once C, always C
79 // If empty string, we presume is character
80 // test for integer for 1st row only
81 // after that it is up to testing other rows to prove
82 // that it is not an integer
83 if (strlen($v) == 0) $types[$i] = 'C';
84 if (strpos($v,'.') !== false) $types[$i] = 'N';
85 else $types[$i] = 'I';
89 if (strpos($v,'.') !== false) $types[$i] = 'N';
96 function adodb_transpose(&$arr, &$newarr, &$hdr, $fobjs)
98 $oldX = sizeof(reset($arr));
103 $hdr = array('Fields');
104 for ($y = 0; $y < $oldY; $y++
) {
105 $hdr[] = $arr[$y][0];
110 for ($x = $startx; $x < $oldX; $x++
) {
113 $newarr[] = array($o->name
);
117 for ($y = 0; $y < $oldY; $y++
) {
118 $newarr[$x-$startx][] = $arr[$y][$x];
124 function _adodb_replace($zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
126 // Add Quote around table name to support use of spaces / reserved keywords
127 $table=sprintf('%s%s%s', $zthis->nameQuote
,$table,$zthis->nameQuote
);
129 if (count($fieldArray) == 0) return 0;
131 if (!is_array($keyCol)) {
132 $keyCol = array($keyCol);
135 foreach($fieldArray as $k => $v) {
138 $fieldArray[$k] = $v;
139 } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null
)!=0) {
140 $v = $zthis->qstr($v);
141 $fieldArray[$k] = $v;
143 if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
145 // Add Quote around column name to support use of spaces / reserved keywords
146 $uSet .= sprintf(',%s%s%s=%s',$zthis->nameQuote
,$k,$zthis->nameQuote
,$v);
148 $uSet = ltrim($uSet, ',');
150 // Add Quote around column name in where clause
152 foreach ($keyCol as $v) {
153 if (isset($fieldArray[$v])) {
154 $where .= sprintf(' and %s%s%s=%s ', $zthis->nameQuote
,$v,$zthis->nameQuote
,$fieldArray[$v]);
158 $where = substr($where, 5);
161 if ($uSet && $where) {
162 $update = "UPDATE $table SET $uSet WHERE $where";
163 $rs = $zthis->Execute($update);
166 if ($zthis->poorAffectedRows
) {
167 // The Select count(*) wipes out any errors that the update would have returned.
168 // PHPLens Issue No: 5696
169 if ($zthis->ErrorNo()<>0) return 0;
171 // affected_rows == 0 if update field values identical to old values
172 // for mysql - which is silly.
173 $cnt = $zthis->GetOne("select count(*) from $table where $where");
174 if ($cnt > 0) return 1; // record already exists
176 if (($zthis->Affected_Rows()>0)) return 1;
182 $iCols = $iVals = '';
183 foreach($fieldArray as $k => $v) {
184 if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
186 // Add Quote around Column Name
187 $iCols .= sprintf(',%s%s%s',$zthis->nameQuote
,$k,$zthis->nameQuote
);
190 $iCols = ltrim($iCols, ',');
191 $iVals = ltrim($iVals, ',');
193 $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
194 $rs = $zthis->Execute($insert);
195 return ($rs) ?
2 : 0;
198 function _adodb_getmenu($zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
199 $size=0, $selectAttr='',$compareFields0=true)
201 global $ADODB_FETCH_MODE;
203 $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
205 $hasvalue = $zthis->FieldCount() > 1;
207 $compareFields0 = true;
211 while(!$zthis->EOF
) {
212 $zval = rtrim(reset($zthis->fields
));
214 if ($blank1stItem && $zval == "") {
220 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC
) {
221 // Get 2nd field's value regardless of its name
222 $zval2 = current(array_slice($zthis->fields
, 1, 1));
224 // With NUM or BOTH fetch modes, we have a numeric index
225 $zval2 = $zthis->fields
[1];
227 $zval2 = trim($zval2);
228 $value = 'value="' . htmlspecialchars($zval2) . '"';
231 /** @noinspection PhpUndefinedVariableInspection */
232 $s .= _adodb_getmenu_option($defstr, $compareFields0 ?
$zval : $zval2, $value, $zval);
237 return $s ."\n</select>\n";
240 function _adodb_getmenu_gp($zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
241 $size=0, $selectAttr='',$compareFields0=true)
243 global $ADODB_FETCH_MODE;
245 $s = _adodb_getmenu_select($name, $defstr, $blank1stItem, $multiple, $size, $selectAttr);
247 $hasvalue = $zthis->FieldCount() > 1;
248 $hasgroup = $zthis->FieldCount() > 2;
250 $compareFields0 = true;
256 while(!$zthis->EOF
) {
257 $zval = rtrim(reset($zthis->fields
));
260 if ($blank1stItem && $zval=="") {
266 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC
) {
267 // Get 2nd field's value regardless of its name
268 $fields = array_slice($zthis->fields
, 1);
269 $zval2 = current($fields);
271 $group = trim(next($fields));
274 // With NUM or BOTH fetch modes, we have a numeric index
275 $zval2 = $zthis->fields
[1];
277 $group = trim($zthis->fields
[2]);
280 $zval2 = trim($zval2);
281 $value = "value='".htmlspecialchars($zval2)."'";
284 if ($optgroup != $group) {
289 $s .="\n</optgroup>";
291 $s .="\n<optgroup label='". htmlspecialchars($group) ."'>";
294 /** @noinspection PhpUndefinedVariableInspection */
295 $s .= _adodb_getmenu_option($defstr, $compareFields0 ?
$zval : $zval2, $value, $zval);
300 // closing last optgroup
301 if($optgroup != null) {
302 $s .= "\n</optgroup>";
304 return $s ."\n</select>\n";
308 * Generate the opening SELECT tag for getmenu functions.
310 * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
312 * @param string $name
313 * @param string $defstr
314 * @param bool $blank1stItem
315 * @param bool $multiple
317 * @param string $selectAttr
319 * @return string HTML
321 function _adodb_getmenu_select($name, $defstr = '', $blank1stItem = true,
322 $multiple = false, $size = 0, $selectAttr = '')
324 if ($multiple ||
is_array($defstr)) {
328 $attr = ' multiple size="' . $size . '"';
329 if (!strpos($name,'[]')) {
333 $attr = ' size="' . $size . '"';
338 $html = '<select name="' . $name . '"' . $attr . ' ' . $selectAttr . '>';
340 if (is_string($blank1stItem)) {
341 $barr = explode(':',$blank1stItem);
342 if (sizeof($barr) == 1) {
345 $html .= "\n<option value=\"" . $barr[0] . "\">" . $barr[1] . "</option>";
347 $html .= "\n<option></option>";
355 * Print the OPTION tags for getmenu functions.
357 * ADOdb internal function, used by _adodb_getmenu() and _adodb_getmenu_gp().
359 * @param string $defstr Default values
360 * @param string $compare Value to compare against defaults
361 * @param string $value Ready-to-print `value="xxx"` (or empty) string
362 * @param string $display Display value
364 * @return string HTML
366 function _adodb_getmenu_option($defstr, $compare, $value, $display)
368 if ( is_array($defstr) && in_array($compare, $defstr)
369 ||
!is_array($defstr) && strcasecmp($compare, $defstr) == 0
371 $selected = ' selected="selected"';
376 return "\n<option $value$selected>" . htmlspecialchars($display) . '</option>';
380 Count the number of records this sql statement will return by using
381 query rewriting heuristics...
383 Does not work with UNIONs, except with postgresql and oracle.
388 $cnt = _adodb_getcount($conn, $sql);
391 function _adodb_getcount($zthis, $sql,$inputarr=false,$secs2cache=0)
396 * These databases require a "SELECT * FROM (SELECT" type
397 * statement to have an alias for the result
400 $requiresAliasArray = array('postgres9','postgres','mysql','mysqli','mssql','mssqlnative','sqlsrv');
401 if (in_array($zthis->databaseType
,$requiresAliasArray)
402 ||
in_array($zthis->dsnType
,$requiresAliasArray)
404 $requiresAlias = '_ADODB_ALIAS_';
407 if (!empty($zthis->_nestedSQL
)
408 ||
preg_match("/^\s*SELECT\s+DISTINCT/is", $sql)
409 ||
preg_match('/\s+GROUP\s+BY\s+/is',$sql)
410 ||
preg_match('/\s+UNION\s+/is',$sql)
412 $rewritesql = adodb_strip_order_by($sql);
414 // ok, has SELECT DISTINCT or GROUP BY so see if we can use a table alias
415 // but this is only supported by oracle and postgresql...
416 if ($zthis->dataProvider
== 'oci8') {
417 // Allow Oracle hints to be used for query optimization, Chris Wrye
418 if (preg_match('#/\\*+.*?\\*\\/#', $sql, $hint)) {
419 $rewritesql = "SELECT ".$hint[0]." COUNT(*) FROM (".$rewritesql.")";
421 $rewritesql = "SELECT COUNT(*) FROM (".$rewritesql.")";
423 $rewritesql = "SELECT COUNT(*) FROM ($rewritesql) $requiresAlias";
427 // Replace 'SELECT ... FROM' with 'SELECT COUNT(*) FROM'
428 // Parse the query one char at a time starting after the SELECT
429 // to find the FROM clause's position, ignoring any sub-queries.
430 $start = stripos($sql, 'SELECT') +
7;
431 if ($start === false) {
432 // Not a SELECT statement - probably should trigger an exception here
437 for ($pos = $start; $pos < $len; $pos++
) {
438 switch ($sql[$pos]) {
439 case '(': $numParentheses++
; continue 2;
440 case ')': $numParentheses--; continue 2;
442 // Ignore whatever is between parentheses (sub-queries)
443 if ($numParentheses > 0) {
446 // Exit loop if 'FROM' keyword was found
447 if (strtoupper(substr($sql, $pos, 4)) == 'FROM') {
451 $rewritesql = 'SELECT COUNT(*) ' . substr($sql, $pos);
453 // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails
454 // with mssql, access and postgresql. Also a good speedup optimization - skips sorting!
455 // also see PHPLens Issue No: 12752
456 $rewritesql = adodb_strip_order_by($rewritesql);
459 if (isset($rewritesql) && $rewritesql != $sql) {
460 if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) {
461 $rewritesql .= $limitarr[0];
465 // we only use half the time of secs2cache because the count can quickly
466 // become inaccurate if new records are added
467 $qryRecs = $zthis->CacheGetOne($secs2cache/2,$rewritesql,$inputarr);
470 $qryRecs = $zthis->GetOne($rewritesql,$inputarr);
472 if ($qryRecs !== false) return $qryRecs;
475 //--------------------------------------------
476 // query rewrite failed - so try slower way...
478 // strip off unneeded ORDER BY if no UNION
479 if (preg_match('/\s*UNION\s*/is', $sql)) {
482 $rewritesql = adodb_strip_order_by($sql);
485 if (preg_match('/\sLIMIT\s+[0-9]+/i',$sql,$limitarr)) {
486 $rewritesql .= $limitarr[0];
490 $rstest = $zthis->CacheExecute($secs2cache,$rewritesql,$inputarr);
491 if (!$rstest) $rstest = $zthis->CacheExecute($secs2cache,$sql,$inputarr);
493 $rstest = $zthis->Execute($rewritesql,$inputarr);
494 if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr);
497 $qryRecs = $rstest->RecordCount();
498 if ($qryRecs == -1) {
499 // some databases will return -1 on MoveLast() - change to MoveNext()
500 while(!$rstest->EOF
) {
503 $qryRecs = $rstest->_currentRow
;
506 if ($qryRecs == -1) return 0;
512 * Execute query with pagination including record count.
514 * This code might not work with SQL that has UNION in it.
515 * Also if you are using cachePageExecute(), there is a strong possibility that
516 * data will get out of sync. cachePageExecute() should only be used with
517 * tables that rarely change.
519 * @param ADOConnection $zthis Connection
520 * @param string $sql Query to execute
521 * @param int $nrows Number of rows per page
522 * @param int $page Page number to retrieve (1-based)
523 * @param array $inputarr Array of bind variables
524 * @param int $secs2cache Time-to-live of the cache (in seconds), 0 to force query execution
526 * @return ADORecordSet|bool
528 * @author Cornel G <conyg@fx.ro>
530 function _adodb_pageexecute_all_rows($zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0)
532 $atfirstpage = false;
535 // If an invalid nrows is supplied, assume a default value of 10 rows per page
536 if (!isset($nrows) ||
$nrows <= 0) $nrows = 10;
538 $qryRecs = _adodb_getcount($zthis,$sql,$inputarr,$secs2cache);
539 $lastpageno = (int) ceil($qryRecs / $nrows);
541 // Check whether $page is the last page or if we are trying to retrieve
542 // a page number greater than the last one.
543 if ($page >= $lastpageno) {
548 // If page number <= 1, then we are at the first page
549 if (empty($page) ||
$page <= 1) {
554 // We get the data we want
555 $offset = $nrows * ($page-1);
557 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
559 $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
562 // Before returning the RecordSet, we set the pagination properties we need
564 $rsreturn->_maxRecordCount
= $qryRecs;
565 $rsreturn->rowsPerPage
= $nrows;
566 $rsreturn->AbsolutePage($page);
567 $rsreturn->AtFirstPage($atfirstpage);
568 $rsreturn->AtLastPage($atlastpage);
569 $rsreturn->LastPageNo($lastpageno);
575 * Execute query with pagination without last page information.
577 * This code might not work with SQL that has UNION in it.
578 * Also if you are using cachePageExecute(), there is a strong possibility that
579 * data will get out of sync. cachePageExecute() should only be used with
580 * tables that rarely change.
582 * @param ADOConnection $zthis Connection
583 * @param string $sql Query to execute
584 * @param int $nrows Number of rows per page
585 * @param int $page Page number to retrieve (1-based)
586 * @param array $inputarr Array of bind variables
587 * @param int $secs2cache Time-to-live of the cache (in seconds), 0 to force query execution
589 * @return ADORecordSet|bool
591 * @author Iván Oliva
593 function _adodb_pageexecute_no_last_page($zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0)
595 $atfirstpage = false;
598 if (!isset($page) ||
$page <= 1) {
599 // If page number <= 1, then we are at the first page
604 // If an invalid nrows is supplied, we assume a default value of 10 rows per page
608 $pagecounteroffset = ($page * $nrows) - $nrows;
610 // To find out if there are more pages of rows, simply increase the limit or
611 // nrows by 1 and see if that number of records was returned. If it was,
612 // then we know there is at least one more page left, otherwise we are on
613 // the last page. Therefore allow non-Count() paging with single queries
614 // rather than three queries as was done before.
615 $test_nrows = $nrows +
1;
616 if ($secs2cache > 0) {
617 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
619 $rsreturn = $zthis->SelectLimit($sql, $test_nrows, $pagecounteroffset, $inputarr, $secs2cache);
622 // Now check to see if the number of rows returned was the higher value we asked for or not.
623 if ( $rsreturn->_numOfRows
== $test_nrows ) {
624 // Still at least 1 more row, so we are not on last page yet...
625 // Remove the last row from the RS.
626 $rsreturn->_numOfRows
= ( $rsreturn->_numOfRows
- 1 );
627 } elseif ( $rsreturn->_numOfRows
== 0 && $page > 1 ) {
628 // Likely requested a page that doesn't exist, so need to find the last
629 // page and return it. Revert to original method and loop through pages
630 // until we find some data...
631 $pagecounter = $page +
1;
635 while ($rstest && $rstest->EOF
&& $pagecounter > 0) {
638 $pagecounteroffset = $nrows * ($pagecounter - 1);
641 $rstest = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $pagecounteroffset, $inputarr);
644 $rstest = $zthis->SelectLimit($sql, $nrows, $pagecounteroffset, $inputarr, $secs2cache);
647 if ($rstest) $rstest->Close();
650 // If we are at the last page or beyond it, we are going to retrieve it
651 $page = $pagecounter;
653 // We have to do this again in case the last page is the same as
654 // the first page, that is, the recordset has only 1 page.
658 // We get the data we want
659 $offset = $nrows * ($page-1);
660 if ($secs2cache > 0) {
661 $rsreturn = $zthis->CacheSelectLimit($secs2cache, $sql, $nrows, $offset, $inputarr);
664 $rsreturn = $zthis->SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
666 } elseif ( $rsreturn->_numOfRows
< $test_nrows ) {
667 // Rows is less than what we asked for, so must be at the last page.
671 // Before returning the RecordSet, we set the pagination properties we need
673 $rsreturn->rowsPerPage
= $nrows;
674 $rsreturn->AbsolutePage($page);
675 $rsreturn->AtFirstPage($atfirstpage);
676 $rsreturn->AtLastPage($atlastpage);
682 * Performs case conversion and quoting of the given field name.
684 * See Global variable $ADODB_QUOTE_FIELDNAMES.
686 * @param ADOConnection $zthis
687 * @param string $fieldName
689 * @return string Quoted field name
691 function _adodb_quote_fieldname($zthis, $fieldName)
693 global $ADODB_QUOTE_FIELDNAMES;
695 // Case conversion - defaults to UPPER
696 $case = is_bool($ADODB_QUOTE_FIELDNAMES) ?
'UPPER' : $ADODB_QUOTE_FIELDNAMES;
699 $fieldName = strtolower($fieldName);
707 $fieldName = strtoupper($fieldName);
711 // Quote field if requested, or necessary (field contains space)
712 if ($ADODB_QUOTE_FIELDNAMES ||
strpos($fieldName, ' ') !== false ) {
713 if ($ADODB_QUOTE_FIELDNAMES === 'BRACKETS') {
714 return $zthis->leftBracket
. $fieldName . $zthis->rightBracket
;
716 return $zthis->nameQuote
. $fieldName . $zthis->nameQuote
;
723 function _adodb_getupdatesql(&$zthis, $rs, $arrFields, $forceUpdate=false, $force=2)
726 printf(ADODB_BAD_RS
,'GetUpdateSQL');
730 $fieldUpdatedCount = 0;
731 if (is_array($arrFields))
732 $arrFields = array_change_key_case($arrFields,CASE_UPPER
);
734 $hasnumeric = isset($rs->fields
[0]);
737 // Loop through all of the fields in the recordset
738 for ($i=0, $max=$rs->fieldCount(); $i < $max; $i++
) {
739 // Get the field from the recordset
740 $field = $rs->fetchField($i);
742 // If the recordset field is one
743 // of the fields passed in then process.
744 $upperfname = strtoupper($field->name
);
745 if (adodb_key_exists($upperfname, $arrFields, $force)) {
747 // If the existing field value in the recordset
748 // is different from the value passed in then
749 // go ahead and append the field name and new value to
752 if ($hasnumeric) $val = $rs->fields
[$i];
753 else if (isset($rs->fields
[$upperfname])) $val = $rs->fields
[$upperfname];
754 else if (isset($rs->fields
[$field->name
])) $val = $rs->fields
[$field->name
];
755 else if (isset($rs->fields
[strtolower($upperfname)])) $val = $rs->fields
[strtolower($upperfname)];
758 if ($forceUpdate ||
$val !== $arrFields[$upperfname]) {
759 // Set the counter for the number of fields that will be updated.
760 $fieldUpdatedCount++
;
762 // Based on the datatype of the field
763 // Format the value properly for the database
764 $type = $rs->metaType($field->type
);
766 if ($type == 'null') {
770 $fnameq = _adodb_quote_fieldname($zthis, $field->name
);
772 //********************************************************//
773 if (is_null($arrFields[$upperfname])
774 ||
(empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
775 ||
$arrFields[$upperfname] === $zthis->null2null
781 // // Ignore empty values. This is already handled in "adodb_key_exists" function.
786 $setFields .= $fnameq . " = null, ";
791 $arrFields[$upperfname] = "";
792 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
797 // set the value that was given in array, so you can give both null and empty values
798 if (is_null($arrFields[$upperfname]) ||
$arrFields[$upperfname] === $zthis->null2null
) {
799 $setFields .= $fnameq . " = null, ";
801 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
805 case ADODB_FORCE_NULL_AND_ZERO
:
811 $setFields .= $fnameq . ' = 0, ';
814 $setFields .= $fnameq . ' = null, ';
820 //********************************************************//
822 // we do this so each driver can customize the sql for
823 // DB specific column types.
824 // Oracle needs BLOB types to be handled with a returning clause
825 // postgres has special needs as well
826 $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
832 // If there were any modified fields then build the rest of the update query.
833 if ($fieldUpdatedCount > 0 ||
$forceUpdate) {
834 // Get the table name from the existing query.
835 if (!empty($rs->tableName
)) {
836 $tableName = $rs->tableName
;
838 preg_match("/FROM\s+".ADODB_TABLE_REGEX
."/is", $rs->sql
, $tableName);
839 $tableName = $tableName[1];
842 // Get the full where clause excluding the word "WHERE" from the existing query.
843 preg_match('/\sWHERE\s(.*)/is', $rs->sql
, $whereClause);
846 // not a good hack, improvements?
848 if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
849 else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
850 else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard));
851 else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/
853 $whereClause = array(false, false);
857 $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
860 $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
861 if (strlen($whereClause[1]) > 0) {
862 $sql .= ' WHERE '.$whereClause[1];
870 function adodb_key_exists($key, $arr,$force=2)
873 // the following is the old behaviour where null or empty fields are ignored
874 return (!empty($arr[$key])) ||
(isset($arr[$key]) && strlen($arr[$key])>0);
877 if (isset($arr[$key]))
880 return array_key_exists($key,$arr);
884 * There is a special case of this function for the oci8 driver.
885 * The proper way to handle an insert w/ a blob in oracle requires
886 * a returning clause with bind variables and a descriptor blob.
890 function _adodb_getinsertsql(&$zthis, $rs, $arrFields, $force=2)
892 static $cacheRS = false;
893 static $cacheSig = 0;
899 if (is_array($arrFields))
900 $arrFields = array_change_key_case($arrFields,CASE_UPPER
);
901 $fieldInsertedCount = 0;
903 if (is_string($rs)) {
904 //ok we have a table name
905 //try and get the column info ourself.
908 //we need an object for the recordSet
909 //because we have to call MetaType.
910 //php can't do a $rsclass::MetaType()
911 $rsclass = $zthis->rsPrefix
.$zthis->databaseType
;
912 $recordSet = new $rsclass(ADORecordSet
::DUMMY_QUERY_ID
, $zthis->fetchMode
);
913 $recordSet->connection
= $zthis;
915 if (is_string($cacheRS) && $cacheRS == $rs) {
916 $columns = $cacheCols;
918 $columns = $zthis->MetaColumns( $tableName );
919 $cacheRS = $tableName;
920 $cacheCols = $columns;
922 } else if (is_subclass_of($rs, 'adorecordset')) {
923 if (isset($rs->insertSig
) && is_integer($cacheRS) && $cacheRS == $rs->insertSig
) {
924 $columns = $cacheCols;
927 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++
)
928 $columns[] = $rs->FetchField($i);
929 $cacheRS = $cacheSig;
930 $cacheCols = $columns;
931 $rs->insertSig
= $cacheSig++
;
936 printf(ADODB_BAD_RS
,'GetInsertSQL');
940 // Loop through all of the fields in the recordset
941 foreach( $columns as $field ) {
942 $upperfname = strtoupper($field->name
);
943 if (adodb_key_exists($upperfname, $arrFields, $force)) {
945 $fnameq = _adodb_quote_fieldname($zthis, $field->name
);
946 $type = $recordSet->MetaType($field->type
);
948 /********************************************************/
949 if (is_null($arrFields[$upperfname])
950 ||
(empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
951 ||
$arrFields[$upperfname] === $zthis->null2null
955 case ADODB_FORCE_IGNORE
: // we must always set null if missing
959 case ADODB_FORCE_NULL
:
963 case ADODB_FORCE_EMPTY
:
965 $arrFields[$upperfname] = "";
966 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
970 case ADODB_FORCE_VALUE
:
971 //Set the value that was given in array, so you can give both null and empty values
972 if (is_null($arrFields[$upperfname]) ||
$arrFields[$upperfname] === $zthis->null2null
) {
975 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
979 case ADODB_FORCE_NULL_AND_ZERO
:
994 /*********************************************************/
996 //we do this so each driver can customize the sql for
997 //DB specific column types.
998 //Oracle needs BLOB types to be handled with a returning clause
999 //postgres has special needs as well
1000 $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
1006 // Set the counter for the number of fields that will be inserted.
1007 $fieldInsertedCount++
;
1009 // Get the name of the fields to insert
1010 $fields .= $fnameq . ", ";
1015 // If there were any inserted fields then build the rest of the insert query.
1016 if ($fieldInsertedCount <= 0) return false;
1018 // Get the table name from the existing query.
1020 if (!empty($rs->tableName
)) $tableName = $rs->tableName
;
1021 else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX
."/is", $rs->sql
, $tableName))
1022 $tableName = $tableName[1];
1027 // Strip off the comma and space on the end of both the fields
1028 // and their values.
1029 $fields = substr($fields, 0, -2);
1030 $values = substr($values, 0, -2);
1032 // Append the fields and their values to the insert query.
1033 return 'INSERT INTO '.$tableName.' ( '.$fields.' ) VALUES ( '.$values.' )';
1038 * This private method is used to help construct
1039 * the update/sql which is generated by GetInsertSQL and GetUpdateSQL.
1040 * It handles the string construction of 1 column -> sql string based on
1041 * the column type. We want to do 'safe' handling of BLOBs
1043 * @param string the type of sql we are trying to create
1045 * @param string column data type from the db::MetaType() method
1046 * @param string the column name
1047 * @param array the column value
1052 function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields)
1054 // Based on the datatype of the field
1055 // Format the value properly for the database
1058 //in order to handle Blobs correctly, we need
1059 //to do some magic for Oracle
1061 //we need to create a new descriptor to handle
1063 if (!empty($zthis->hasReturningInto
)) {
1064 if ($action == 'I') {
1065 $sql = 'empty_blob(), ';
1067 $sql = $fnameq . '=empty_blob(), ';
1069 //add the variable to the returning clause array
1070 //so the user can build this later in
1071 //case they want to add more to it
1072 $zthis->_returningArray
[$fname] = ':xx' . $fname . 'xx';
1074 if (empty($arrFields[$fname])) {
1075 if ($action == 'I') {
1076 $sql = 'empty_blob(), ';
1078 $sql = $fnameq . '=empty_blob(), ';
1081 //this is to maintain compatibility
1082 //with older adodb versions.
1083 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
1089 //we need to do some more magic here for long variables
1090 //to handle these correctly in oracle.
1092 //create a safe bind var name
1093 //to avoid conflicts w/ dupes.
1094 if (!empty($zthis->hasReturningInto
)) {
1095 if ($action == 'I') {
1096 $sql = ':xx' . $fname . 'xx, ';
1098 $sql = $fnameq . '=:xx' . $fname . 'xx, ';
1100 //add the variable to the returning clause array
1101 //so the user can build this later in
1102 //case they want to add more to it
1103 $zthis->_returningArray
[$fname] = ':xx' . $fname . 'xx';
1105 //this is to maintain compatibility
1106 //with older adodb versions.
1107 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
1112 $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
1119 function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $recurse=true)
1123 switch($zthis->dataProvider
) {
1125 if ($type == 'L') $type = 'C';
1128 return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields);
1137 $val = $zthis->qstr($arrFields[$fname]);
1141 $val = $zthis->DBDate($arrFields[$fname]);
1145 $val = $zthis->DBTimeStamp($arrFields[$fname]);
1149 $val = $arrFields[$fname];
1150 if (!is_numeric($val)) $val = str_replace(',', '.', (float)$val);
1155 $val = $arrFields[$fname];
1156 if (!is_numeric($val)) $val = (integer) $val;
1160 $val = str_replace(array("'"," ","("),"",$arrFields[$fname]); // basic sql injection defence
1161 if (empty($val)) $val = '0';
1165 if ($action == 'I') return $val . ", ";
1167 return $fnameq . "=" . $val . ", ";
1172 * Replaces standard _execute when debug mode is enabled
1174 * @param ADOConnection $zthis An ADOConnection object
1175 * @param string|string[] $sql A string or array of SQL statements
1176 * @param string[]|null $inputarr An optional array of bind parameters
1178 * @return handle|void A handle to the executed query
1180 function _adodb_debug_execute($zthis, $sql, $inputarr)
1182 // Unpack the bind parameters
1185 foreach ($inputarr as $kk => $vv) {
1186 if (is_string($vv) && strlen($vv) > 64) {
1187 $vv = substr($vv, 0, 64) . '...';
1190 $ss .= "($kk=>null) ";
1192 if (is_array($vv)) {
1193 $vv = sprintf("Array Of Values: [%s]", implode(',', $vv));
1195 $ss .= "($kk=>'$vv') ";
1201 $sqlTxt = is_array($sql) ?
$sql[0] : $sql;
1203 // Remove newlines and tabs, compress repeating spaces
1204 $sqlTxt = preg_replace('/\s+/', ' ', $sqlTxt);
1206 // check if running from browser or command-line
1207 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1209 $myDatabaseType = $zthis->databaseType
;
1210 if (!isset($zthis->dsnType
)) {
1211 // Append the PDO driver name
1212 $myDatabaseType .= '-' . $zthis->dsnType
;
1217 // Default formatting for passed parameter
1218 $ss = sprintf('<code class="adodb-debug">%s</code>', htmlspecialchars($ss));
1220 if ($zthis->debug
=== -1) {
1221 $outString = "<br class='adodb-debug'>(%s): %s %s<br class='adodb-debug'>";
1222 ADOConnection
::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false);
1223 } elseif ($zthis->debug
!== -99) {
1224 $outString = "<hr class='adodb-debug'>(%s): %s %s<hr class='adodb-debug'>";
1225 ADOConnection
::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false);
1229 if ($zthis->debug
!== -99) {
1230 $outString = sprintf("%s\n%s\n %s %s \n%s\n", str_repeat('-', 78), $myDatabaseType, $sqlTxt, $ss, str_repeat('-', 78));
1231 ADOConnection
::outp($outString, false);
1235 // Now execute the query
1236 $qID = $zthis->_query($sql, $inputarr);
1238 // Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
1239 // because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion
1240 if ($zthis->databaseType
== 'mssql') {
1241 // ErrorNo is a slow function call in mssql
1242 if ($emsg = $zthis->ErrorMsg()) {
1243 if ($err = $zthis->ErrorNo()) {
1244 if ($zthis->debug
=== -99) {
1245 ADOConnection
::outp("<hr>\n($myDatabaseType): " . htmlspecialchars($sqlTxt) . " $ss\n<hr>\n", false);
1248 ADOConnection
::outp($err . ': ' . $emsg);
1253 // Statement execution has failed
1254 if ($zthis->debug
=== -99) {
1256 $outString = "<hr class='adodb-debug'>(%s): %s %s<hr class='adodb-debug'>";
1257 ADOConnection
::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false);
1259 $outString = sprintf("%s\n%s\n %s %s \n%s\n",str_repeat('-',78),$myDatabaseType,$sqlTxt,$ss,str_repeat('-',78));
1260 ADOConnection
::outp($outString, false);
1264 // Send last error to output
1265 $errno = $zthis->ErrorNo();
1267 ADOConnection
::outp($errno . ': ' . $zthis->ErrorMsg());
1272 if ($qID === false ||
$zthis->debug
=== 99) {
1279 * Pretty print the debug_backtrace function
1281 * @param string[]|bool $printOrArr Whether to print the result directly or return the result
1282 * @param int $maximumDepth The maximum depth of the array to traverse
1283 * @param int $elementsToIgnore The backtrace array indexes to ignore
1284 * @param null|bool $ishtml True if we are in a CGI environment, false for CLI,
1285 * null to auto detect
1287 * @return string Formatted backtrace
1289 function _adodb_backtrace($printOrArr=true, $maximumDepth=9999, $elementsToIgnore=0, $ishtml=null)
1291 if (!function_exists('debug_backtrace')) {
1295 if ($ishtml === null) {
1296 // Auto determine if we in a CGI enviroment
1297 $html = (isset($_SERVER['HTTP_USER_AGENT']));
1302 $cgiString = "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>";
1303 $cliString = "%% line %4d, file: %s";
1304 $fmt = ($html) ?
$cgiString : $cliString;
1308 $s = ($html) ?
'<pre align=left>' : '';
1310 if (is_array($printOrArr)) {
1311 $traceArr = $printOrArr;
1313 $traceArr = debug_backtrace();
1316 // Remove first 2 elements that just show calls to adodb_backtrace
1317 array_shift($traceArr);
1318 array_shift($traceArr);
1320 // We want last element to have no indent
1321 $tabs = sizeof($traceArr) - 1;
1323 foreach ($traceArr as $arr) {
1324 if ($elementsToIgnore) {
1325 // Ignore array element at start of array
1326 $elementsToIgnore--;
1331 if ($maximumDepth < 0) {
1338 $s .= str_repeat($html ?
' ' : "\t", $tabs);
1342 $s .= '<font face="Courier New,Courier">';
1345 if (isset($arr['class'])) {
1346 $s .= $arr['class'] . '.';
1349 if (isset($arr['args'])) {
1350 foreach ($arr['args'] as $v) {
1353 } elseif (is_array($v)) {
1354 $args[] = 'Array[' . sizeof($v) . ']';
1355 } elseif (is_object($v)) {
1356 $args[] = 'Object:' . get_class($v);
1357 } elseif (is_bool($v)) {
1358 $args[] = $v ?
'true' : 'false';
1362 $v = substr($v, 0, $MAXSTRLEN);
1363 // Remove newlines and tabs, compress repeating spaces
1364 $v = preg_replace('/\s+/', ' ', $v);
1365 // Convert htmlchars (not sure why we do this in CLI)
1366 $str = htmlspecialchars($v);
1368 if (strlen($v) > $MAXSTRLEN) {
1376 $s .= $arr['function'] . '(' . implode(', ', $args) . ')';
1377 $s .= @sprintf
($fmt, $arr['line'], $arr['file'], basename($arr['file']));