Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
36 kaklik 1
<?php 
2
/*
3
 * Set tabs to 4 for best viewing.
4
 * 
5
 * Latest version is available at http://adodb.sourceforge.net
6
 * 
7
 * This is the main include file for ADOdb.
8
 * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
9
 *
10
 * The ADOdb files are formatted so that doxygen can be used to generate documentation.
11
 * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
12
 */
13
 
14
/**
15
	\mainpage 	
16
 
17
	 @version V4.80 8 Mar 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
18
 
19
	Released under both BSD license and Lesser GPL library license. You can choose which license
20
	you prefer.
21
 
22
	PHP's database access functions are not standardised. This creates a need for a database 
23
	class library to hide the differences between the different database API's (encapsulate 
24
	the differences) so we can easily switch databases.
25
 
26
	We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
27
	Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
28
	ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
29
	other databases via ODBC.
30
 
31
	Latest Download at http://adodb.sourceforge.net/
32
 
33
 */
34
 
35
 if (!defined('_ADODB_LAYER')) {
36
 	define('_ADODB_LAYER',1);
37
 
38
	//==============================================================================================	
39
	// CONSTANT DEFINITIONS
40
	//==============================================================================================	
41
 
42
 
43
	/** 
44
	 * Set ADODB_DIR to the directory where this file resides...
45
	 * This constant was formerly called $ADODB_RootPath
46
	 */
47
	if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
48
 
49
	//==============================================================================================	
50
	// GLOBAL VARIABLES
51
	//==============================================================================================	
52
 
53
	GLOBAL 
54
		$ADODB_vers, 		// database version
55
		$ADODB_COUNTRECS,	// count number of records returned - slows down query
56
		$ADODB_CACHE_DIR,	// directory to cache recordsets
57
		$ADODB_EXTENSION,   // ADODB extension installed
58
		$ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
59
	 	$ADODB_FETCH_MODE;	// DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
60
 
61
	//==============================================================================================	
62
	// GLOBAL SETUP
63
	//==============================================================================================	
64
 
65
	$ADODB_EXTENSION = defined('ADODB_EXTENSION');
66
 
67
	//********************************************************//
68
	/*
69
	Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
70
	Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
71
 
72
 
73
		1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
74
		2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
75
		3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values.
76
	*/
77
        define('ADODB_FORCE_IGNORE',0);
78
        define('ADODB_FORCE_NULL',1);
79
        define('ADODB_FORCE_EMPTY',2);
80
        define('ADODB_FORCE_VALUE',3);
81
    //********************************************************//
82
 
83
 
84
	if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
85
 
86
		define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
87
 
88
	// allow [ ] @ ` " and . in table names
89
		define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
90
 
91
	// prefetching used by oracle
92
		if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
93
 
94
 
95
	/*
96
	Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
97
	This currently works only with mssql, odbc, oci8po and ibase derived drivers.
98
 
99
 
100
		1 = assoc uppercase field names. $rs->fields['ORDERID']
101
		2 = use native-case field names. $rs->fields['OrderID']
102
	*/
103
 
104
		define('ADODB_FETCH_DEFAULT',0);
105
		define('ADODB_FETCH_NUM',1);
106
		define('ADODB_FETCH_ASSOC',2);
107
		define('ADODB_FETCH_BOTH',3);
108
 
109
		if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
110
 
111
		// PHP's version scheme makes converting to numbers difficult - workaround
112
		$_adodb_ver = (float) PHP_VERSION;
113
		if ($_adodb_ver >= 5.0) {
114
			define('ADODB_PHPVER',0x5000);
115
		} else if ($_adodb_ver > 4.299999) { # 4.3
116
			define('ADODB_PHPVER',0x4300);
117
		} else if ($_adodb_ver > 4.199999) { # 4.2
118
			define('ADODB_PHPVER',0x4200);
119
		} else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) {
120
			define('ADODB_PHPVER',0x4050);
121
		} else {
122
			define('ADODB_PHPVER',0x4000);
123
		}
124
	}
125
 
126
	//if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
127
 
128
 
129
	/**
130
	 	Accepts $src and $dest arrays, replacing string $data
131
	*/
132
	function ADODB_str_replace($src, $dest, $data)
133
	{
134
		if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
135
 
136
		$s = reset($src);
137
		$d = reset($dest);
138
		while ($s !== false) {
139
			$data = str_replace($s,$d,$data);
140
			$s = next($src);
141
			$d = next($dest);
142
		}
143
		return $data;
144
	}
145
 
146
	function ADODB_Setup()
147
	{
148
	GLOBAL 
149
		$ADODB_vers, 		// database version
150
		$ADODB_COUNTRECS,	// count number of records returned - slows down query
151
		$ADODB_CACHE_DIR,	// directory to cache recordsets
152
	 	$ADODB_FETCH_MODE,
153
		$ADODB_FORCE_TYPE;
154
 
155
		$ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
156
		$ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
157
 
158
 
159
		if (!isset($ADODB_CACHE_DIR)) {
160
			$ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
161
		} else {
162
			// do not accept url based paths, eg. http:/ or ftp:/
163
			if (strpos($ADODB_CACHE_DIR,'://') !== false) 
164
				die("Illegal path http:// or ftp://");
165
		}
166
 
167
 
168
		// Initialize random number generator for randomizing cache flushes
169
		srand(((double)microtime())*1000000);
170
 
171
		/**
172
		 * ADODB version as a string.
173
		 */
174
		$ADODB_vers = 'V4.80 8 Mar 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
175
 
176
		/**
177
		 * Determines whether recordset->RecordCount() is used. 
178
		 * Set to false for highest performance -- RecordCount() will always return -1 then
179
		 * for databases that provide "virtual" recordcounts...
180
		 */
181
		if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true; 
182
	}
183
 
184
 
185
	//==============================================================================================	
186
	// CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
187
	//==============================================================================================	
188
 
189
	ADODB_Setup();
190
 
191
	//==============================================================================================	
192
	// CLASS ADOFieldObject
193
	//==============================================================================================	
194
	/**
195
	 * Helper class for FetchFields -- holds info on a column
196
	 */
197
	class ADOFieldObject { 
198
		var $name = '';
199
		var $max_length=0;
200
		var $type="";
201
/*
202
		// additional fields by dannym... (danny_milo@yahoo.com)
203
		var $not_null = false; 
204
		// actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
205
		// so we can as well make not_null standard (leaving it at "false" does not harm anyways)
206
 
207
		var $has_default = false; // this one I have done only in mysql and postgres for now ... 
208
			// others to come (dannym)
209
		var $default_value; // default, if any, and supported. Check has_default first.
210
*/
211
	}
212
 
213
 
214
 
215
	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
216
	{
217
		//print "Errorno ($fn errno=$errno m=$errmsg) ";
218
		$thisConnection->_transOK = false;
219
		if ($thisConnection->_oldRaiseFn) {
220
			$fn = $thisConnection->_oldRaiseFn;
221
			$fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
222
		}
223
	}
224
 
225
	//==============================================================================================	
226
	// CLASS ADOConnection
227
	//==============================================================================================	
228
 
229
	/**
230
	 * Connection object. For connecting to databases, and executing queries.
231
	 */ 
232
	class ADOConnection {
233
	//
234
	// PUBLIC VARS 
235
	//
236
	var $dataProvider = 'native';
237
	var $databaseType = '';		/// RDBMS currently in use, eg. odbc, mysql, mssql					
238
	var $database = '';			/// Name of database to be used.	
239
	var $host = ''; 			/// The hostname of the database server	
240
	var $user = ''; 			/// The username which is used to connect to the database server. 
241
	var $password = ''; 		/// Password for the username. For security, we no longer store it.
242
	var $debug = false; 		/// if set to true will output sql statements
243
	var $maxblobsize = 262144; 	/// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
244
	var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase	
245
	var $substr = 'substr';		/// substring operator
246
	var $length = 'length';		/// string length ofperator
247
	var $random = 'rand()';		/// random function
248
	var $upperCase = 'upper';		/// uppercase function
249
	var $fmtDate = "'Y-m-d'";	/// used by DBDate() as the default date format used by the database
250
	var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
251
	var $true = '1'; 			/// string that represents TRUE for a database
252
	var $false = '0'; 			/// string that represents FALSE for a database
253
	var $replaceQuote = "\\'"; 	/// string to use to replace quotes
254
	var $nameQuote = '"';		/// string to use to quote identifiers and names
255
	var $charSet=false; 		/// character set to use - only for interbase, postgres and oci8
256
	var $metaDatabasesSQL = '';
257
	var $metaTablesSQL = '';
258
	var $uniqueOrderBy = false; /// All order by columns have to be unique
259
	var $emptyDate = '&nbsp;';
260
	var $emptyTimeStamp = '&nbsp;';
261
	var $lastInsID = false;
262
	//--
263
	var $hasInsertID = false; 		/// supports autoincrement ID?
264
	var $hasAffectedRows = false; 	/// supports affected rows for update/delete?
265
	var $hasTop = false;			/// support mssql/access SELECT TOP 10 * FROM TABLE
266
	var $hasLimit = false;			/// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
267
	var $readOnly = false; 			/// this is a readonly database - used by phpLens
268
	var $hasMoveFirst = false;  /// has ability to run MoveFirst(), scrolling backwards
269
	var $hasGenID = false; 		/// can generate sequences using GenID();
270
	var $hasTransactions = true; /// has transactions
271
	//--
272
	var $genID = 0; 			/// sequence id used by GenID();
273
	var $raiseErrorFn = false; 	/// error function to call
274
	var $isoDates = false; /// accepts dates in ISO format
275
	var $cacheSecs = 3600; /// cache for 1 hour
276
	var $sysDate = false; /// name of function that returns the current date
277
	var $sysTimeStamp = false; /// name of function that returns the current timestamp
278
	var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
279
 
280
	var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
281
	var $numCacheHits = 0; 
282
	var $numCacheMisses = 0;
283
	var $pageExecuteCountRows = true;
284
	var $uniqueSort = false; /// indicates that all fields in order by must be unique
285
	var $leftOuter = false; /// operator to use for left outer join in WHERE clause
286
	var $rightOuter = false; /// operator to use for right outer join in WHERE clause
287
	var $ansiOuter = false; /// whether ansi outer join syntax supported
288
	var $autoRollback = false; // autoRollback on PConnect().
289
	var $poorAffectedRows = false; // affectedRows not working or unreliable
290
 
291
	var $fnExecute = false;
292
	var $fnCacheExecute = false;
293
	var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
294
	var $rsPrefix = "ADORecordSet_";
295
 
296
	var $autoCommit = true; 	/// do not modify this yourself - actually private
297
	var $transOff = 0; 			/// temporarily disable transactions
298
	var $transCnt = 0; 			/// count of nested transactions
299
 
300
	var $fetchMode=false;
301
	 //
302
	 // PRIVATE VARS
303
	 //
304
	var $_oldRaiseFn =  false;
305
	var $_transOK = null;
306
	var $_connectionID	= false;	/// The returned link identifier whenever a successful database connection is made.	
307
	var $_errorMsg = false;		/// A variable which was used to keep the returned last error message.  The value will
308
								/// then returned by the errorMsg() function	
309
	var $_errorCode = false;	/// Last error code, not guaranteed to be used - only by oci8					
310
	var $_queryID = false;		/// This variable keeps the last created result link identifier
311
 
312
	var $_isPersistentConnection = false;	/// A boolean variable to state whether its a persistent connection or normal connection.	*/
313
	var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
314
	var $_evalAll = false;
315
	var $_affected = false;
316
	var $_logsql = false;
317
 
318
	/**
319
	 * Constructor
320
	 */
321
	function ADOConnection()			
322
	{
323
		die('Virtual Class -- cannot instantiate');
324
	}
325
 
326
	function Version()
327
	{
328
	global $ADODB_vers;
329
 
330
		return (float) substr($ADODB_vers,1);
331
	}
332
 
333
	/**
334
		Get server version info...
335
 
336
		@returns An array with 2 elements: $arr['string'] is the description string, 
337
			and $arr[version] is the version (also a string).
338
	*/
339
	function ServerInfo()
340
	{
341
		return array('description' => '', 'version' => '');
342
	}
343
 
344
	function IsConnected()
345
	{
346
    	return !empty($this->_connectionID);
347
	}
348
 
349
	function _findvers($str)
350
	{
351
		if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
352
		else return '';
353
	}
354
 
355
	/**
356
	* All error messages go through this bottleneck function.
357
	* You can define your own handler by defining the function name in ADODB_OUTP.
358
	*/
359
	function outp($msg,$newline=true)
360
	{
361
	global $ADODB_FLUSH,$ADODB_OUTP;
362
 
363
		if (defined('ADODB_OUTP')) {
364
			$fn = ADODB_OUTP;
365
			$fn($msg,$newline);
366
			return;
367
		} else if (isset($ADODB_OUTP)) {
368
			$fn = $ADODB_OUTP;
369
			$fn($msg,$newline);
370
			return;
371
		}
372
 
373
		if ($newline) $msg .= "<br>\n";
374
 
375
		if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
376
		else echo strip_tags($msg);
377
 
378
 
379
		if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan 
380
 
381
	}
382
 
383
	function Time()
384
	{
385
		$rs =& $this->_Execute("select $this->sysTimeStamp");
386
		if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
387
 
388
		return false;
389
	}
390
 
391
	/**
392
	 * Connect to database
393
	 *
394
	 * @param [argHostname]		Host to connect to
395
	 * @param [argUsername]		Userid to login
396
	 * @param [argPassword]		Associated password
397
	 * @param [argDatabaseName]	database
398
	 * @param [forceNew]		force new connection
399
	 *
400
	 * @return true or false
401
	 */	  
402
	function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) 
403
	{
404
		if ($argHostname != "") $this->host = $argHostname;
405
		if ($argUsername != "") $this->user = $argUsername;
406
		if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
407
		if ($argDatabaseName != "") $this->database = $argDatabaseName;		
408
 
409
		$this->_isPersistentConnection = false;	
410
		if ($forceNew) {
411
			if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
412
		} else {
413
			 if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
414
		}
415
		if (isset($rez)) {
416
			$err = $this->ErrorMsg();
417
			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
418
			$ret = false;
419
		} else {
420
			$err = "Missing extension for ".$this->dataProvider;
421
			$ret = 0;
422
		}
423
		if ($fn = $this->raiseErrorFn) 
424
			$fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
425
 
426
 
427
		$this->_connectionID = false;
428
		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
429
		return $ret;
430
	}	
431
 
432
	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
433
	{
434
		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
435
	}
436
 
437
 
438
	/**
439
	 * Always force a new connection to database - currently only works with oracle
440
	 *
441
	 * @param [argHostname]		Host to connect to
442
	 * @param [argUsername]		Userid to login
443
	 * @param [argPassword]		Associated password
444
	 * @param [argDatabaseName]	database
445
	 *
446
	 * @return true or false
447
	 */	  
448
	function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") 
449
	{
450
		return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
451
	}
452
 
453
	/**
454
	 * Establish persistent connect to database
455
	 *
456
	 * @param [argHostname]		Host to connect to
457
	 * @param [argUsername]		Userid to login
458
	 * @param [argPassword]		Associated password
459
	 * @param [argDatabaseName]	database
460
	 *
461
	 * @return return true or false
462
	 */	
463
	function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
464
	{
465
		if (defined('ADODB_NEVER_PERSIST')) 
466
			return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
467
 
468
		if ($argHostname != "") $this->host = $argHostname;
469
		if ($argUsername != "") $this->user = $argUsername;
470
		if ($argPassword != "") $this->password = $argPassword;
471
		if ($argDatabaseName != "") $this->database = $argDatabaseName;		
472
 
473
		$this->_isPersistentConnection = true;	
474
		if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
475
		if (isset($rez)) {
476
			$err = $this->ErrorMsg();
477
			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
478
			$ret = false;
479
		} else {
480
			$err = "Missing extension for ".$this->dataProvider;
481
			$ret = 0;
482
		}
483
		if ($fn = $this->raiseErrorFn) {
484
			$fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
485
		}
486
 
487
		$this->_connectionID = false;
488
		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
489
		return $ret;
490
	}
491
 
492
	// Format date column in sql string given an input format that understands Y M D
493
	function SQLDate($fmt, $col=false)
494
	{	
495
		if (!$col) $col = $this->sysDate;
496
		return $col; // child class implement
497
	}
498
 
499
	/**
500
	 * Should prepare the sql statement and return the stmt resource.
501
	 * For databases that do not support this, we return the $sql. To ensure
502
	 * compatibility with databases that do not support prepare:
503
	 *
504
	 *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
505
	 *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
506
	 *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
507
	 *
508
	 * @param sql	SQL to send to database
509
	 *
510
	 * @return return FALSE, or the prepared statement, or the original sql if
511
	 * 			if the database does not support prepare.
512
	 *
513
	 */	
514
	function Prepare($sql)
515
	{
516
		return $sql;
517
	}
518
 
519
	/**
520
	 * Some databases, eg. mssql require a different function for preparing
521
	 * stored procedures. So we cannot use Prepare().
522
	 *
523
	 * Should prepare the stored procedure  and return the stmt resource.
524
	 * For databases that do not support this, we return the $sql. To ensure
525
	 * compatibility with databases that do not support prepare:
526
	 *
527
	 * @param sql	SQL to send to database
528
	 *
529
	 * @return return FALSE, or the prepared statement, or the original sql if
530
	 * 			if the database does not support prepare.
531
	 *
532
	 */	
533
	function PrepareSP($sql,$param=true)
534
	{
535
		return $this->Prepare($sql,$param);
536
	}
537
 
538
	/**
539
	* PEAR DB Compat
540
	*/
541
	function Quote($s)
542
	{
543
		return $this->qstr($s,false);
544
	}
545
 
546
	/**
547
	 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
548
	*/
549
	function QMagic($s)
550
	{
551
		return $this->qstr($s,get_magic_quotes_gpc());
552
	}
553
 
554
	function q(&$s)
555
	{
556
		$s = $this->qstr($s,false);
557
	}
558
 
559
	/**
560
	* PEAR DB Compat - do not use internally. 
561
	*/
562
	function ErrorNative()
563
	{
564
		return $this->ErrorNo();
565
	}
566
 
567
 
568
   /**
569
	* PEAR DB Compat - do not use internally. 
570
	*/
571
	function nextId($seq_name)
572
	{
573
		return $this->GenID($seq_name);
574
	}
575
 
576
	/**
577
	*	 Lock a row, will escalate and lock the table if row locking not supported
578
	*	will normally free the lock at the end of the transaction
579
	*
580
	*  @param $table	name of table to lock
581
	*  @param $where	where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
582
	*/
583
	function RowLock($table,$where)
584
	{
585
		return false;
586
	}
587
 
588
	function CommitLock($table)
589
	{
590
		return $this->CommitTrans();
591
	}
592
 
593
	function RollbackLock($table)
594
	{
595
		return $this->RollbackTrans();
596
	}
597
 
598
	/**
599
	* PEAR DB Compat - do not use internally. 
600
	*
601
	* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
602
	* 	for easy porting :-)
603
	*
604
	* @param mode	The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
605
	* @returns		The previous fetch mode
606
	*/
607
	function SetFetchMode($mode)
608
	{	
609
		$old = $this->fetchMode;
610
		$this->fetchMode = $mode;
611
 
612
		if ($old === false) {
613
		global $ADODB_FETCH_MODE;
614
			return $ADODB_FETCH_MODE;
615
		}
616
		return $old;
617
	}
618
 
619
 
620
	/**
621
	* PEAR DB Compat - do not use internally. 
622
	*/
623
	function &Query($sql, $inputarr=false)
624
	{
625
		$rs = &$this->Execute($sql, $inputarr);
626
		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
627
		return $rs;
628
	}
629
 
630
 
631
	/**
632
	* PEAR DB Compat - do not use internally
633
	*/
634
	function &LimitQuery($sql, $offset, $count, $params=false)
635
	{
636
		$rs = &$this->SelectLimit($sql, $count, $offset, $params); 
637
		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
638
		return $rs;
639
	}
640
 
641
 
642
	/**
643
	* PEAR DB Compat - do not use internally
644
	*/
645
	function Disconnect()
646
	{
647
		return $this->Close();
648
	}
649
 
650
	/*
651
		 Returns placeholder for parameter, eg.
652
		 $DB->Param('a')
653
 
654
		 will return ':a' for Oracle, and '?' for most other databases...
655
 
656
		 For databases that require positioned params, eg $1, $2, $3 for postgresql,
657
		 	pass in Param(false) before setting the first parameter.
658
	*/
659
	function Param($name,$type='C')
660
	{
661
		return '?';
662
	}
663
 
664
	/*
665
		InParameter and OutParameter are self-documenting versions of Parameter().
666
	*/
667
	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
668
	{
669
		return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
670
	}
671
 
672
	/*
673
	*/
674
	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
675
	{
676
		return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
677
 
678
	}
679
 
680
	/* 
681
	Usage in oracle
682
		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
683
		$db->Parameter($stmt,$id,'myid');
684
		$db->Parameter($stmt,$group,'group',64);
685
		$db->Execute();
686
 
687
		@param $stmt Statement returned by Prepare() or PrepareSP().
688
		@param $var PHP variable to bind to
689
		@param $name Name of stored procedure variable name to bind to.
690
		@param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
691
		@param [$maxLen] Holds an maximum length of the variable.
692
		@param [$type] The data type of $var. Legal values depend on driver.
693
 
694
	*/
695
	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
696
	{
697
		return false;
698
	}
699
 
700
	/**
701
		Improved method of initiating a transaction. Used together with CompleteTrans().
702
		Advantages include:
703
 
704
		a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
705
		   Only the outermost block is treated as a transaction.<br>
706
		b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
707
		c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
708
		   are disabled, making it backward compatible.
709
	*/
710
	function StartTrans($errfn = 'ADODB_TransMonitor')
711
	{
712
		if ($this->transOff > 0) {
713
			$this->transOff += 1;
714
			return;
715
		}
716
 
717
		$this->_oldRaiseFn = $this->raiseErrorFn;
718
		$this->raiseErrorFn = $errfn;
719
		$this->_transOK = true;
720
 
721
		if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
722
		$this->BeginTrans();
723
		$this->transOff = 1;
724
	}
725
 
726
 
727
	/**
728
		Used together with StartTrans() to end a transaction. Monitors connection
729
		for sql errors, and will commit or rollback as appropriate.
730
 
731
		@autoComplete if true, monitor sql errors and commit and rollback as appropriate, 
732
		and if set to false force rollback even if no SQL error detected.
733
		@returns true on commit, false on rollback.
734
	*/
735
	function CompleteTrans($autoComplete = true)
736
	{
737
		if ($this->transOff > 1) {
738
			$this->transOff -= 1;
739
			return true;
740
		}
741
		$this->raiseErrorFn = $this->_oldRaiseFn;
742
 
743
		$this->transOff = 0;
744
		if ($this->_transOK && $autoComplete) {
745
			if (!$this->CommitTrans()) {
746
				$this->_transOK = false;
747
				if ($this->debug) ADOConnection::outp("Smart Commit failed");
748
			} else
749
				if ($this->debug) ADOConnection::outp("Smart Commit occurred");
750
		} else {
751
			$this->_transOK = false;
752
			$this->RollbackTrans();
753
			if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
754
		}
755
 
756
		return $this->_transOK;
757
	}
758
 
759
	/*
760
		At the end of a StartTrans/CompleteTrans block, perform a rollback.
761
	*/
762
	function FailTrans()
763
	{
764
		if ($this->debug) 
765
			if ($this->transOff == 0) {
766
				ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
767
			} else {
768
				ADOConnection::outp("FailTrans was called");
769
				adodb_backtrace();
770
			}
771
		$this->_transOK = false;
772
	}
773
 
774
	/**
775
		Check if transaction has failed, only for Smart Transactions.
776
	*/
777
	function HasFailedTrans()
778
	{
779
		if ($this->transOff > 0) return $this->_transOK == false;
780
		return false;
781
	}
782
 
783
	/**
784
	 * Execute SQL 
785
	 *
786
	 * @param sql		SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
787
	 * @param [inputarr]	holds the input data to bind to. Null elements will be set to null.
788
	 * @return 		RecordSet or false
789
	 */
790
	function &Execute($sql,$inputarr=false) 
791
	{
792
		if ($this->fnExecute) {
793
			$fn = $this->fnExecute;
794
			$ret =& $fn($this,$sql,$inputarr);
795
			if (isset($ret)) return $ret;
796
		}
797
		if ($inputarr) {
798
			if (!is_array($inputarr)) $inputarr = array($inputarr);
799
 
800
			$element0 = reset($inputarr);
801
			# is_object check because oci8 descriptors can be passed in
802
			$array_2d = is_array($element0) && !is_object(reset($element0));
803
			//remove extra memory copy of input -mikefedyk
804
			unset($element0);
805
 
806
			if (!is_array($sql) && !$this->_bindInputArray) {
807
				$sqlarr = explode('?',$sql);
808
 
809
				if (!$array_2d) $inputarr = array($inputarr);
810
				foreach($inputarr as $arr) {
811
					$sql = ''; $i = 0;
812
					//Use each() instead of foreach to reduce memory usage -mikefedyk
813
					while(list(, $v) = each($arr)) {
814
						$sql .= $sqlarr[$i];
815
						// from Ron Baldwin <ron.baldwin#sourceprose.com>
816
						// Only quote string types	
817
						$typ = gettype($v);
818
						if ($typ == 'string')
819
							//New memory copy of input created here -mikefedyk
820
							$sql .= $this->qstr($v);
821
						else if ($typ == 'double')
822
							$sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
823
						else if ($typ == 'boolean')
824
							$sql .= $v ? $this->true : $this->false;
825
						else if ($v === null)
826
							$sql .= 'NULL';
827
						else
828
							$sql .= $v;
829
						$i += 1;
830
					}
831
					if (isset($sqlarr[$i])) {
832
						$sql .= $sqlarr[$i];
833
						if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql));
834
					} else if ($i != sizeof($sqlarr))	
835
						ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql));
836
 
837
					$ret =& $this->_Execute($sql);
838
					if (!$ret) return $ret;
839
				}	
840
			} else {
841
				if ($array_2d) {
842
					if (is_string($sql))
843
						$stmt = $this->Prepare($sql);
844
					else
845
						$stmt = $sql;
846
 
847
					foreach($inputarr as $arr) {
848
						$ret =& $this->_Execute($stmt,$arr);
849
						if (!$ret) return $ret;
850
					}
851
				} else {
852
					$ret =& $this->_Execute($sql,$inputarr);
853
				}
854
			}
855
		} else {
856
			$ret =& $this->_Execute($sql,false);
857
		}
858
 
859
		return $ret;
860
	}
861
 
862
 
863
	function &_Execute($sql,$inputarr=false)
864
	{
865
		if ($this->debug) {
866
			global $ADODB_INCLUDED_LIB;
867
			if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
868
			$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
869
		} else {
870
			$this->_queryID = @$this->_query($sql,$inputarr);
871
		}
872
 
873
		/************************
874
		// OK, query executed
875
		*************************/
876
 
877
		if ($this->_queryID === false) { // error handling if query fails
878
			if ($this->debug == 99) adodb_backtrace(true,5);	
879
			$fn = $this->raiseErrorFn;
880
			if ($fn) {
881
				$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
882
			} 
883
			$false = false;
884
			return $false;
885
		} 
886
 
887
		if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
888
			$rs =& new ADORecordSet_empty();
889
			return $rs;
890
		}
891
 
892
		// return real recordset from select statement
893
		$rsclass = $this->rsPrefix.$this->databaseType;
894
		$rs = new $rsclass($this->_queryID,$this->fetchMode);
895
		$rs->connection = &$this; // Pablo suggestion
896
		$rs->Init();
897
		if (is_array($sql)) $rs->sql = $sql[0];
898
		else $rs->sql = $sql;
899
		if ($rs->_numOfRows <= 0) {
900
		global $ADODB_COUNTRECS;
901
			if ($ADODB_COUNTRECS) {
902
				if (!$rs->EOF) { 
903
					$rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
904
					$rs->_queryID = $this->_queryID;
905
				} else
906
					$rs->_numOfRows = 0;
907
			}
908
		}
909
		return $rs;
910
	}
911
 
912
	function CreateSequence($seqname='adodbseq',$startID=1)
913
	{
914
		if (empty($this->_genSeqSQL)) return false;
915
		return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
916
	}
917
 
918
	function DropSequence($seqname='adodbseq')
919
	{
920
		if (empty($this->_dropSeqSQL)) return false;
921
		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
922
	}
923
 
924
	/**
925
	 * Generates a sequence id and stores it in $this->genID;
926
	 * GenID is only available if $this->hasGenID = true;
927
	 *
928
	 * @param seqname		name of sequence to use
929
	 * @param startID		if sequence does not exist, start at this ID
930
	 * @return		0 if not supported, otherwise a sequence id
931
	 */
932
	function GenID($seqname='adodbseq',$startID=1)
933
	{
934
		if (!$this->hasGenID) {
935
			return 0; // formerly returns false pre 1.60
936
		}
937
 
938
		$getnext = sprintf($this->_genIDSQL,$seqname);
939
 
940
		$holdtransOK = $this->_transOK;
941
 
942
		$save_handler = $this->raiseErrorFn;
943
		$this->raiseErrorFn = '';
944
		@($rs = $this->Execute($getnext));
945
		$this->raiseErrorFn = $save_handler;
946
 
947
		if (!$rs) {
948
			$this->_transOK = $holdtransOK; //if the status was ok before reset
949
			$createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
950
			$rs = $this->Execute($getnext);
951
		}
952
		if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
953
		else $this->genID = 0; // false
954
 
955
		if ($rs) $rs->Close();
956
 
957
		return $this->genID;
958
	}	
959
 
960
	/**
961
	 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
962
	 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
963
	 * @return  the last inserted ID. Not all databases support this.
964
	 */ 
965
	function Insert_ID($table='',$column='')
966
	{
967
		if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
968
		if ($this->hasInsertID) return $this->_insertid($table,$column);
969
		if ($this->debug) {
970
			ADOConnection::outp( '<p>Insert_ID error</p>');
971
			adodb_backtrace();
972
		}
973
		return false;
974
	}
975
 
976
 
977
	/**
978
	 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
979
	 *
980
	 * @return  the last inserted ID. All databases support this. But aware possible
981
	 * problems in multiuser environments. Heavy test this before deploying.
982
	 */ 
983
	function PO_Insert_ID($table="", $id="") 
984
	{
985
	   if ($this->hasInsertID){
986
		   return $this->Insert_ID($table,$id);
987
	   } else {
988
		   return $this->GetOne("SELECT MAX($id) FROM $table");
989
	   }
990
	}
991
 
992
	/**
993
	* @return # rows affected by UPDATE/DELETE
994
	*/ 
995
	function Affected_Rows()
996
	{
997
		if ($this->hasAffectedRows) {
998
			if ($this->fnExecute === 'adodb_log_sql') {
999
				if ($this->_logsql && $this->_affected !== false) return $this->_affected;
1000
			}
1001
			$val = $this->_affectedrows();
1002
			return ($val < 0) ? false : $val;
1003
		}
1004
 
1005
		if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1006
		return false;
1007
	}
1008
 
1009
 
1010
	/**
1011
	 * @return  the last error message
1012
	 */
1013
	function ErrorMsg()
1014
	{
1015
		if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1016
		else return '';
1017
	}
1018
 
1019
 
1020
	/**
1021
	 * @return the last error number. Normally 0 means no error.
1022
	 */
1023
	function ErrorNo() 
1024
	{
1025
		return ($this->_errorMsg) ? -1 : 0;
1026
	}
1027
 
1028
	function MetaError($err=false)
1029
	{
1030
		include_once(ADODB_DIR."/adodb-error.inc.php");
1031
		if ($err === false) $err = $this->ErrorNo();
1032
		return adodb_error($this->dataProvider,$this->databaseType,$err);
1033
	}
1034
 
1035
	function MetaErrorMsg($errno)
1036
	{
1037
		include_once(ADODB_DIR."/adodb-error.inc.php");
1038
		return adodb_errormsg($errno);
1039
	}
1040
 
1041
	/**
1042
	 * @returns an array with the primary key columns in it.
1043
	 */
1044
	function MetaPrimaryKeys($table, $owner=false)
1045
	{
1046
	// owner not used in base class - see oci8
1047
		$p = array();
1048
		$objs =& $this->MetaColumns($table);
1049
		if ($objs) {
1050
			foreach($objs as $v) {
1051
				if (!empty($v->primary_key))
1052
					$p[] = $v->name;
1053
			}
1054
		}
1055
		if (sizeof($p)) return $p;
1056
		if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
1057
			return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1058
		return false;
1059
	}
1060
 
1061
	/**
1062
	 * @returns assoc array where keys are tables, and values are foreign keys
1063
	 */
1064
	function MetaForeignKeys($table, $owner=false, $upper=false)
1065
	{
1066
		return false;
1067
	}
1068
	/**
1069
	 * Choose a database to connect to. Many databases do not support this.
1070
	 *
1071
	 * @param dbName 	is the name of the database to select
1072
	 * @return 		true or false
1073
	 */
1074
	function SelectDB($dbName) 
1075
	{return false;}
1076
 
1077
 
1078
	/**
1079
	* Will select, getting rows from $offset (1-based), for $nrows. 
1080
	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
1081
	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1082
	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
1083
	* eg. 
1084
	*  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1085
	*  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1086
	*
1087
	* Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1088
	* BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1089
	*
1090
	* @param sql
1091
	* @param [offset]	is the row to start calculations from (1-based)
1092
	* @param [nrows]		is the number of rows to get
1093
	* @param [inputarr]	array of bind variables
1094
	* @param [secs2cache]		is a private parameter only used by jlim
1095
	* @return		the recordset ($rs->databaseType == 'array')
1096
 	*/
1097
	function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
1098
	{
1099
		if ($this->hasTop && $nrows > 0) {
1100
		// suggested by Reinhard Balling. Access requires top after distinct 
1101
		 // Informix requires first before distinct - F Riosa
1102
			$ismssql = (strpos($this->databaseType,'mssql') !== false);
1103
			if ($ismssql) $isaccess = false;
1104
			else $isaccess = (strpos($this->databaseType,'access') !== false);
1105
 
1106
			if ($offset <= 0) {
1107
 
1108
					// access includes ties in result
1109
					if ($isaccess) {
1110
						$sql = preg_replace(
1111
						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1112
 
1113
						if ($secs2cache>0) {
1114
							$ret =& $this->CacheExecute($secs2cache, $sql,$inputarr);
1115
						} else {
1116
							$ret =& $this->Execute($sql,$inputarr);
1117
						}
1118
						return $ret; // PHP5 fix
1119
					} else if ($ismssql){
1120
						$sql = preg_replace(
1121
						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1122
					} else {
1123
						$sql = preg_replace(
1124
						'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1125
					}
1126
			} else {
1127
				$nn = $nrows + $offset;
1128
				if ($isaccess || $ismssql) {
1129
					$sql = preg_replace(
1130
					'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1131
				} else {
1132
					$sql = preg_replace(
1133
					'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1134
				}
1135
			}
1136
		}
1137
 
1138
		// if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
1139
		// 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1140
		global $ADODB_COUNTRECS;
1141
 
1142
		$savec = $ADODB_COUNTRECS;
1143
		$ADODB_COUNTRECS = false;
1144
 
1145
		if ($offset>0){
1146
			if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1147
			else $rs = &$this->Execute($sql,$inputarr);
1148
		} else {
1149
			if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1150
			else $rs = &$this->Execute($sql,$inputarr);
1151
		}
1152
		$ADODB_COUNTRECS = $savec;
1153
		if ($rs && !$rs->EOF) {
1154
			$rs =& $this->_rs2rs($rs,$nrows,$offset);
1155
		}
1156
		//print_r($rs);
1157
		return $rs;
1158
	}
1159
 
1160
	/**
1161
	* Create serializable recordset. Breaks rs link to connection.
1162
	*
1163
	* @param rs			the recordset to serialize
1164
	*/
1165
	function &SerializableRS(&$rs)
1166
	{
1167
		$rs2 =& $this->_rs2rs($rs);
1168
		$ignore = false;
1169
		$rs2->connection =& $ignore;
1170
 
1171
		return $rs2;
1172
	}
1173
 
1174
	/**
1175
	* Convert database recordset to an array recordset
1176
	* input recordset's cursor should be at beginning, and
1177
	* old $rs will be closed.
1178
	*
1179
	* @param rs			the recordset to copy
1180
	* @param [nrows]  	number of rows to retrieve (optional)
1181
	* @param [offset] 	offset by number of rows (optional)
1182
	* @return 			the new recordset
1183
	*/
1184
	function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
1185
	{
1186
		if (! $rs) {
1187
			$false = false;
1188
			return $false;
1189
		}
1190
		$dbtype = $rs->databaseType;
1191
		if (!$dbtype) {
1192
			$rs = &$rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1193
			return $rs;
1194
		}
1195
		if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1196
			$rs->MoveFirst();
1197
			$rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1198
			return $rs;
1199
		}
1200
		$flds = array();
1201
		for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1202
			$flds[] = $rs->FetchField($i);
1203
		}
1204
 
1205
		$arr =& $rs->GetArrayLimit($nrows,$offset);
1206
		//print_r($arr);
1207
		if ($close) $rs->Close();
1208
 
1209
		$arrayClass = $this->arrayClass;
1210
 
1211
		$rs2 = new $arrayClass();
1212
		$rs2->connection = &$this;
1213
		$rs2->sql = $rs->sql;
1214
		$rs2->dataProvider = $this->dataProvider;
1215
		$rs2->InitArrayFields($arr,$flds);
1216
		$rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1217
		return $rs2;
1218
	}
1219
 
1220
	/*
1221
	* Return all rows. Compat with PEAR DB
1222
	*/
1223
	function &GetAll($sql, $inputarr=false)
1224
	{
1225
		$arr =& $this->GetArray($sql,$inputarr);
1226
		return $arr;
1227
	}
1228
 
1229
	function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
1230
	{
1231
		$rs =& $this->Execute($sql, $inputarr);
1232
		if (!$rs) {
1233
			$false = false;
1234
			return $false;
1235
		}
1236
		$arr =& $rs->GetAssoc($force_array,$first2cols);
1237
		return $arr;
1238
	}
1239
 
1240
	function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
1241
	{
1242
		if (!is_numeric($secs2cache)) {
1243
			$first2cols = $force_array;
1244
			$force_array = $inputarr;
1245
		}
1246
		$rs =& $this->CacheExecute($secs2cache, $sql, $inputarr);
1247
		if (!$rs) {
1248
			$false = false;
1249
			return $false;
1250
		}
1251
		$arr =& $rs->GetAssoc($force_array,$first2cols);
1252
		return $arr;
1253
	}
1254
 
1255
	/**
1256
	* Return first element of first row of sql statement. Recordset is disposed
1257
	* for you.
1258
	*
1259
	* @param sql			SQL statement
1260
	* @param [inputarr]		input bind array
1261
	*/
1262
	function GetOne($sql,$inputarr=false)
1263
	{
1264
	global $ADODB_COUNTRECS;
1265
		$crecs = $ADODB_COUNTRECS;
1266
		$ADODB_COUNTRECS = false;
1267
 
1268
		$ret = false;
1269
		$rs = &$this->Execute($sql,$inputarr);
1270
		if ($rs) {	
1271
			if (!$rs->EOF) $ret = reset($rs->fields);
1272
			$rs->Close();
1273
		}
1274
		$ADODB_COUNTRECS = $crecs;
1275
		return $ret;
1276
	}
1277
 
1278
	function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
1279
	{
1280
		$ret = false;
1281
		$rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1282
		if ($rs) {		
1283
			if (!$rs->EOF) $ret = reset($rs->fields);
1284
			$rs->Close();
1285
		} 
1286
 
1287
		return $ret;
1288
	}
1289
 
1290
	function GetCol($sql, $inputarr = false, $trim = false)
1291
	{
1292
	  	$rv = false;
1293
	  	$rs = &$this->Execute($sql, $inputarr);
1294
	  	if ($rs) {
1295
			$rv = array();
1296
	   		if ($trim) {
1297
				while (!$rs->EOF) {
1298
					$rv[] = trim(reset($rs->fields));
1299
					$rs->MoveNext();
1300
		   		}
1301
			} else {
1302
				while (!$rs->EOF) {
1303
					$rv[] = reset($rs->fields);
1304
					$rs->MoveNext();
1305
		   		}
1306
			}
1307
	   		$rs->Close();
1308
	  	}
1309
	  	return $rv;
1310
	}
1311
 
1312
	function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
1313
	{
1314
	  	$rv = false;
1315
	  	$rs = &$this->CacheExecute($secs, $sql, $inputarr);
1316
	  	if ($rs) {
1317
			if ($trim) {
1318
				while (!$rs->EOF) {
1319
					$rv[] = trim(reset($rs->fields));
1320
					$rs->MoveNext();
1321
		   		}
1322
			} else {
1323
				while (!$rs->EOF) {
1324
					$rv[] = reset($rs->fields);
1325
					$rs->MoveNext();
1326
		   		}
1327
			}
1328
	   		$rs->Close();
1329
	  	}
1330
	  	return $rv;
1331
	}
1332
 
1333
	/*
1334
		Calculate the offset of a date for a particular database and generate
1335
			appropriate SQL. Useful for calculating future/past dates and storing
1336
			in a database.
1337
 
1338
		If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1339
	*/
1340
	function OffsetDate($dayFraction,$date=false)
1341
	{		
1342
		if (!$date) $date = $this->sysDate;
1343
		return  '('.$date.'+'.$dayFraction.')';
1344
	}
1345
 
1346
 
1347
	/**
1348
	*
1349
	* @param sql			SQL statement
1350
	* @param [inputarr]		input bind array
1351
	*/
1352
	function &GetArray($sql,$inputarr=false)
1353
	{
1354
	global $ADODB_COUNTRECS;
1355
 
1356
		$savec = $ADODB_COUNTRECS;
1357
		$ADODB_COUNTRECS = false;
1358
		$rs =& $this->Execute($sql,$inputarr);
1359
		$ADODB_COUNTRECS = $savec;
1360
		if (!$rs) 
1361
			if (defined('ADODB_PEAR')) {
1362
				$cls = ADODB_PEAR_Error();
1363
				return $cls;
1364
			} else {
1365
				$false = false;
1366
				return $false;
1367
			}
1368
		$arr =& $rs->GetArray();
1369
		$rs->Close();
1370
		return $arr;
1371
	}
1372
 
1373
	function &CacheGetAll($secs2cache,$sql=false,$inputarr=false)
1374
	{
1375
		return $this->CacheGetArray($secs2cache,$sql,$inputarr);
1376
	}
1377
 
1378
	function &CacheGetArray($secs2cache,$sql=false,$inputarr=false)
1379
	{
1380
	global $ADODB_COUNTRECS;
1381
 
1382
		$savec = $ADODB_COUNTRECS;
1383
		$ADODB_COUNTRECS = false;
1384
		$rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1385
		$ADODB_COUNTRECS = $savec;
1386
 
1387
		if (!$rs) 
1388
			if (defined('ADODB_PEAR')) {
1389
				$cls = ADODB_PEAR_Error();
1390
				return $cls;
1391
			} else {
1392
				$false = false;
1393
				return $false;
1394
			}
1395
		$arr =& $rs->GetArray();
1396
		$rs->Close();
1397
		return $arr;
1398
	}
1399
 
1400
 
1401
 
1402
	/**
1403
	* Return one row of sql statement. Recordset is disposed for you.
1404
	*
1405
	* @param sql			SQL statement
1406
	* @param [inputarr]		input bind array
1407
	*/
1408
	function &GetRow($sql,$inputarr=false)
1409
	{
1410
	global $ADODB_COUNTRECS;
1411
		$crecs = $ADODB_COUNTRECS;
1412
		$ADODB_COUNTRECS = false;
1413
 
1414
		$rs =& $this->Execute($sql,$inputarr);
1415
 
1416
		$ADODB_COUNTRECS = $crecs;
1417
		if ($rs) {
1418
			if (!$rs->EOF) $arr = $rs->fields;
1419
			else $arr = array();
1420
			$rs->Close();
1421
			return $arr;
1422
		}
1423
 
1424
		$false = false;
1425
		return $false;
1426
	}
1427
 
1428
	function &CacheGetRow($secs2cache,$sql=false,$inputarr=false)
1429
	{
1430
		$rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1431
		if ($rs) {
1432
			$arr = false;
1433
			if (!$rs->EOF) $arr = $rs->fields;
1434
			$rs->Close();
1435
			return $arr;
1436
		}
1437
		$false = false;
1438
		return $false;
1439
	}
1440
 
1441
	/**
1442
	* Insert or replace a single record. Note: this is not the same as MySQL's replace. 
1443
	* ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1444
	* Also note that no table locking is done currently, so it is possible that the
1445
	* record be inserted twice by two programs...
1446
	*
1447
	* $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1448
	*
1449
	* $table		table name
1450
	* $fieldArray	associative array of data (you must quote strings yourself).
1451
	* $keyCol		the primary key field name or if compound key, array of field names
1452
	* autoQuote		set to true to use a hueristic to quote strings. Works with nulls and numbers
1453
	*					but does not work with dates nor SQL functions.
1454
	* has_autoinc	the primary key is an auto-inc field, so skip in insert.
1455
	*
1456
	* Currently blob replace not supported
1457
	*
1458
	* returns 0 = fail, 1 = update, 2 = insert 
1459
	*/
1460
 
1461
	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
1462
	{
1463
		global $ADODB_INCLUDED_LIB;
1464
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1465
 
1466
		return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1467
	}
1468
 
1469
 
1470
	/**
1471
	* Will select, getting rows from $offset (1-based), for $nrows. 
1472
	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
1473
	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1474
	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
1475
	* eg. 
1476
	*  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1477
	*  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1478
	*
1479
	* BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1480
	*
1481
	* @param [secs2cache]	seconds to cache data, set to 0 to force query. This is optional
1482
	* @param sql
1483
	* @param [offset]	is the row to start calculations from (1-based)
1484
	* @param [nrows]	is the number of rows to get
1485
	* @param [inputarr]	array of bind variables
1486
	* @return		the recordset ($rs->databaseType == 'array')
1487
 	*/
1488
	function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
1489
	{	
1490
		if (!is_numeric($secs2cache)) {
1491
			if ($sql === false) $sql = -1;
1492
			if ($offset == -1) $offset = false;
1493
									  // sql,	nrows, offset,inputarr
1494
			$rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1495
		} else {
1496
			if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
1497
			$rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1498
		}
1499
		return $rs;
1500
	}
1501
 
1502
 
1503
	/**
1504
	* Flush cached recordsets that match a particular $sql statement. 
1505
	* If $sql == false, then we purge all files in the cache.
1506
 	*/
1507
 
1508
	/**
1509
   * Flush cached recordsets that match a particular $sql statement. 
1510
   * If $sql == false, then we purge all files in the cache.
1511
    */
1512
	function CacheFlush($sql=false,$inputarr=false)
1513
	{
1514
	global $ADODB_CACHE_DIR;
1515
 
1516
		if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1517
         /*if (strncmp(PHP_OS,'WIN',3) === 0)
1518
            $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
1519
         else */
1520
            $dir = $ADODB_CACHE_DIR;
1521
 
1522
         if ($this->debug) {
1523
            ADOConnection::outp( "CacheFlush: $dir<br><pre>\n", $this->_dirFlush($dir),"</pre>");
1524
         } else {
1525
            $this->_dirFlush($dir);
1526
         }
1527
         return;
1528
      } 
1529
 
1530
      global $ADODB_INCLUDED_CSV;
1531
      if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1532
 
1533
      $f = $this->_gencachename($sql.serialize($inputarr),false);
1534
      adodb_write_file($f,''); // is adodb_write_file needed?
1535
      if (!@unlink($f)) {
1536
         if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1537
      }
1538
   }
1539
 
1540
   /**
1541
   * Private function to erase all of the files and subdirectories in a directory.
1542
   *
1543
   * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
1544
   * Note: $kill_top_level is used internally in the function to flush subdirectories.
1545
   */
1546
   function _dirFlush($dir, $kill_top_level = false) {
1547
      if(!$dh = @opendir($dir)) return;
1548
 
1549
      while (($obj = readdir($dh))) {
1550
         if($obj=='.' || $obj=='..')
1551
            continue;
1552
 
1553
         if (!@unlink($dir.'/'.$obj))
1554
			  $this->_dirFlush($dir.'/'.$obj, true);
1555
      }
1556
      if ($kill_top_level === true)
1557
         @rmdir($dir);
1558
      return true;
1559
   }
1560
 
1561
 
1562
	function xCacheFlush($sql=false,$inputarr=false)
1563
	{
1564
	global $ADODB_CACHE_DIR;
1565
 
1566
		if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1567
			if (strncmp(PHP_OS,'WIN',3) === 0) {
1568
				$cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
1569
			} else {
1570
				//$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
1571
				$cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/'; 
1572
				// old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
1573
			}
1574
			if ($this->debug) {
1575
				ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
1576
			} else {
1577
				exec($cmd);
1578
			}
1579
			return;
1580
		} 
1581
 
1582
		global $ADODB_INCLUDED_CSV;
1583
		if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1584
 
1585
		$f = $this->_gencachename($sql.serialize($inputarr),false);
1586
		adodb_write_file($f,''); // is adodb_write_file needed?
1587
		if (!@unlink($f)) {
1588
			if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1589
		}
1590
	}
1591
 
1592
	/**
1593
	* Private function to generate filename for caching.
1594
	* Filename is generated based on:
1595
	*
1596
	*  - sql statement
1597
	*  - database type (oci8, ibase, ifx, etc)
1598
	*  - database name
1599
	*  - userid
1600
	*  - setFetchMode (adodb 4.23)
1601
	*
1602
	* When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). 
1603
	* Assuming that we can have 50,000 files per directory with good performance, 
1604
	* then we can scale to 12.8 million unique cached recordsets. Wow!
1605
 	*/
1606
	function _gencachename($sql,$createdir)
1607
	{
1608
	global $ADODB_CACHE_DIR;
1609
	static $notSafeMode;
1610
 
1611
		if ($this->fetchMode === false) { 
1612
		global $ADODB_FETCH_MODE;
1613
			$mode = $ADODB_FETCH_MODE;
1614
		} else {
1615
			$mode = $this->fetchMode;
1616
		}
1617
		$m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1618
 
1619
		if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
1620
		$dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
1621
 
1622
		if ($createdir && $notSafeMode && !file_exists($dir)) {
1623
			$oldu = umask(0);
1624
			if (!mkdir($dir,0771)) 
1625
				if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
1626
			umask($oldu);
1627
		}
1628
		return $dir.'/adodb_'.$m.'.cache';
1629
	}
1630
 
1631
 
1632
	/**
1633
	 * Execute SQL, caching recordsets.
1634
	 *
1635
	 * @param [secs2cache]	seconds to cache data, set to 0 to force query. 
1636
	 *					  This is an optional parameter.
1637
	 * @param sql		SQL statement to execute
1638
	 * @param [inputarr]	holds the input data  to bind to
1639
	 * @return 		RecordSet or false
1640
	 */
1641
	function &CacheExecute($secs2cache,$sql=false,$inputarr=false)
1642
	{
1643
 
1644
 
1645
		if (!is_numeric($secs2cache)) {
1646
			$inputarr = $sql;
1647
			$sql = $secs2cache;
1648
			$secs2cache = $this->cacheSecs;
1649
		}
1650
 
1651
		if (is_array($sql)) {
1652
			$sqlparam = $sql;
1653
			$sql = $sql[0];
1654
		} else
1655
			$sqlparam = $sql;
1656
 
1657
		global $ADODB_INCLUDED_CSV;
1658
		if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
1659
 
1660
		$md5file = $this->_gencachename($sql.serialize($inputarr),true);
1661
		$err = '';
1662
 
1663
		if ($secs2cache > 0){
1664
			$rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
1665
			$this->numCacheHits += 1;
1666
		} else {
1667
			$err='Timeout 1';
1668
			$rs = false;
1669
			$this->numCacheMisses += 1;
1670
		}
1671
		if (!$rs) {
1672
		// no cached rs found
1673
			if ($this->debug) {
1674
				if (get_magic_quotes_runtime()) {
1675
					ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
1676
				}
1677
				if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
1678
			}
1679
 
1680
			$rs = &$this->Execute($sqlparam,$inputarr);
1681
 
1682
			if ($rs) {
1683
				$eof = $rs->EOF;
1684
				$rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
1685
				$txt = _rs2serialize($rs,false,$sql); // serialize
1686
 
1687
				if (!adodb_write_file($md5file,$txt,$this->debug)) {
1688
					if ($fn = $this->raiseErrorFn) {
1689
						$fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
1690
					}
1691
					if ($this->debug) ADOConnection::outp( " Cache write error");
1692
				}
1693
				if ($rs->EOF && !$eof) {
1694
					$rs->MoveFirst();
1695
					//$rs = &csv2rs($md5file,$err);		
1696
					$rs->connection = &$this; // Pablo suggestion
1697
				}  
1698
 
1699
			} else
1700
				@unlink($md5file);
1701
		} else {
1702
			$this->_errorMsg = '';
1703
			$this->_errorCode = 0;
1704
 
1705
			if ($this->fnCacheExecute) {
1706
				$fn = $this->fnCacheExecute;
1707
				$fn($this, $secs2cache, $sql, $inputarr);
1708
			}
1709
		// ok, set cached object found
1710
			$rs->connection = &$this; // Pablo suggestion
1711
			if ($this->debug){ 
1712
 
1713
				$inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1714
				$ttl = $rs->timeCreated + $secs2cache - time();
1715
				$s = is_array($sql) ? $sql[0] : $sql;
1716
				if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
1717
 
1718
				ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
1719
			}
1720
		}
1721
		return $rs;
1722
	}
1723
 
1724
 
1725
	/* 
1726
		Similar to PEAR DB's autoExecute(), except that 
1727
		$mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
1728
		If $mode == 'UPDATE', then $where is compulsory as a safety measure.
1729
 
1730
		$forceUpdate means that even if the data has not changed, perform update.
1731
	 */
1732
	function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false) 
1733
	{
1734
		$sql = 'SELECT * FROM '.$table;  
1735
		if ($where!==FALSE) $sql .= ' WHERE '.$where;
1736
		else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
1737
			ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause');
1738
			return false;
1739
		}
1740
 
1741
		$rs =& $this->SelectLimit($sql,1);
1742
		if (!$rs) return false; // table does not exist
1743
		$rs->tableName = $table;
1744
 
1745
		switch((string) $mode) {
1746
		case 'UPDATE':
1747
		case '2':
1748
			$sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
1749
			break;
1750
		case 'INSERT':
1751
		case '1':
1752
			$sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
1753
			break;
1754
		default:
1755
			ADOConnection::outp("AutoExecute: Unknown mode=$mode");
1756
			return false;
1757
		}
1758
		$ret = false;
1759
		if ($sql) $ret = $this->Execute($sql);
1760
		if ($ret) $ret = true;
1761
		return $ret;
1762
	}
1763
 
1764
 
1765
	/**
1766
	 * Generates an Update Query based on an existing recordset.
1767
	 * $arrFields is an associative array of fields with the value
1768
	 * that should be assigned.
1769
	 *
1770
	 * Note: This function should only be used on a recordset
1771
	 *	   that is run against a single table and sql should only 
1772
	 *		 be a simple select stmt with no groupby/orderby/limit
1773
	 *
1774
	 * "Jonathan Younger" <jyounger@unilab.com>
1775
  	 */
1776
	function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
1777
	{
1778
		global $ADODB_INCLUDED_LIB;
1779
 
1780
        //********************************************************//
1781
        //This is here to maintain compatibility
1782
        //with older adodb versions. Sets force type to force nulls if $forcenulls is set.
1783
		if (!isset($force)) {
1784
				global $ADODB_FORCE_TYPE;
1785
			    $force = $ADODB_FORCE_TYPE;
1786
		}
1787
		//********************************************************//
1788
 
1789
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1790
		return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
1791
	}
1792
 
1793
	/**
1794
	 * Generates an Insert Query based on an existing recordset.
1795
	 * $arrFields is an associative array of fields with the value
1796
	 * that should be assigned.
1797
	 *
1798
	 * Note: This function should only be used on a recordset
1799
	 *	   that is run against a single table.
1800
  	 */
1801
	function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
1802
	{	
1803
		global $ADODB_INCLUDED_LIB;
1804
		if (!isset($force)) {
1805
			global $ADODB_FORCE_TYPE;
1806
			$force = $ADODB_FORCE_TYPE;
1807
 
1808
		}
1809
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
1810
		return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
1811
	}
1812
 
1813
 
1814
	/**
1815
	* Update a blob column, given a where clause. There are more sophisticated
1816
	* blob handling functions that we could have implemented, but all require
1817
	* a very complex API. Instead we have chosen something that is extremely
1818
	* simple to understand and use. 
1819
	*
1820
	* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
1821
	*
1822
	* Usage to update a $blobvalue which has a primary key blob_id=1 into a 
1823
	* field blobtable.blobcolumn:
1824
	*
1825
	*	UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
1826
	*
1827
	* Insert example:
1828
	*
1829
	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1830
	*	$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1831
	*/
1832
 
1833
	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
1834
	{
1835
		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1836
	}
1837
 
1838
	/**
1839
	* Usage:
1840
	*	UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
1841
	*	
1842
	*	$blobtype supports 'BLOB' and 'CLOB'
1843
	*
1844
	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1845
	*	$conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
1846
	*/
1847
	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
1848
	{
1849
		$fd = fopen($path,'rb');
1850
		if ($fd === false) return false;
1851
		$val = fread($fd,filesize($path));
1852
		fclose($fd);
1853
		return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
1854
	}
1855
 
1856
	function BlobDecode($blob)
1857
	{
1858
		return $blob;
1859
	}
1860
 
1861
	function BlobEncode($blob)
1862
	{
1863
		return $blob;
1864
	}
1865
 
1866
	function SetCharSet($charset)
1867
	{
1868
		return false;
1869
	}
1870
 
1871
	function IfNull( $field, $ifNull ) 
1872
	{
1873
		return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
1874
	}
1875
 
1876
	function LogSQL($enable=true)
1877
	{
1878
		include_once(ADODB_DIR.'/adodb-perf.inc.php');
1879
 
1880
		if ($enable) $this->fnExecute = 'adodb_log_sql';
1881
		else $this->fnExecute = false;
1882
 
1883
		$old = $this->_logsql;	
1884
		$this->_logsql = $enable;
1885
		if ($enable && !$old) $this->_affected = false;
1886
		return $old;
1887
	}
1888
 
1889
	function GetCharSet()
1890
	{
1891
		return false;
1892
	}
1893
 
1894
	/**
1895
	* Usage:
1896
	*	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
1897
	*
1898
	*	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
1899
	*	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
1900
	*/
1901
	function UpdateClob($table,$column,$val,$where)
1902
	{
1903
		return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
1904
	}
1905
 
1906
	// not the fastest implementation - quick and dirty - jlim
1907
	// for best performance, use the actual $rs->MetaType().
1908
	function MetaType($t,$len=-1,$fieldobj=false)
1909
	{
1910
 
1911
		if (empty($this->_metars)) {
1912
			$rsclass = $this->rsPrefix.$this->databaseType;
1913
			$this->_metars =& new $rsclass(false,$this->fetchMode); 
1914
			$this->_metars->connection =& $this;
1915
		}
1916
		return $this->_metars->MetaType($t,$len,$fieldobj);
1917
	}
1918
 
1919
 
1920
	/**
1921
	*  Change the SQL connection locale to a specified locale.
1922
	*  This is used to get the date formats written depending on the client locale.
1923
	*/
1924
	function SetDateLocale($locale = 'En')
1925
	{
1926
		$this->locale = $locale;
1927
		switch (strtoupper($locale))
1928
		{
1929
			case 'EN':
1930
				$this->fmtDate="'Y-m-d'";
1931
				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
1932
				break;
1933
 
1934
			case 'US':
1935
				$this->fmtDate = "'m-d-Y'";
1936
				$this->fmtTimeStamp = "'m-d-Y H:i:s'";
1937
				break;
1938
 
1939
			case 'NL':
1940
			case 'FR':
1941
			case 'RO':
1942
			case 'IT':
1943
				$this->fmtDate="'d-m-Y'";
1944
				$this->fmtTimeStamp = "'d-m-Y H:i:s'";
1945
				break;
1946
 
1947
			case 'GE':
1948
				$this->fmtDate="'d.m.Y'";
1949
				$this->fmtTimeStamp = "'d.m.Y H:i:s'";
1950
				break;
1951
 
1952
			default:
1953
				$this->fmtDate="'Y-m-d'";
1954
				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
1955
				break;
1956
		}
1957
	}
1958
 
1959
	function &GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
1960
	{
1961
	global $_ADODB_ACTIVE_DBS;
1962
 
1963
		$save = $this->SetFetchMode(ADODB_FETCH_NUM);
1964
		if (empty($whereOrderBy)) $whereOrderBy = '1=1';
1965
		$rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
1966
		$this->SetFetchMode($save);
1967
 
1968
		$false = false;
1969
 
1970
		if ($rows === false) {	
1971
			return $false;
1972
		}
1973
 
1974
 
1975
		if (!isset($_ADODB_ACTIVE_DBS)) {
1976
			include_once(ADODB_DIR.'/adodb-active-record.inc.php');
1977
		}	
1978
		if (!class_exists($class)) {
1979
			ADOConnection::outp("Unknown class $class in GetActiveRcordsClass()");
1980
			return $false;
1981
		}
1982
		$arr = array();
1983
		foreach($rows as $row) {
1984
 
1985
			$obj =& new $class($table,$primkeyArr,$this);
1986
			if ($obj->ErrorMsg()){
1987
				$this->_errorMsg = $obj->ErrorMsg();
1988
				return $false;
1989
			}
1990
			$obj->Set($row);
1991
			$arr[] =& $obj;
1992
		}
1993
		return $arr;
1994
	}
1995
 
1996
	function &GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
1997
	{
1998
		$arr =& $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
1999
		return $arr;
2000
	}
2001
 
2002
	/**
2003
	 * Close Connection
2004
	 */
2005
	function Close()
2006
	{
2007
		$rez = $this->_close();
2008
		$this->_connectionID = false;
2009
		return $rez;
2010
	}
2011
 
2012
	/**
2013
	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2014
	 *
2015
	 * @return true if succeeded or false if database does not support transactions
2016
	 */
2017
	function BeginTrans() {return false;}
2018
 
2019
 
2020
	/**
2021
	 * If database does not support transactions, always return true as data always commited
2022
	 *
2023
	 * @param $ok  set to false to rollback transaction, true to commit
2024
	 *
2025
	 * @return true/false.
2026
	 */
2027
	function CommitTrans($ok=true) 
2028
	{ return true;}
2029
 
2030
 
2031
	/**
2032
	 * If database does not support transactions, rollbacks always fail, so return false
2033
	 *
2034
	 * @return true/false.
2035
	 */
2036
	function RollbackTrans() 
2037
	{ return false;}
2038
 
2039
 
2040
	/**
2041
	 * return the databases that the driver can connect to. 
2042
	 * Some databases will return an empty array.
2043
	 *
2044
	 * @return an array of database names.
2045
	 */
2046
		function MetaDatabases() 
2047
		{
2048
		global $ADODB_FETCH_MODE;
2049
 
2050
			if ($this->metaDatabasesSQL) {
2051
				$save = $ADODB_FETCH_MODE; 
2052
				$ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
2053
 
2054
				if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2055
 
2056
				$arr = $this->GetCol($this->metaDatabasesSQL);
2057
				if (isset($savem)) $this->SetFetchMode($savem);
2058
				$ADODB_FETCH_MODE = $save; 
2059
 
2060
				return $arr;
2061
			}
2062
 
2063
			return false;
2064
		}
2065
 
2066
 
2067
	/**
2068
	 * @param ttype can either be 'VIEW' or 'TABLE' or false. 
2069
	 * 		If false, both views and tables are returned.
2070
	 *		"VIEW" returns only views
2071
	 *		"TABLE" returns only tables
2072
	 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2073
	 * @param mask  is the input mask - only supported by oci8 and postgresql
2074
	 *
2075
	 * @return  array of tables for current database.
2076
	 */ 
2077
	function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
2078
	{
2079
	global $ADODB_FETCH_MODE;
2080
 
2081
 
2082
		$false = false;
2083
		if ($mask) {
2084
			return $false;
2085
		}
2086
		if ($this->metaTablesSQL) {
2087
			$save = $ADODB_FETCH_MODE; 
2088
			$ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
2089
 
2090
			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2091
 
2092
			$rs = $this->Execute($this->metaTablesSQL);
2093
			if (isset($savem)) $this->SetFetchMode($savem);
2094
			$ADODB_FETCH_MODE = $save; 
2095
 
2096
			if ($rs === false) return $false;
2097
			$arr =& $rs->GetArray();
2098
			$arr2 = array();
2099
 
2100
			if ($hast = ($ttype && isset($arr[0][1]))) { 
2101
				$showt = strncmp($ttype,'T',1);
2102
			}
2103
 
2104
			for ($i=0; $i < sizeof($arr); $i++) {
2105
				if ($hast) {
2106
					if ($showt == 0) {
2107
						if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
2108
					} else {
2109
						if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
2110
					}
2111
				} else
2112
					$arr2[] = trim($arr[$i][0]);
2113
			}
2114
			$rs->Close();
2115
			return $arr2;
2116
		}
2117
		return $false;
2118
	}
2119
 
2120
 
2121
	function _findschema(&$table,&$schema)
2122
	{
2123
		if (!$schema && ($at = strpos($table,'.')) !== false) {
2124
			$schema = substr($table,0,$at);
2125
			$table = substr($table,$at+1);
2126
		}
2127
	}
2128
 
2129
	/**
2130
	 * List columns in a database as an array of ADOFieldObjects. 
2131
	 * See top of file for definition of object.
2132
	 *
2133
	 * @param $table	table name to query
2134
	 * @param $normalize	makes table name case-insensitive (required by some databases)
2135
	 * @schema is optional database schema to use - not supported by all databases.
2136
	 *
2137
	 * @return  array of ADOFieldObjects for current table.
2138
	 */
2139
	function &MetaColumns($table,$normalize=true) 
2140
	{
2141
	global $ADODB_FETCH_MODE;
2142
 
2143
		$false = false;
2144
 
2145
		if (!empty($this->metaColumnsSQL)) {
2146
 
2147
			$schema = false;
2148
			$this->_findschema($table,$schema);
2149
 
2150
			$save = $ADODB_FETCH_MODE;
2151
			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2152
			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2153
			$rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2154
			if (isset($savem)) $this->SetFetchMode($savem);
2155
			$ADODB_FETCH_MODE = $save;
2156
			if ($rs === false || $rs->EOF) return $false;
2157
 
2158
			$retarr = array();
2159
			while (!$rs->EOF) { //print_r($rs->fields);
2160
				$fld = new ADOFieldObject();
2161
				$fld->name = $rs->fields[0];
2162
				$fld->type = $rs->fields[1];
2163
				if (isset($rs->fields[3]) && $rs->fields[3]) {
2164
					if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
2165
					$fld->scale = $rs->fields[4];
2166
					if ($fld->scale>0) $fld->max_length += 1;
2167
				} else
2168
					$fld->max_length = $rs->fields[2];
2169
 
2170
				if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;	
2171
				else $retarr[strtoupper($fld->name)] = $fld;
2172
				$rs->MoveNext();
2173
			}
2174
			$rs->Close();
2175
			return $retarr;	
2176
		}
2177
		return $false;
2178
	}
2179
 
2180
    /**
2181
      * List indexes on a table as an array.
2182
      * @param table  table name to query
2183
      * @param primary true to only show primary keys. Not actually used for most databases
2184
	  *
2185
      * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2186
 
2187
		 Array (
2188
		    [name_of_index] => Array
2189
		      (
2190
	          [unique] => true or false
2191
	          [columns] => Array
2192
	          (
2193
	          	[0] => firstname
2194
		      	[1] => lastname
2195
	          )
2196
		)		
2197
      */
2198
     function &MetaIndexes($table, $primary = false, $owner = false)
2199
     {
2200
	 		$false = false;
2201
            return $false;
2202
     }
2203
 
2204
	/**
2205
	 * List columns names in a table as an array. 
2206
	 * @param table	table name to query
2207
	 *
2208
	 * @return  array of column names for current table.
2209
	 */ 
2210
	function &MetaColumnNames($table, $numIndexes=false) 
2211
	{
2212
		$objarr =& $this->MetaColumns($table);
2213
		if (!is_array($objarr)) {
2214
			$false = false;
2215
			return $false;
2216
		}
2217
		$arr = array();
2218
		if ($numIndexes) {
2219
			$i = 0;
2220
			foreach($objarr as $v) $arr[$i++] = $v->name;
2221
		} else
2222
			foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2223
 
2224
		return $arr;
2225
	}
2226
 
2227
	/**
2228
	 * Different SQL databases used different methods to combine strings together.
2229
	 * This function provides a wrapper. 
2230
	 * 
2231
	 * param s	variable number of string parameters
2232
	 *
2233
	 * Usage: $db->Concat($str1,$str2);
2234
	 * 
2235
	 * @return concatenated string
2236
	 */ 	 
2237
	function Concat()
2238
	{	
2239
		$arr = func_get_args();
2240
		return implode($this->concat_operator, $arr);
2241
	}
2242
 
2243
 
2244
	/**
2245
	 * Converts a date "d" to a string that the database can understand.
2246
	 *
2247
	 * @param d	a date in Unix date time format.
2248
	 *
2249
	 * @return  date string in database date format
2250
	 */
2251
	function DBDate($d)
2252
	{
2253
		if (empty($d) && $d !== 0) return 'null';
2254
 
2255
		if (is_string($d) && !is_numeric($d)) {
2256
			if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
2257
			if ($this->isoDates) return "'$d'";
2258
			$d = ADOConnection::UnixDate($d);
2259
		}
2260
 
2261
		return adodb_date($this->fmtDate,$d);
2262
	}
2263
 
2264
 
2265
	/**
2266
	 * Converts a timestamp "ts" to a string that the database can understand.
2267
	 *
2268
	 * @param ts	a timestamp in Unix date time format.
2269
	 *
2270
	 * @return  timestamp string in database timestamp format
2271
	 */
2272
	function DBTimeStamp($ts)
2273
	{
2274
		if (empty($ts) && $ts !== 0) return 'null';
2275
 
2276
		# strlen(14) allows YYYYMMDDHHMMSS format
2277
		if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) 
2278
			return adodb_date($this->fmtTimeStamp,$ts);
2279
 
2280
		if ($ts === 'null') return $ts;
2281
		if ($this->isoDates && strlen($ts) !== 14) return "'$ts'";
2282
 
2283
		$ts = ADOConnection::UnixTimeStamp($ts);
2284
		return adodb_date($this->fmtTimeStamp,$ts);
2285
	}
2286
 
2287
	/**
2288
	 * Also in ADORecordSet.
2289
	 * @param $v is a date string in YYYY-MM-DD format
2290
	 *
2291
	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2292
	 */
2293
	function UnixDate($v)
2294
	{
2295
		if (is_object($v)) {
2296
		// odbtp support
2297
		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2298
			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2299
		}
2300
 
2301
		if (is_numeric($v) && strlen($v) !== 8) return $v;
2302
		if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", 
2303
			($v), $rr)) return false;
2304
 
2305
		if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
2306
		// h-m-s-MM-DD-YY
2307
		return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2308
	}
2309
 
2310
 
2311
	/**
2312
	 * Also in ADORecordSet.
2313
	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2314
	 *
2315
	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2316
	 */
2317
	function UnixTimeStamp($v)
2318
	{
2319
		if (is_object($v)) {
2320
		// odbtp support
2321
		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2322
			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2323
		}
2324
 
2325
		if (!preg_match( 
2326
			"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", 
2327
			($v), $rr)) return false;
2328
 
2329
		if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
2330
 
2331
		// h-m-s-MM-DD-YY
2332
		if (!isset($rr[5])) return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2333
		return  @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2334
	}
2335
 
2336
	/**
2337
	 * Also in ADORecordSet.
2338
	 *
2339
	 * Format database date based on user defined format.
2340
	 *
2341
	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
2342
	 * @param fmt 	is the format to apply to it, using date()
2343
	 *
2344
	 * @return a date formated as user desires
2345
	 */
2346
 
2347
	function UserDate($v,$fmt='Y-m-d',$gmt=false)
2348
	{
2349
		$tt = $this->UnixDate($v);
2350
 
2351
		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2352
		if (($tt === false || $tt == -1) && $v != false) return $v;
2353
		else if ($tt == 0) return $this->emptyDate;
2354
		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2355
		}
2356
 
2357
		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2358
 
2359
	}
2360
 
2361
		/**
2362
	 *
2363
	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
2364
	 * @param fmt 	is the format to apply to it, using date()
2365
	 *
2366
	 * @return a timestamp formated as user desires
2367
	 */
2368
	function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
2369
	{
2370
		if (!isset($v)) return $this->emptyTimeStamp;
2371
		# strlen(14) allows YYYYMMDDHHMMSS format
2372
		if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2373
		$tt = $this->UnixTimeStamp($v);
2374
		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2375
		if (($tt === false || $tt == -1) && $v != false) return $v;
2376
		if ($tt == 0) return $this->emptyTimeStamp;
2377
		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2378
	}
2379
 
2380
	function escape($s,$magic_quotes=false)
2381
	{
2382
		return $this->addq($s,$magic_quotes);
2383
	}
2384
 
2385
	/**
2386
	* Quotes a string, without prefixing nor appending quotes. 
2387
	*/
2388
	function addq($s,$magic_quotes=false)
2389
	{
2390
		if (!$magic_quotes) {
2391
 
2392
			if ($this->replaceQuote[0] == '\\'){
2393
				// only since php 4.0.5
2394
				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2395
				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2396
			}
2397
			return  str_replace("'",$this->replaceQuote,$s);
2398
		}
2399
 
2400
		// undo magic quotes for "
2401
		$s = str_replace('\\"','"',$s);
2402
 
2403
		if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2404
			return $s;
2405
		else {// change \' to '' for sybase/mssql
2406
			$s = str_replace('\\\\','\\',$s);
2407
			return str_replace("\\'",$this->replaceQuote,$s);
2408
		}
2409
	}
2410
 
2411
	/**
2412
	 * Correctly quotes a string so that all strings are escaped. We prefix and append
2413
	 * to the string single-quotes.
2414
	 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
2415
	 * 
2416
	 * @param s			the string to quote
2417
	 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
2418
	 *				This undoes the stupidity of magic quotes for GPC.
2419
	 *
2420
	 * @return  quoted string to be sent back to database
2421
	 */
2422
	function qstr($s,$magic_quotes=false)
2423
	{	
2424
		if (!$magic_quotes) {
2425
 
2426
			if ($this->replaceQuote[0] == '\\'){
2427
				// only since php 4.0.5
2428
				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2429
				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2430
			}
2431
			return  "'".str_replace("'",$this->replaceQuote,$s)."'";
2432
		}
2433
 
2434
		// undo magic quotes for "
2435
		$s = str_replace('\\"','"',$s);
2436
 
2437
		if ($this->replaceQuote == "\\'")  // ' already quoted, no need to change anything
2438
			return "'$s'";
2439
		else {// change \' to '' for sybase/mssql
2440
			$s = str_replace('\\\\','\\',$s);
2441
			return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2442
		}
2443
	}
2444
 
2445
 
2446
	/**
2447
	* Will select the supplied $page number from a recordset, given that it is paginated in pages of 
2448
	* $nrows rows per page. It also saves two boolean values saying if the given page is the first 
2449
	* and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2450
	*
2451
	* See readme.htm#ex8 for an example of usage.
2452
	*
2453
	* @param sql
2454
	* @param nrows		is the number of rows per page to get
2455
	* @param page		is the page number to get (1-based)
2456
	* @param [inputarr]	array of bind variables
2457
	* @param [secs2cache]		is a private parameter only used by jlim
2458
	* @return		the recordset ($rs->databaseType == 'array')
2459
	*
2460
	* NOTE: phpLens uses a different algorithm and does not use PageExecute().
2461
	*
2462
	*/
2463
	function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) 
2464
	{
2465
		global $ADODB_INCLUDED_LIB;
2466
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2467
		if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2468
		else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2469
		return $rs;
2470
	}
2471
 
2472
 
2473
	/**
2474
	* Will select the supplied $page number from a recordset, given that it is paginated in pages of 
2475
	* $nrows rows per page. It also saves two boolean values saying if the given page is the first 
2476
	* and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2477
	*
2478
	* @param secs2cache	seconds to cache data, set to 0 to force query
2479
	* @param sql
2480
	* @param nrows		is the number of rows per page to get
2481
	* @param page		is the page number to get (1-based)
2482
	* @param [inputarr]	array of bind variables
2483
	* @return		the recordset ($rs->databaseType == 'array')
2484
	*/
2485
	function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) 
2486
	{
2487
		/*switch($this->dataProvider) {
2488
		case 'postgres':
2489
		case 'mysql': 
2490
			break;
2491
		default: $secs2cache = 0; break;
2492
		}*/
2493
		$rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
2494
		return $rs;
2495
	}
2496
 
2497
} // end class ADOConnection
2498
 
2499
 
2500
 
2501
	//==============================================================================================	
2502
	// CLASS ADOFetchObj
2503
	//==============================================================================================	
2504
 
2505
	/**
2506
	* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
2507
	*/
2508
	class ADOFetchObj {
2509
	};
2510
 
2511
	//==============================================================================================	
2512
	// CLASS ADORecordSet_empty
2513
	//==============================================================================================	
2514
 
2515
	/**
2516
	* Lightweight recordset when there are no records to be returned
2517
	*/
2518
	class ADORecordSet_empty
2519
	{
2520
		var $dataProvider = 'empty';
2521
		var $databaseType = false;
2522
		var $EOF = true;
2523
		var $_numOfRows = 0;
2524
		var $fields = false;
2525
		var $connection = false;
2526
		function RowCount() {return 0;}
2527
		function RecordCount() {return 0;}
2528
		function PO_RecordCount(){return 0;}
2529
		function Close(){return true;}
2530
		function FetchRow() {return false;}
2531
		function FieldCount(){ return 0;}
2532
		function Init() {}
2533
	}
2534
 
2535
	//==============================================================================================	
2536
	// DATE AND TIME FUNCTIONS
2537
	//==============================================================================================	
2538
	include_once(ADODB_DIR.'/adodb-time.inc.php');
2539
 
2540
	//==============================================================================================	
2541
	// CLASS ADORecordSet
2542
	//==============================================================================================	
2543
 
2544
	if (PHP_VERSION < 5) include_once(ADODB_DIR.'/adodb-php4.inc.php');
2545
	else include_once(ADODB_DIR.'/adodb-iterator.inc.php');
2546
   /**
2547
	 * RecordSet class that represents the dataset returned by the database.
2548
	 * To keep memory overhead low, this class holds only the current row in memory.
2549
	 * No prefetching of data is done, so the RecordCount() can return -1 ( which
2550
	 * means recordcount not known).
2551
	 */
2552
	class ADORecordSet extends ADODB_BASE_RS {
2553
	/*
2554
	 * public variables	
2555
	 */
2556
	var $dataProvider = "native";
2557
	var $fields = false; 	/// holds the current row data
2558
	var $blobSize = 100; 	/// any varchar/char field this size or greater is treated as a blob
2559
							/// in other words, we use a text area for editing.
2560
	var $canSeek = false; 	/// indicates that seek is supported
2561
	var $sql; 				/// sql text
2562
	var $EOF = false;		/// Indicates that the current record position is after the last record in a Recordset object. 
2563
 
2564
	var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
2565
	var $emptyDate = '&nbsp;'; /// what to display when $time==0
2566
	var $debug = false;
2567
	var $timeCreated=0; 	/// datetime in Unix format rs created -- for cached recordsets
2568
 
2569
	var $bind = false; 		/// used by Fields() to hold array - should be private?
2570
	var $fetchMode;			/// default fetch mode
2571
	var $connection = false; /// the parent connection
2572
	/*
2573
	 *	private variables	
2574
	 */
2575
	var $_numOfRows = -1;	/** number of rows, or -1 */
2576
	var $_numOfFields = -1;	/** number of fields in recordset */
2577
	var $_queryID = -1;		/** This variable keeps the result link identifier.	*/
2578
	var $_currentRow = -1;	/** This variable keeps the current row in the Recordset.	*/
2579
	var $_closed = false; 	/** has recordset been closed */
2580
	var $_inited = false; 	/** Init() should only be called once */
2581
	var $_obj; 				/** Used by FetchObj */
2582
	var $_names;			/** Used by FetchObj */
2583
 
2584
	var $_currentPage = -1;	/** Added by Iván Oliva to implement recordset pagination */
2585
	var $_atFirstPage = false;	/** Added by Iván Oliva to implement recordset pagination */
2586
	var $_atLastPage = false;	/** Added by Iván Oliva to implement recordset pagination */
2587
	var $_lastPageNo = -1; 
2588
	var $_maxRecordCount = 0;
2589
	var $datetime = false;
2590
 
2591
	/**
2592
	 * Constructor
2593
	 *
2594
	 * @param queryID  	this is the queryID returned by ADOConnection->_query()
2595
	 *
2596
	 */
2597
	function ADORecordSet($queryID) 
2598
	{
2599
		$this->_queryID = $queryID;
2600
	}
2601
 
2602
 
2603
 
2604
	function Init()
2605
	{
2606
		if ($this->_inited) return;
2607
		$this->_inited = true;
2608
		if ($this->_queryID) @$this->_initrs();
2609
		else {
2610
			$this->_numOfRows = 0;
2611
			$this->_numOfFields = 0;
2612
		}
2613
		if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
2614
 
2615
			$this->_currentRow = 0;
2616
			if ($this->EOF = ($this->_fetch() === false)) {
2617
				$this->_numOfRows = 0; // _numOfRows could be -1
2618
			}
2619
		} else {
2620
			$this->EOF = true;
2621
		}
2622
	}
2623
 
2624
 
2625
	/**
2626
	 * Generate a SELECT tag string from a recordset, and return the string.
2627
	 * If the recordset has 2 cols, we treat the 1st col as the containing 
2628
	 * the text to display to the user, and 2nd col as the return value. Default
2629
	 * strings are compared with the FIRST column.
2630
	 *
2631
	 * @param name  		name of SELECT tag
2632
	 * @param [defstr]		the value to hilite. Use an array for multiple hilites for listbox.
2633
	 * @param [blank1stItem]	true to leave the 1st item in list empty
2634
	 * @param [multiple]		true for listbox, false for popup
2635
	 * @param [size]		#rows to show for listbox. not used by popup
2636
	 * @param [selectAttr]		additional attributes to defined for SELECT tag.
2637
	 *				useful for holding javascript onChange='...' handlers.
2638
	 & @param [compareFields0]	when we have 2 cols in recordset, we compare the defstr with 
2639
	 *				column 0 (1st col) if this is true. This is not documented.
2640
	 *
2641
	 * @return HTML
2642
	 *
2643
	 * changes by glen.davies@cce.ac.nz to support multiple hilited items
2644
	 */
2645
	function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
2646
			$size=0, $selectAttr='',$compareFields0=true)
2647
	{
2648
		global $ADODB_INCLUDED_LIB;
2649
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2650
		return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
2651
			$size, $selectAttr,$compareFields0);
2652
	}
2653
 
2654
 
2655
 
2656
	/**
2657
	 * Generate a SELECT tag string from a recordset, and return the string.
2658
	 * If the recordset has 2 cols, we treat the 1st col as the containing 
2659
	 * the text to display to the user, and 2nd col as the return value. Default
2660
	 * strings are compared with the SECOND column.
2661
	 *
2662
	 */
2663
	function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')	
2664
	{
2665
		return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
2666
			$size, $selectAttr,false);
2667
	}
2668
 
2669
	/*
2670
		Grouped Menu
2671
	*/
2672
	function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
2673
			$size=0, $selectAttr='')
2674
	{
2675
		global $ADODB_INCLUDED_LIB;
2676
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
2677
		return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
2678
			$size, $selectAttr,false);
2679
	}
2680
 
2681
	/**
2682
	 * return recordset as a 2-dimensional array.
2683
	 *
2684
	 * @param [nRows]  is the number of rows to return. -1 means every row.
2685
	 *
2686
	 * @return an array indexed by the rows (0-based) from the recordset
2687
	 */
2688
	function &GetArray($nRows = -1) 
2689
	{
2690
	global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
2691
		$results = adodb_getall($this,$nRows);
2692
		return $results;
2693
	}
2694
		$results = array();
2695
		$cnt = 0;
2696
		while (!$this->EOF && $nRows != $cnt) {
2697
			$results[] = $this->fields;
2698
			$this->MoveNext();
2699
			$cnt++;
2700
		}
2701
		return $results;
2702
	}
2703
 
2704
	function &GetAll($nRows = -1)
2705
	{
2706
		$arr =& $this->GetArray($nRows);
2707
		return $arr;
2708
	}
2709
 
2710
	/*
2711
	* Some databases allow multiple recordsets to be returned. This function
2712
	* will return true if there is a next recordset, or false if no more.
2713
	*/
2714
	function NextRecordSet()
2715
	{
2716
		return false;
2717
	}
2718
 
2719
	/**
2720
	 * return recordset as a 2-dimensional array. 
2721
	 * Helper function for ADOConnection->SelectLimit()
2722
	 *
2723
	 * @param offset	is the row to start calculations from (1-based)
2724
	 * @param [nrows]	is the number of rows to return
2725
	 *
2726
	 * @return an array indexed by the rows (0-based) from the recordset
2727
	 */
2728
	function &GetArrayLimit($nrows,$offset=-1) 
2729
	{	
2730
		if ($offset <= 0) {
2731
			$arr =& $this->GetArray($nrows);
2732
			return $arr;
2733
		} 
2734
 
2735
		$this->Move($offset);
2736
 
2737
		$results = array();
2738
		$cnt = 0;
2739
		while (!$this->EOF && $nrows != $cnt) {
2740
			$results[$cnt++] = $this->fields;
2741
			$this->MoveNext();
2742
		}
2743
 
2744
		return $results;
2745
	}
2746
 
2747
 
2748
	/**
2749
	 * Synonym for GetArray() for compatibility with ADO.
2750
	 *
2751
	 * @param [nRows]  is the number of rows to return. -1 means every row.
2752
	 *
2753
	 * @return an array indexed by the rows (0-based) from the recordset
2754
	 */
2755
	function &GetRows($nRows = -1) 
2756
	{
2757
		$arr =& $this->GetArray($nRows);
2758
		return $arr;
2759
	}
2760
 
2761
	/**
2762
	 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. 
2763
	 * The first column is treated as the key and is not included in the array. 
2764
	 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
2765
	 * $force_array == true.
2766
	 *
2767
	 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
2768
	 * 	array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
2769
	 * 	read the source.
2770
	 *
2771
	 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and 
2772
	 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
2773
	 *
2774
	 * @return an associative array indexed by the first column of the array, 
2775
	 * 	or false if the  data has less than 2 cols.
2776
	 */
2777
	function &GetAssoc($force_array = false, $first2cols = false) 
2778
	{
2779
	global $ADODB_EXTENSION;
2780
 
2781
		$cols = $this->_numOfFields;
2782
		if ($cols < 2) {
2783
			$false = false;
2784
			return $false;
2785
		}
2786
		$numIndex = isset($this->fields[0]);
2787
		$results = array();
2788
 
2789
		if (!$first2cols && ($cols > 2 || $force_array)) {
2790
			if ($ADODB_EXTENSION) {
2791
				if ($numIndex) {
2792
					while (!$this->EOF) {
2793
						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
2794
						adodb_movenext($this);
2795
					}
2796
				} else {
2797
					while (!$this->EOF) {
2798
						$results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2799
						adodb_movenext($this);
2800
					}
2801
				}
2802
			} else {
2803
				if ($numIndex) {
2804
					while (!$this->EOF) {
2805
						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
2806
						$this->MoveNext();
2807
					}
2808
				} else {
2809
					while (!$this->EOF) {
2810
						$results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2811
						$this->MoveNext();
2812
					}
2813
				}
2814
			}
2815
		} else {
2816
			if ($ADODB_EXTENSION) {
2817
				// return scalar values
2818
				if ($numIndex) {
2819
					while (!$this->EOF) {
2820
					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2821
						$results[trim(($this->fields[0]))] = $this->fields[1];
2822
						adodb_movenext($this);
2823
					}
2824
				} else {
2825
					while (!$this->EOF) {
2826
					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2827
						$v1 = trim(reset($this->fields));
2828
						$v2 = ''.next($this->fields); 
2829
						$results[$v1] = $v2;
2830
						adodb_movenext($this);
2831
					}
2832
				}
2833
			} else {
2834
				if ($numIndex) {
2835
					while (!$this->EOF) {
2836
					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2837
						$results[trim(($this->fields[0]))] = $this->fields[1];
2838
						$this->MoveNext();
2839
					}
2840
				} else {
2841
					while (!$this->EOF) {
2842
					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2843
						$v1 = trim(reset($this->fields));
2844
						$v2 = ''.next($this->fields); 
2845
						$results[$v1] = $v2;
2846
						$this->MoveNext();
2847
					}
2848
				}
2849
			}
2850
		}
2851
 
2852
		$ref =& $results; # workaround accelerator incompat with PHP 4.4 :(
2853
		return $ref; 
2854
	}
2855
 
2856
 
2857
	/**
2858
	 *
2859
	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
2860
	 * @param fmt 	is the format to apply to it, using date()
2861
	 *
2862
	 * @return a timestamp formated as user desires
2863
	 */
2864
	function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
2865
	{
2866
		if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
2867
		$tt = $this->UnixTimeStamp($v);
2868
		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2869
		if (($tt === false || $tt == -1) && $v != false) return $v;
2870
		if ($tt === 0) return $this->emptyTimeStamp;
2871
		return adodb_date($fmt,$tt);
2872
	}
2873
 
2874
 
2875
	/**
2876
	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
2877
	 * @param fmt 	is the format to apply to it, using date()
2878
	 *
2879
	 * @return a date formated as user desires
2880
	 */
2881
	function UserDate($v,$fmt='Y-m-d')
2882
	{
2883
		$tt = $this->UnixDate($v);
2884
		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2885
		if (($tt === false || $tt == -1) && $v != false) return $v;
2886
		else if ($tt == 0) return $this->emptyDate;
2887
		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2888
		}
2889
		return adodb_date($fmt,$tt);
2890
	}
2891
 
2892
 
2893
	/**
2894
	 * @param $v is a date string in YYYY-MM-DD format
2895
	 *
2896
	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2897
	 */
2898
	function UnixDate($v)
2899
	{
2900
		return ADOConnection::UnixDate($v);
2901
	}
2902
 
2903
 
2904
	/**
2905
	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2906
	 *
2907
	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2908
	 */
2909
	function UnixTimeStamp($v)
2910
	{
2911
		return ADOConnection::UnixTimeStamp($v);
2912
	}
2913
 
2914
 
2915
	/**
2916
	* PEAR DB Compat - do not use internally
2917
	*/
2918
	function Free()
2919
	{
2920
		return $this->Close();
2921
	}
2922
 
2923
 
2924
	/**
2925
	* PEAR DB compat, number of rows
2926
	*/
2927
	function NumRows()
2928
	{
2929
		return $this->_numOfRows;
2930
	}
2931
 
2932
 
2933
	/**
2934
	* PEAR DB compat, number of cols
2935
	*/
2936
	function NumCols()
2937
	{
2938
		return $this->_numOfFields;
2939
	}
2940
 
2941
	/**
2942
	* Fetch a row, returning false if no more rows. 
2943
	* This is PEAR DB compat mode.
2944
	*
2945
	* @return false or array containing the current record
2946
	*/
2947
	function &FetchRow()
2948
	{
2949
		if ($this->EOF) {
2950
			$false = false;
2951
			return $false;
2952
		}
2953
		$arr = $this->fields;
2954
		$this->_currentRow++;
2955
		if (!$this->_fetch()) $this->EOF = true;
2956
		return $arr;
2957
	}
2958
 
2959
 
2960
	/**
2961
	* Fetch a row, returning PEAR_Error if no more rows. 
2962
	* This is PEAR DB compat mode.
2963
	*
2964
	* @return DB_OK or error object
2965
	*/
2966
	function FetchInto(&$arr)
2967
	{
2968
		if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
2969
		$arr = $this->fields;
2970
		$this->MoveNext();
2971
		return 1; // DB_OK
2972
	}
2973
 
2974
 
2975
	/**
2976
	 * Move to the first row in the recordset. Many databases do NOT support this.
2977
	 *
2978
	 * @return true or false
2979
	 */
2980
	function MoveFirst() 
2981
	{
2982
		if ($this->_currentRow == 0) return true;
2983
		return $this->Move(0);			
2984
	}			
2985
 
2986
 
2987
	/**
2988
	 * Move to the last row in the recordset. 
2989
	 *
2990
	 * @return true or false
2991
	 */
2992
	function MoveLast() 
2993
	{
2994
		if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
2995
		if ($this->EOF) return false;
2996
		while (!$this->EOF) {
2997
			$f = $this->fields;
2998
			$this->MoveNext();
2999
		}
3000
		$this->fields = $f;
3001
		$this->EOF = false;
3002
		return true;
3003
	}
3004
 
3005
 
3006
	/**
3007
	 * Move to next record in the recordset.
3008
	 *
3009
	 * @return true if there still rows available, or false if there are no more rows (EOF).
3010
	 */
3011
	function MoveNext() 
3012
	{
3013
		if (!$this->EOF) {
3014
			$this->_currentRow++;
3015
			if ($this->_fetch()) return true;
3016
		}
3017
		$this->EOF = true;
3018
		/* -- tested error handling when scrolling cursor -- seems useless.
3019
		$conn = $this->connection;
3020
		if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3021
			$fn = $conn->raiseErrorFn;
3022
			$fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3023
		}
3024
		*/
3025
		return false;
3026
	}
3027
 
3028
 
3029
	/**
3030
	 * Random access to a specific row in the recordset. Some databases do not support
3031
	 * access to previous rows in the databases (no scrolling backwards).
3032
	 *
3033
	 * @param rowNumber is the row to move to (0-based)
3034
	 *
3035
	 * @return true if there still rows available, or false if there are no more rows (EOF).
3036
	 */
3037
	function Move($rowNumber = 0) 
3038
	{
3039
		$this->EOF = false;
3040
		if ($rowNumber == $this->_currentRow) return true;
3041
		if ($rowNumber >= $this->_numOfRows)
3042
	   		if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
3043
 
3044
		if ($this->canSeek) { 
3045
 
3046
			if ($this->_seek($rowNumber)) {
3047
				$this->_currentRow = $rowNumber;
3048
				if ($this->_fetch()) {
3049
					return true;
3050
				}
3051
			} else {
3052
				$this->EOF = true;
3053
				return false;
3054
			}
3055
		} else {
3056
			if ($rowNumber < $this->_currentRow) return false;
3057
			global $ADODB_EXTENSION;
3058
			if ($ADODB_EXTENSION) {
3059
				while (!$this->EOF && $this->_currentRow < $rowNumber) {
3060
					adodb_movenext($this);
3061
				}
3062
			} else {
3063
 
3064
				while (! $this->EOF && $this->_currentRow < $rowNumber) {
3065
					$this->_currentRow++;
3066
 
3067
					if (!$this->_fetch()) $this->EOF = true;
3068
				}
3069
			}
3070
			return !($this->EOF);
3071
		}
3072
 
3073
		$this->fields = false;	
3074
		$this->EOF = true;
3075
		return false;
3076
	}
3077
 
3078
 
3079
	/**
3080
	 * Get the value of a field in the current row by column name.
3081
	 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3082
	 * 
3083
	 * @param colname  is the field to access
3084
	 *
3085
	 * @return the value of $colname column
3086
	 */
3087
	function Fields($colname)
3088
	{
3089
		return $this->fields[$colname];
3090
	}
3091
 
3092
	function GetAssocKeys($upper=true)
3093
	{
3094
		$this->bind = array();
3095
		for ($i=0; $i < $this->_numOfFields; $i++) {
3096
			$o = $this->FetchField($i);
3097
			if ($upper === 2) $this->bind[$o->name] = $i;
3098
			else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
3099
		}
3100
	}
3101
 
3102
  /**
3103
   * Use associative array to get fields array for databases that do not support
3104
   * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
3105
   *
3106
   * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
3107
   * before you execute your SQL statement, and access $rs->fields['col'] directly.
3108
   *
3109
   * $upper  0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
3110
   */
3111
	function &GetRowAssoc($upper=1)
3112
	{
3113
		$record = array();
3114
	 //	if (!$this->fields) return $record;
3115
 
3116
	   	if (!$this->bind) {
3117
			$this->GetAssocKeys($upper);
3118
		}
3119
 
3120
		foreach($this->bind as $k => $v) {
3121
			$record[$k] = $this->fields[$v];
3122
		}
3123
 
3124
		return $record;
3125
	}
3126
 
3127
 
3128
	/**
3129
	 * Clean up recordset
3130
	 *
3131
	 * @return true or false
3132
	 */
3133
	function Close() 
3134
	{
3135
		// free connection object - this seems to globally free the object
3136
		// and not merely the reference, so don't do this...
3137
		// $this->connection = false; 
3138
		if (!$this->_closed) {
3139
			$this->_closed = true;
3140
			return $this->_close();		
3141
		} else
3142
			return true;
3143
	}
3144
 
3145
	/**
3146
	 * synonyms RecordCount and RowCount	
3147
	 *
3148
	 * @return the number of rows or -1 if this is not supported
3149
	 */
3150
	function RecordCount() {return $this->_numOfRows;}
3151
 
3152
 
3153
	/*
3154
	* If we are using PageExecute(), this will return the maximum possible rows
3155
	* that can be returned when paging a recordset.
3156
	*/
3157
	function MaxRecordCount()
3158
	{
3159
		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
3160
	}
3161
 
3162
	/**
3163
	 * synonyms RecordCount and RowCount	
3164
	 *
3165
	 * @return the number of rows or -1 if this is not supported
3166
	 */
3167
	function RowCount() {return $this->_numOfRows;} 
3168
 
3169
 
3170
	 /**
3171
	 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
3172
	 *
3173
	 * @return  the number of records from a previous SELECT. All databases support this.
3174
	 *
3175
	 * But aware possible problems in multiuser environments. For better speed the table
3176
	 * must be indexed by the condition. Heavy test this before deploying.
3177
	 */ 
3178
	function PO_RecordCount($table="", $condition="") {
3179
 
3180
		$lnumrows = $this->_numOfRows;
3181
		// the database doesn't support native recordcount, so we do a workaround
3182
		if ($lnumrows == -1 && $this->connection) {
3183
			IF ($table) {
3184
				if ($condition) $condition = " WHERE " . $condition; 
3185
				$resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
3186
				if ($resultrows) $lnumrows = reset($resultrows->fields);
3187
			}
3188
		}
3189
		return $lnumrows;
3190
	}
3191
 
3192
 
3193
	/**
3194
	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3195
	 */
3196
	function CurrentRow() {return $this->_currentRow;}
3197
 
3198
	/**
3199
	 * synonym for CurrentRow -- for ADO compat
3200
	 *
3201
	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
3202
	 */
3203
	function AbsolutePosition() {return $this->_currentRow;}
3204
 
3205
	/**
3206
	 * @return the number of columns in the recordset. Some databases will set this to 0
3207
	 * if no records are returned, others will return the number of columns in the query.
3208
	 */
3209
	function FieldCount() {return $this->_numOfFields;}   
3210
 
3211
 
3212
	/**
3213
	 * Get the ADOFieldObject of a specific column.
3214
	 *
3215
	 * @param fieldoffset	is the column position to access(0-based).
3216
	 *
3217
	 * @return the ADOFieldObject for that column, or false.
3218
	 */
3219
	function &FetchField($fieldoffset) 
3220
	{
3221
		// must be defined by child class
3222
	}	
3223
 
3224
	/**
3225
	 * Get the ADOFieldObjects of all columns in an array.
3226
	 *
3227
	 */
3228
	function& FieldTypesArray()
3229
	{
3230
		$arr = array();
3231
		for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) 
3232
			$arr[] = $this->FetchField($i);
3233
		return $arr;
3234
	}
3235
 
3236
	/**
3237
	* Return the fields array of the current row as an object for convenience.
3238
	* The default case is lowercase field names.
3239
	*
3240
	* @return the object with the properties set to the fields of the current row
3241
	*/
3242
	function &FetchObj()
3243
	{
3244
		$o =& $this->FetchObject(false);
3245
		return $o;
3246
	}
3247
 
3248
	/**
3249
	* Return the fields array of the current row as an object for convenience.
3250
	* The default case is uppercase.
3251
	* 
3252
	* @param $isupper to set the object property names to uppercase
3253
	*
3254
	* @return the object with the properties set to the fields of the current row
3255
	*/
3256
	function &FetchObject($isupper=true)
3257
	{
3258
		if (empty($this->_obj)) {
3259
			$this->_obj = new ADOFetchObj();
3260
			$this->_names = array();
3261
			for ($i=0; $i <$this->_numOfFields; $i++) {
3262
				$f = $this->FetchField($i);
3263
				$this->_names[] = $f->name;
3264
			}
3265
		}
3266
		$i = 0;
3267
		if (PHP_VERSION >= 5) $o = clone($this->_obj);
3268
		else $o = $this->_obj;
3269
 
3270
		for ($i=0; $i <$this->_numOfFields; $i++) {
3271
			$name = $this->_names[$i];
3272
			if ($isupper) $n = strtoupper($name);
3273
			else $n = $name;
3274
 
3275
			$o->$n = $this->Fields($name);
3276
		}
3277
		return $o;
3278
	}
3279
 
3280
	/**
3281
	* Return the fields array of the current row as an object for convenience.
3282
	* The default is lower-case field names.
3283
	* 
3284
	* @return the object with the properties set to the fields of the current row,
3285
	* 	or false if EOF
3286
	*
3287
	* Fixed bug reported by tim@orotech.net
3288
	*/
3289
	function &FetchNextObj()
3290
	{
3291
		$o =& $this->FetchNextObject(false);
3292
		return $o;
3293
	}
3294
 
3295
 
3296
	/**
3297
	* Return the fields array of the current row as an object for convenience. 
3298
	* The default is upper case field names.
3299
	* 
3300
	* @param $isupper to set the object property names to uppercase
3301
	*
3302
	* @return the object with the properties set to the fields of the current row,
3303
	* 	or false if EOF
3304
	*
3305
	* Fixed bug reported by tim@orotech.net
3306
	*/
3307
	function &FetchNextObject($isupper=true)
3308
	{
3309
		$o = false;
3310
		if ($this->_numOfRows != 0 && !$this->EOF) {
3311
			$o = $this->FetchObject($isupper);	
3312
			$this->_currentRow++;
3313
			if ($this->_fetch()) return $o;
3314
		}
3315
		$this->EOF = true;
3316
		return $o;
3317
	}
3318
 
3319
	/**
3320
	 * Get the metatype of the column. This is used for formatting. This is because
3321
	 * many databases use different names for the same type, so we transform the original
3322
	 * type to our standardised version which uses 1 character codes:
3323
	 *
3324
	 * @param t  is the type passed in. Normally is ADOFieldObject->type.
3325
	 * @param len is the maximum length of that field. This is because we treat character
3326
	 * 	fields bigger than a certain size as a 'B' (blob).
3327
	 * @param fieldobj is the field object returned by the database driver. Can hold
3328
	 *	additional info (eg. primary_key for mysql).
3329
	 * 
3330
	 * @return the general type of the data: 
3331
	 *	C for character < 250 chars
3332
	 *	X for teXt (>= 250 chars)
3333
	 *	B for Binary
3334
	 * 	N for numeric or floating point
3335
	 *	D for date
3336
	 *	T for timestamp
3337
	 * 	L for logical/Boolean
3338
	 *	I for integer
3339
	 *	R for autoincrement counter/integer
3340
	 * 
3341
	 *
3342
	*/
3343
	function MetaType($t,$len=-1,$fieldobj=false)
3344
	{
3345
		if (is_object($t)) {
3346
			$fieldobj = $t;
3347
			$t = $fieldobj->type;
3348
			$len = $fieldobj->max_length;
3349
		}
3350
	// changed in 2.32 to hashing instead of switch stmt for speed...
3351
	static $typeMap = array(
3352
		'VARCHAR' => 'C',
3353
		'VARCHAR2' => 'C',
3354
		'CHAR' => 'C',
3355
		'C' => 'C',
3356
		'STRING' => 'C',
3357
		'NCHAR' => 'C',
3358
		'NVARCHAR' => 'C',
3359
		'VARYING' => 'C',
3360
		'BPCHAR' => 'C',
3361
		'CHARACTER' => 'C',
3362
		'INTERVAL' => 'C',  # Postgres
3363
		##
3364
		'LONGCHAR' => 'X',
3365
		'TEXT' => 'X',
3366
		'NTEXT' => 'X',
3367
		'M' => 'X',
3368
		'X' => 'X',
3369
		'CLOB' => 'X',
3370
		'NCLOB' => 'X',
3371
		'LVARCHAR' => 'X',
3372
		##
3373
		'BLOB' => 'B',
3374
		'IMAGE' => 'B',
3375
		'BINARY' => 'B',
3376
		'VARBINARY' => 'B',
3377
		'LONGBINARY' => 'B',
3378
		'B' => 'B',
3379
		##
3380
		'YEAR' => 'D', // mysql
3381
		'DATE' => 'D',
3382
		'D' => 'D',
3383
		##
3384
		'TIME' => 'T',
3385
		'TIMESTAMP' => 'T',
3386
		'DATETIME' => 'T',
3387
		'TIMESTAMPTZ' => 'T',
3388
		'T' => 'T',
3389
		##
3390
		'BOOL' => 'L',
3391
		'BOOLEAN' => 'L', 
3392
		'BIT' => 'L',
3393
		'L' => 'L',
3394
		##
3395
		'COUNTER' => 'R',
3396
		'R' => 'R',
3397
		'SERIAL' => 'R', // ifx
3398
		'INT IDENTITY' => 'R',
3399
		##
3400
		'INT' => 'I',
3401
		'INT2' => 'I',
3402
		'INT4' => 'I',
3403
		'INT8' => 'I',
3404
		'INTEGER' => 'I',
3405
		'INTEGER UNSIGNED' => 'I',
3406
		'SHORT' => 'I',
3407
		'TINYINT' => 'I',
3408
		'SMALLINT' => 'I',
3409
		'I' => 'I',
3410
		##
3411
		'LONG' => 'N', // interbase is numeric, oci8 is blob
3412
		'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
3413
		'DECIMAL' => 'N',
3414
		'DEC' => 'N',
3415
		'REAL' => 'N',
3416
		'DOUBLE' => 'N',
3417
		'DOUBLE PRECISION' => 'N',
3418
		'SMALLFLOAT' => 'N',
3419
		'FLOAT' => 'N',
3420
		'NUMBER' => 'N',
3421
		'NUM' => 'N',
3422
		'NUMERIC' => 'N',
3423
		'MONEY' => 'N',
3424
 
3425
		## informix 9.2
3426
		'SQLINT' => 'I', 
3427
		'SQLSERIAL' => 'I', 
3428
		'SQLSMINT' => 'I', 
3429
		'SQLSMFLOAT' => 'N', 
3430
		'SQLFLOAT' => 'N', 
3431
		'SQLMONEY' => 'N', 
3432
		'SQLDECIMAL' => 'N', 
3433
		'SQLDATE' => 'D', 
3434
		'SQLVCHAR' => 'C', 
3435
		'SQLCHAR' => 'C', 
3436
		'SQLDTIME' => 'T', 
3437
		'SQLINTERVAL' => 'N', 
3438
		'SQLBYTES' => 'B', 
3439
		'SQLTEXT' => 'X' 
3440
		);
3441
 
3442
		$tmap = false;
3443
		$t = strtoupper($t);
3444
		$tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
3445
		switch ($tmap) {
3446
		case 'C':
3447
 
3448
			// is the char field is too long, return as text field... 
3449
			if ($this->blobSize >= 0) {
3450
				if ($len > $this->blobSize) return 'X';
3451
			} else if ($len > 250) {
3452
				return 'X';
3453
			}
3454
			return 'C';
3455
 
3456
		case 'I':
3457
			if (!empty($fieldobj->primary_key)) return 'R';
3458
			return 'I';
3459
 
3460
		case false:
3461
			return 'N';
3462
 
3463
		case 'B':
3464
			 if (isset($fieldobj->binary)) 
3465
				 return ($fieldobj->binary) ? 'B' : 'X';
3466
			return 'B';
3467
 
3468
		case 'D':
3469
			if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T';
3470
			return 'D';
3471
 
3472
		default: 
3473
			if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
3474
			return $tmap;
3475
		}
3476
	}
3477
 
3478
	function _close() {}
3479
 
3480
	/**
3481
	 * set/returns the current recordset page when paginating
3482
	 */
3483
	function AbsolutePage($page=-1)
3484
	{
3485
		if ($page != -1) $this->_currentPage = $page;
3486
		return $this->_currentPage;
3487
	}
3488
 
3489
	/**
3490
	 * set/returns the status of the atFirstPage flag when paginating
3491
	 */
3492
	function AtFirstPage($status=false)
3493
	{
3494
		if ($status != false) $this->_atFirstPage = $status;
3495
		return $this->_atFirstPage;
3496
	}
3497
 
3498
	function LastPageNo($page = false)
3499
	{
3500
		if ($page != false) $this->_lastPageNo = $page;
3501
		return $this->_lastPageNo;
3502
	}
3503
 
3504
	/**
3505
	 * set/returns the status of the atLastPage flag when paginating
3506
	 */
3507
	function AtLastPage($status=false)
3508
	{
3509
		if ($status != false) $this->_atLastPage = $status;
3510
		return $this->_atLastPage;
3511
	}
3512
 
3513
} // end class ADORecordSet
3514
 
3515
	//==============================================================================================	
3516
	// CLASS ADORecordSet_array
3517
	//==============================================================================================	
3518
 
3519
	/**
3520
	 * This class encapsulates the concept of a recordset created in memory
3521
	 * as an array. This is useful for the creation of cached recordsets.
3522
	 * 
3523
	 * Note that the constructor is different from the standard ADORecordSet
3524
	 */
3525
 
3526
	class ADORecordSet_array extends ADORecordSet
3527
	{
3528
		var $databaseType = 'array';
3529
 
3530
		var $_array; 	// holds the 2-dimensional data array
3531
		var $_types;	// the array of types of each column (C B I L M)
3532
		var $_colnames;	// names of each column in array
3533
		var $_skiprow1;	// skip 1st row because it holds column names
3534
		var $_fieldarr; // holds array of field objects
3535
		var $canSeek = true;
3536
		var $affectedrows = false;
3537
		var $insertid = false;
3538
		var $sql = '';
3539
		var $compat = false;
3540
		/**
3541
		 * Constructor
3542
		 *
3543
		 */
3544
		function ADORecordSet_array($fakeid=1)
3545
		{
3546
		global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
3547
 
3548
			// fetch() on EOF does not delete $this->fields
3549
			$this->compat = !empty($ADODB_COMPAT_FETCH);
3550
			$this->ADORecordSet($fakeid); // fake queryID		
3551
			$this->fetchMode = $ADODB_FETCH_MODE;
3552
		}
3553
 
3554
 
3555
		/**
3556
		 * Setup the array.
3557
		 *
3558
		 * @param array		is a 2-dimensional array holding the data.
3559
		 *			The first row should hold the column names 
3560
		 *			unless paramter $colnames is used.
3561
		 * @param typearr	holds an array of types. These are the same types 
3562
		 *			used in MetaTypes (C,B,L,I,N).
3563
		 * @param [colnames]	array of column names. If set, then the first row of
3564
		 *			$array should not hold the column names.
3565
		 */
3566
		function InitArray($array,$typearr,$colnames=false)
3567
		{
3568
			$this->_array = $array;
3569
			$this->_types = $typearr;	
3570
			if ($colnames) {
3571
				$this->_skiprow1 = false;
3572
				$this->_colnames = $colnames;
3573
			} else  {
3574
				$this->_skiprow1 = true;
3575
				$this->_colnames = $array[0];
3576
			}
3577
			$this->Init();
3578
		}
3579
		/**
3580
		 * Setup the Array and datatype file objects
3581
		 *
3582
		 * @param array		is a 2-dimensional array holding the data.
3583
		 *			The first row should hold the column names 
3584
		 *			unless paramter $colnames is used.
3585
		 * @param fieldarr	holds an array of ADOFieldObject's.
3586
		 */
3587
		function InitArrayFields(&$array,&$fieldarr)
3588
		{
3589
			$this->_array =& $array;
3590
			$this->_skiprow1= false;
3591
			if ($fieldarr) {
3592
				$this->_fieldobjects =& $fieldarr;
3593
			} 
3594
			$this->Init();
3595
		}
3596
 
3597
		function &GetArray($nRows=-1)
3598
		{
3599
			if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
3600
				return $this->_array;
3601
			} else {
3602
				$arr =& ADORecordSet::GetArray($nRows);
3603
				return $arr;
3604
			}
3605
		}
3606
 
3607
		function _initrs()
3608
		{
3609
			$this->_numOfRows =  sizeof($this->_array);
3610
			if ($this->_skiprow1) $this->_numOfRows -= 1;
3611
 
3612
			$this->_numOfFields =(isset($this->_fieldobjects)) ?
3613
				 sizeof($this->_fieldobjects):sizeof($this->_types);
3614
		}
3615
 
3616
		/* Use associative array to get fields array */
3617
		function Fields($colname)
3618
		{
3619
			$mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
3620
 
3621
			if ($mode & ADODB_FETCH_ASSOC) {
3622
				if (!isset($this->fields[$colname])) $colname = strtolower($colname);
3623
				return $this->fields[$colname];
3624
			}
3625
			if (!$this->bind) {
3626
				$this->bind = array();
3627
				for ($i=0; $i < $this->_numOfFields; $i++) {
3628
					$o = $this->FetchField($i);
3629
					$this->bind[strtoupper($o->name)] = $i;
3630
				}
3631
			}
3632
			return $this->fields[$this->bind[strtoupper($colname)]];
3633
		}
3634
 
3635
		function &FetchField($fieldOffset = -1) 
3636
		{
3637
			if (isset($this->_fieldobjects)) {
3638
				return $this->_fieldobjects[$fieldOffset];
3639
			}
3640
			$o =  new ADOFieldObject();
3641
			$o->name = $this->_colnames[$fieldOffset];
3642
			$o->type =  $this->_types[$fieldOffset];
3643
			$o->max_length = -1; // length not known
3644
 
3645
			return $o;
3646
		}
3647
 
3648
		function _seek($row)
3649
		{
3650
			if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
3651
				$this->_currentRow = $row;
3652
				if ($this->_skiprow1) $row += 1;
3653
				$this->fields = $this->_array[$row];
3654
				return true;
3655
			}
3656
			return false;
3657
		}
3658
 
3659
		function MoveNext() 
3660
		{
3661
			if (!$this->EOF) {		
3662
				$this->_currentRow++;
3663
 
3664
				$pos = $this->_currentRow;
3665
 
3666
				if ($this->_numOfRows <= $pos) {
3667
					if (!$this->compat) $this->fields = false;
3668
				} else {
3669
					if ($this->_skiprow1) $pos += 1;
3670
					$this->fields = $this->_array[$pos];
3671
					return true;
3672
				}		
3673
				$this->EOF = true;
3674
			}
3675
 
3676
			return false;
3677
		}	
3678
 
3679
		function _fetch()
3680
		{
3681
			$pos = $this->_currentRow;
3682
 
3683
			if ($this->_numOfRows <= $pos) {
3684
				if (!$this->compat) $this->fields = false;
3685
				return false;
3686
			}
3687
			if ($this->_skiprow1) $pos += 1;
3688
			$this->fields = $this->_array[$pos];
3689
			return true;
3690
		}
3691
 
3692
		function _close() 
3693
		{
3694
			return true;	
3695
		}
3696
 
3697
	} // ADORecordSet_array
3698
 
3699
	//==============================================================================================	
3700
	// HELPER FUNCTIONS
3701
	//==============================================================================================			
3702
 
3703
	/**
3704
	 * Synonym for ADOLoadCode. Private function. Do not use.
3705
	 *
3706
	 * @deprecated
3707
	 */
3708
	function ADOLoadDB($dbType) 
3709
	{ 
3710
		return ADOLoadCode($dbType);
3711
	}
3712
 
3713
	/**
3714
	 * Load the code for a specific database driver. Private function. Do not use.
3715
	 */
3716
	function ADOLoadCode($dbType) 
3717
	{
3718
	global $ADODB_LASTDB;
3719
 
3720
		if (!$dbType) return false;
3721
		$db = strtolower($dbType);
3722
		switch ($db) {
3723
			case 'ado': 
3724
				if (PHP_VERSION >= 5) $db = 'ado5';
3725
				$class = 'ado'; 
3726
				break;
3727
			case 'ifx':
3728
			case 'maxsql': $class = $db = 'mysqlt'; break;
3729
			case 'postgres':
3730
			case 'postgres8':
3731
			case 'pgsql': $class = $db = 'postgres7'; break;
3732
			default:
3733
				$class = $db; break;
3734
		}
3735
 
3736
		$file = ADODB_DIR."/drivers/adodb-".$db.".inc.php";
3737
		@include_once($file);
3738
		$ADODB_LASTDB = $class;
3739
		if (class_exists("ADODB_" . $class)) return $class;
3740
 
3741
		//ADOConnection::outp(adodb_pr(get_declared_classes(),true));
3742
		if (!file_exists($file)) ADOConnection::outp("Missing file: $file");
3743
		else ADOConnection::outp("Syntax error in file: $file");
3744
		return false;
3745
	}
3746
 
3747
	/**
3748
	 * synonym for ADONewConnection for people like me who cannot remember the correct name
3749
	 */
3750
	function &NewADOConnection($db='')
3751
	{
3752
		$tmp =& ADONewConnection($db);
3753
		return $tmp;
3754
	}
3755
 
3756
	/**
3757
	 * Instantiate a new Connection class for a specific database driver.
3758
	 *
3759
	 * @param [db]  is the database Connection object to create. If undefined,
3760
	 * 	use the last database driver that was loaded by ADOLoadCode().
3761
	 *
3762
	 * @return the freshly created instance of the Connection class.
3763
	 */
3764
	function &ADONewConnection($db='')
3765
	{
3766
	GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB;
3767
 
3768
		if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
3769
		$errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
3770
		$false = false;
3771
		if ($at = strpos($db,'://')) {
3772
			$origdsn = $db;
3773
			if (PHP_VERSION < 5) $dsna = @parse_url($db);
3774
			else {
3775
				$fakedsn = 'fake'.substr($db,$at);
3776
				$dsna = @parse_url($fakedsn);
3777
				$dsna['scheme'] = substr($db,0,$at);
3778
 
3779
				if (strncmp($db,'pdo',3) == 0) {
3780
					$sch = explode('_',$dsna['scheme']);
3781
					if (sizeof($sch)>1) {
3782
						$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
3783
						$dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
3784
						$dsna['scheme'] = 'pdo';
3785
					}
3786
				}
3787
			}
3788
 
3789
			if (!$dsna) {
3790
				// special handling of oracle, which might not have host
3791
				$db = str_replace('@/','@adodb-fakehost/',$db);
3792
				$dsna = parse_url($db);
3793
				if (!$dsna) return $false;
3794
				$dsna['host'] = '';
3795
			}
3796
			$db = @$dsna['scheme'];
3797
			if (!$db) return $false;
3798
			$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
3799
			$dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
3800
			$dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
3801
			$dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
3802
 
3803
			if (isset($dsna['query'])) {
3804
				$opt1 = explode('&',$dsna['query']);
3805
				foreach($opt1 as $k => $v) {
3806
					$arr = explode('=',$v);
3807
					$opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
3808
				}
3809
			} else $opt = array();
3810
		}
3811
	/*
3812
	 *  phptype: Database backend used in PHP (mysql, odbc etc.)
3813
	 *  dbsyntax: Database used with regards to SQL syntax etc.
3814
	 *  protocol: Communication protocol to use (tcp, unix etc.)
3815
	 *  hostspec: Host specification (hostname[:port])
3816
	 *  database: Database to use on the DBMS server
3817
	 *  username: User name for login
3818
	 *  password: Password for login
3819
	 */
3820
		if (!empty($ADODB_NEWCONNECTION)) {
3821
			$obj = $ADODB_NEWCONNECTION($db);
3822
 
3823
		} else {
3824
 
3825
			if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
3826
			if (empty($db)) $db = $ADODB_LASTDB;
3827
 
3828
			if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db);
3829
 
3830
			if (!$db) {
3831
				if (isset($origdsn)) $db = $origdsn;
3832
				if ($errorfn) {
3833
					// raise an error
3834
					$ignore = false;
3835
					$errorfn('ADONewConnection', 'ADONewConnection', -998,
3836
							 "could not load the database driver for '$db'",
3837
							 $db,false,$ignore);
3838
				} else
3839
					 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
3840
 
3841
				return $false;
3842
			}
3843
 
3844
			$cls = 'ADODB_'.$db;
3845
			if (!class_exists($cls)) {
3846
				adodb_backtrace();
3847
				return $false;
3848
			}
3849
 
3850
			$obj = new $cls();
3851
		}
3852
 
3853
		# constructor should not fail
3854
		if ($obj) {
3855
			if ($errorfn)  $obj->raiseErrorFn = $errorfn;
3856
			if (isset($dsna)) {
3857
				if (isset($dsna['port'])) $obj->port = $dsna['port'];
3858
				foreach($opt as $k => $v) {
3859
					switch(strtolower($k)) {
3860
					case 'new':
3861
										$nconnect = true; $persist = true; break;
3862
					case 'persist':
3863
					case 'persistent': 	$persist = $v; break;
3864
					case 'debug':		$obj->debug = (integer) $v; break;
3865
					#ibase
3866
					case 'role':		$obj->role = $v; break;
3867
					case 'dialect': 	$obj->dialect = (integer) $v; break;
3868
					case 'charset':		$obj->charset = $v; $obj->charSet=$v; break;
3869
					case 'buffers':		$obj->buffers = $v; break;
3870
					case 'fetchmode':   $obj->SetFetchMode($v); break;
3871
					#ado
3872
					case 'charpage':	$obj->charPage = $v; break;
3873
					#mysql, mysqli
3874
					case 'clientflags': $obj->clientFlags = $v; break;
3875
					#mysql, mysqli, postgres
3876
					case 'port': $obj->port = $v; break;
3877
					#mysqli
3878
					case 'socket': $obj->socket = $v; break;
3879
					#oci8
3880
					case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
3881
					}
3882
				}
3883
				if (empty($persist))
3884
					$ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3885
				else if (empty($nconnect))
3886
					$ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3887
				else
3888
					$ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
3889
 
3890
				if (!$ok) return $false;
3891
			}
3892
		}
3893
		return $obj;
3894
	}
3895
 
3896
 
3897
 
3898
	// $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
3899
	function _adodb_getdriver($provider,$drivername,$perf=false)
3900
	{
3901
		switch ($provider) {
3902
		case 'odbtp':   if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6); 
3903
		case 'odbc' :   if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5); 
3904
		case 'ado'  :   if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4);
3905
		case 'native':  break;
3906
		default:
3907
			return $provider;
3908
		}
3909
 
3910
		switch($drivername) {
3911
		case 'mysqlt':
3912
		case 'mysqli': 
3913
				$drivername='mysql'; 
3914
				break;
3915
		case 'postgres7':
3916
		case 'postgres8':
3917
				$drivername = 'postgres'; 
3918
				break;	
3919
		case 'firebird15': $drivername = 'firebird'; break;
3920
		case 'oracle': $drivername = 'oci8'; break;
3921
		case 'access': if ($perf) $drivername = ''; break;
3922
		case 'db2'   : break;
3923
		case 'sapdb' : break;
3924
		default:
3925
			$drivername = 'generic';
3926
			break;
3927
		}
3928
		return $drivername;
3929
	}
3930
 
3931
	function &NewPerfMonitor(&$conn)
3932
	{
3933
		$false = false;
3934
		$drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
3935
		if (!$drivername || $drivername == 'generic') return $false;
3936
		include_once(ADODB_DIR.'/adodb-perf.inc.php');
3937
		@include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
3938
		$class = "Perf_$drivername";
3939
		if (!class_exists($class)) return $false;
3940
		$perf = new $class($conn);
3941
 
3942
		return $perf;
3943
	}
3944
 
3945
	function &NewDataDictionary(&$conn,$drivername=false)
3946
	{
3947
		$false = false;
3948
		if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
3949
 
3950
		include_once(ADODB_DIR.'/adodb-lib.inc.php');
3951
		include_once(ADODB_DIR.'/adodb-datadict.inc.php');
3952
		$path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
3953
 
3954
		if (!file_exists($path)) {
3955
			ADOConnection::outp("Database driver '$path' not available");
3956
			return $false;
3957
		}
3958
		include_once($path);
3959
		$class = "ADODB2_$drivername";
3960
		$dict = new $class();
3961
		$dict->dataProvider = $conn->dataProvider;
3962
		$dict->connection = &$conn;
3963
		$dict->upperName = strtoupper($drivername);
3964
		$dict->quote = $conn->nameQuote;
3965
		if (!empty($conn->_connectionID))
3966
			$dict->serverInfo = $conn->ServerInfo();
3967
 
3968
		return $dict;
3969
	}
3970
 
3971
 
3972
 
3973
	/*
3974
		Perform a print_r, with pre tags for better formatting.
3975
	*/
3976
	function adodb_pr($var,$as_string=false)
3977
	{
3978
		if ($as_string) ob_start();
3979
 
3980
		if (isset($_SERVER['HTTP_USER_AGENT'])) { 
3981
			echo " <pre>\n";print_r($var);echo "</pre>\n";
3982
		} else
3983
			print_r($var);
3984
 
3985
		if ($as_string) {
3986
			$s = ob_get_contents();
3987
			ob_end_clean();
3988
			return $s;
3989
		}
3990
	}
3991
 
3992
	/*
3993
		Perform a stack-crawl and pretty print it.
3994
 
3995
		@param printOrArr  Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
3996
		@param levels Number of levels to display
3997
	*/
3998
	function adodb_backtrace($printOrArr=true,$levels=9999)
3999
	{
4000
		global $ADODB_INCLUDED_LIB;
4001
		if (empty($ADODB_INCLUDED_LIB)) include_once(ADODB_DIR.'/adodb-lib.inc.php');
4002
		return _adodb_backtrace($printOrArr,$levels);
4003
	}
4004
 
4005
 
4006
}
4007
?>