Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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