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