4416 lines
		
	
	
		
			122 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			4416 lines
		
	
	
		
			122 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php 
 | ||
| /*
 | ||
|  * Set tabs to 4 for best viewing.
 | ||
|  * 
 | ||
|  * Latest version is available at http://adodb.sourceforge.net
 | ||
|  * 
 | ||
|  * This is the main include file for ADOdb.
 | ||
|  * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
 | ||
|  *
 | ||
|  * The ADOdb files are formatted so that doxygen can be used to generate documentation.
 | ||
|  * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
 | ||
|  */
 | ||
| 
 | ||
| /**
 | ||
| 	\mainpage
 | ||
| 	
 | ||
| 	 @version V5.11 5 May 2010   (c) 2000-2010 John Lim (jlim#natsoft.com). All rights reserved.
 | ||
| 
 | ||
| 	Released under both BSD license and Lesser GPL library license. You can choose which license
 | ||
| 	you prefer.
 | ||
| 	
 | ||
| 	PHP's database access functions are not standardised. This creates a need for a database 
 | ||
| 	class library to hide the differences between the different database API's (encapsulate 
 | ||
| 	the differences) so we can easily switch databases.
 | ||
| 
 | ||
| 	We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
 | ||
| 	Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
 | ||
| 	ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
 | ||
| 	other databases via ODBC.
 | ||
| 
 | ||
| 	Latest Download at http://adodb.sourceforge.net/
 | ||
| 	  
 | ||
|  */
 | ||
|  
 | ||
|  if (!defined('_ADODB_LAYER')) {
 | ||
|  	define('_ADODB_LAYER',1);
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CONSTANT DEFINITIONS
 | ||
| 	//==============================================================================================	
 | ||
| 
 | ||
| 
 | ||
| 	/** 
 | ||
| 	 * Set ADODB_DIR to the directory where this file resides...
 | ||
| 	 * This constant was formerly called $ADODB_RootPath
 | ||
| 	 */
 | ||
| 	if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// GLOBAL VARIABLES
 | ||
| 	//==============================================================================================	
 | ||
| 
 | ||
| 	GLOBAL 
 | ||
| 		$ADODB_vers, 		// database version
 | ||
| 		$ADODB_COUNTRECS,	// count number of records returned - slows down query
 | ||
| 		$ADODB_CACHE_DIR,	// directory to cache recordsets
 | ||
| 		$ADODB_CACHE,
 | ||
| 		$ADODB_CACHE_CLASS,
 | ||
| 		$ADODB_EXTENSION,   // ADODB extension installed
 | ||
| 		$ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
 | ||
| 	 	$ADODB_FETCH_MODE,	// DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
 | ||
| 		$ADODB_GETONE_EOF,
 | ||
| 		$ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.	
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// GLOBAL SETUP
 | ||
| 	//==============================================================================================	
 | ||
| 	
 | ||
| 	$ADODB_EXTENSION = defined('ADODB_EXTENSION');
 | ||
| 	
 | ||
| 	//********************************************************//
 | ||
| 	/*
 | ||
| 	Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
 | ||
| 	Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
 | ||
| 
 | ||
|  		0 = ignore empty fields. All empty fields in array are ignored.
 | ||
| 		1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
 | ||
| 		2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
 | ||
| 		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.
 | ||
| 	*/
 | ||
|         define('ADODB_FORCE_IGNORE',0);
 | ||
|         define('ADODB_FORCE_NULL',1);
 | ||
|         define('ADODB_FORCE_EMPTY',2);
 | ||
|         define('ADODB_FORCE_VALUE',3);
 | ||
|     //********************************************************//
 | ||
| 
 | ||
| 
 | ||
| 	if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
 | ||
| 		
 | ||
| 		define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
 | ||
| 	
 | ||
| 	// allow [ ] @ ` " and . in table names
 | ||
| 		define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
 | ||
| 	
 | ||
| 	// prefetching used by oracle
 | ||
| 		if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
 | ||
| 	
 | ||
| 	
 | ||
| 	/*
 | ||
| 	Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
 | ||
| 	This currently works only with mssql, odbc, oci8po and ibase derived drivers.
 | ||
| 	
 | ||
|  		0 = assoc lowercase field names. $rs->fields['orderid']
 | ||
| 		1 = assoc uppercase field names. $rs->fields['ORDERID']
 | ||
| 		2 = use native-case field names. $rs->fields['OrderID']
 | ||
| 	*/
 | ||
| 	
 | ||
| 		define('ADODB_FETCH_DEFAULT',0);
 | ||
| 		define('ADODB_FETCH_NUM',1);
 | ||
| 		define('ADODB_FETCH_ASSOC',2);
 | ||
| 		define('ADODB_FETCH_BOTH',3);
 | ||
| 		
 | ||
| 		if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
 | ||
| 	
 | ||
| 		// PHP's version scheme makes converting to numbers difficult - workaround
 | ||
| 		$_adodb_ver = (float) PHP_VERSION;
 | ||
| 		if ($_adodb_ver >= 5.2) {
 | ||
| 			define('ADODB_PHPVER',0x5200);
 | ||
| 		} else if ($_adodb_ver >= 5.0) {
 | ||
| 			define('ADODB_PHPVER',0x5000);
 | ||
| 		} else 
 | ||
| 			die("PHP5 or later required. You are running ".PHP_VERSION);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	//if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 	Accepts $src and $dest arrays, replacing string $data
 | ||
| 	*/
 | ||
| 	function ADODB_str_replace($src, $dest, $data)
 | ||
| 	{
 | ||
| 		if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
 | ||
| 		
 | ||
| 		$s = reset($src);
 | ||
| 		$d = reset($dest);
 | ||
| 		while ($s !== false) {
 | ||
| 			$data = str_replace($s,$d,$data);
 | ||
| 			$s = next($src);
 | ||
| 			$d = next($dest);
 | ||
| 		}
 | ||
| 		return $data;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function ADODB_Setup()
 | ||
| 	{
 | ||
| 	GLOBAL 
 | ||
| 		$ADODB_vers, 		// database version
 | ||
| 		$ADODB_COUNTRECS,	// count number of records returned - slows down query
 | ||
| 		$ADODB_CACHE_DIR,	// directory to cache recordsets
 | ||
| 	 	$ADODB_FETCH_MODE,
 | ||
| 		$ADODB_CACHE,
 | ||
| 		$ADODB_CACHE_CLASS,
 | ||
| 		$ADODB_FORCE_TYPE,
 | ||
| 		$ADODB_GETONE_EOF,
 | ||
| 		$ADODB_QUOTE_FIELDNAMES;
 | ||
| 		
 | ||
| 		if (empty($ADODB_CACHE_CLASS)) $ADODB_CACHE_CLASS =  'ADODB_Cache_File' ;
 | ||
| 		$ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
 | ||
| 		$ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
 | ||
| 		$ADODB_GETONE_EOF = null;
 | ||
| 
 | ||
| 		if (!isset($ADODB_CACHE_DIR)) {
 | ||
| 			$ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
 | ||
| 		} else {
 | ||
| 			// do not accept url based paths, eg. http:/ or ftp:/
 | ||
| 			if (strpos($ADODB_CACHE_DIR,'://') !== false) 
 | ||
| 				die("Illegal path http:// or ftp://");
 | ||
| 		}
 | ||
| 		
 | ||
| 			
 | ||
| 		// Initialize random number generator for randomizing cache flushes
 | ||
| 		// -- note Since PHP 4.2.0, the seed  becomes optional and defaults to a random value if omitted.
 | ||
| 		 srand(((double)microtime())*1000000);
 | ||
| 		
 | ||
| 		/**
 | ||
| 		 * ADODB version as a string.
 | ||
| 		 */
 | ||
| 		$ADODB_vers = 'V5.11 5 May 2010  (c) 2000-2010 John Lim (jlim#natsoft.com). All rights reserved. Released BSD & LGPL.';
 | ||
| 	
 | ||
| 		/**
 | ||
| 		 * Determines whether recordset->RecordCount() is used. 
 | ||
| 		 * Set to false for highest performance -- RecordCount() will always return -1 then
 | ||
| 		 * for databases that provide "virtual" recordcounts...
 | ||
| 		 */
 | ||
| 		if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true; 
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
 | ||
| 	//==============================================================================================	
 | ||
| 	
 | ||
| 	ADODB_Setup();
 | ||
| 
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADOFieldObject
 | ||
| 	//==============================================================================================	
 | ||
| 	/**
 | ||
| 	 * Helper class for FetchFields -- holds info on a column
 | ||
| 	 */
 | ||
| 	class ADOFieldObject { 
 | ||
| 		var $name = '';
 | ||
| 		var $max_length=0;
 | ||
| 		var $type="";
 | ||
| /*
 | ||
| 		// additional fields by dannym... (danny_milo@yahoo.com)
 | ||
| 		var $not_null = false; 
 | ||
| 		// actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
 | ||
| 		// so we can as well make not_null standard (leaving it at "false" does not harm anyways)
 | ||
| 
 | ||
| 		var $has_default = false; // this one I have done only in mysql and postgres for now ... 
 | ||
| 			// others to come (dannym)
 | ||
| 		var $default_value; // default, if any, and supported. Check has_default first.
 | ||
| */
 | ||
| 	}
 | ||
| 	
 | ||
| 	// for transaction handling
 | ||
| 	
 | ||
| 	function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
 | ||
| 	{
 | ||
| 		//print "Errorno ($fn errno=$errno m=$errmsg) ";
 | ||
| 		$thisConnection->_transOK = false;
 | ||
| 		if ($thisConnection->_oldRaiseFn) {
 | ||
| 			$fn = $thisConnection->_oldRaiseFn;
 | ||
| 			$fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	//------------------
 | ||
| 	// class for caching
 | ||
| 	class ADODB_Cache_File {
 | ||
| 	
 | ||
| 		var $createdir = true; // requires creation of temp dirs
 | ||
| 		
 | ||
| 		function ADODB_Cache_File()
 | ||
| 		{
 | ||
| 		global $ADODB_INCLUDED_CSV;
 | ||
| 			if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
 | ||
| 		}
 | ||
| 		
 | ||
| 		// write serialised recordset to cache item/file
 | ||
| 		function writecache($filename, $contents,  $debug, $secs2cache)
 | ||
| 		{
 | ||
| 			return adodb_write_file($filename, $contents,$debug);
 | ||
| 		}
 | ||
| 		
 | ||
| 		// load serialised recordset and unserialise it
 | ||
| 		function &readcache($filename, &$err, $secs2cache, $rsClass)
 | ||
| 		{
 | ||
| 			$rs = csv2rs($filename,$err,$secs2cache,$rsClass);
 | ||
| 			return $rs;
 | ||
| 		}
 | ||
| 		
 | ||
| 		// flush all items in cache
 | ||
| 		function flushall($debug=false)
 | ||
| 		{
 | ||
| 		global $ADODB_CACHE_DIR;
 | ||
| 
 | ||
| 		$rez = false;
 | ||
| 		
 | ||
| 			if (strlen($ADODB_CACHE_DIR) > 1) {
 | ||
| 				$rez = $this->_dirFlush($ADODB_CACHE_DIR);
 | ||
| 	         	if ($debug) ADOConnection::outp( "flushall: $dir<br><pre>\n". $rez."</pre>");
 | ||
| 	   		}
 | ||
| 			return $rez;
 | ||
| 		}
 | ||
| 		
 | ||
| 		// flush one file in cache
 | ||
| 		function flushcache($f, $debug=false)
 | ||
| 		{
 | ||
| 			if (!@unlink($f)) {
 | ||
| 		   		if ($debug) ADOConnection::outp( "flushcache: failed for $f");
 | ||
| 			}
 | ||
| 		}
 | ||
| 		
 | ||
| 		function getdirname($hash)
 | ||
| 		{
 | ||
| 		global $ADODB_CACHE_DIR;
 | ||
| 			if (!isset($this->notSafeMode)) $this->notSafeMode = !ini_get('safe_mode');
 | ||
| 			return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
 | ||
| 		}
 | ||
| 		
 | ||
| 		// create temp directories
 | ||
| 		function createdir($hash, $debug)
 | ||
| 		{
 | ||
| 			$dir = $this->getdirname($hash);
 | ||
| 			if ($this->notSafeMode && !file_exists($dir)) {
 | ||
| 				$oldu = umask(0);
 | ||
| 				if (!@mkdir($dir,0771)) if(!is_dir($dir) && $debug) ADOConnection::outp("Cannot create $dir");
 | ||
| 				umask($oldu);
 | ||
| 			}
 | ||
| 		
 | ||
| 			return $dir;
 | ||
| 		}
 | ||
| 		
 | ||
| 		/**
 | ||
| 		* Private function to erase all of the files and subdirectories in a directory.
 | ||
| 		*
 | ||
| 		* Just specify the directory, and tell it if you want to delete the directory or just clear it out.
 | ||
| 		* Note: $kill_top_level is used internally in the function to flush subdirectories.
 | ||
| 		*/
 | ||
| 		function _dirFlush($dir, $kill_top_level = false) 
 | ||
| 		{
 | ||
| 		   if(!$dh = @opendir($dir)) return;
 | ||
| 		   
 | ||
| 		   while (($obj = readdir($dh))) {
 | ||
| 		   		if($obj=='.' || $obj=='..') continue;
 | ||
| 				$f = $dir.'/'.$obj;
 | ||
| 		
 | ||
| 				if (strpos($obj,'.cache')) @unlink($f);
 | ||
| 				if (is_dir($f)) $this->_dirFlush($f, true);
 | ||
| 		   }
 | ||
| 		   if ($kill_top_level === true) @rmdir($dir);
 | ||
| 		   return true;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADOConnection
 | ||
| 	//==============================================================================================	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Connection object. For connecting to databases, and executing queries.
 | ||
| 	 */ 
 | ||
| 	class ADOConnection {
 | ||
| 	//
 | ||
| 	// PUBLIC VARS 
 | ||
| 	//
 | ||
| 	var $dataProvider = 'native';
 | ||
| 	var $databaseType = '';		/// RDBMS currently in use, eg. odbc, mysql, mssql					
 | ||
| 	var $database = '';			/// Name of database to be used.	
 | ||
| 	var $host = ''; 			/// The hostname of the database server	
 | ||
| 	var $user = ''; 			/// The username which is used to connect to the database server. 
 | ||
| 	var $password = ''; 		/// Password for the username. For security, we no longer store it.
 | ||
| 	var $debug = false; 		/// if set to true will output sql statements
 | ||
| 	var $maxblobsize = 262144; 	/// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
 | ||
| 	var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase	
 | ||
| 	var $substr = 'substr';		/// substring operator
 | ||
| 	var $length = 'length';		/// string length ofperator
 | ||
| 	var $random = 'rand()';		/// random function
 | ||
| 	var $upperCase = 'upper';		/// uppercase function
 | ||
| 	var $fmtDate = "'Y-m-d'";	/// used by DBDate() as the default date format used by the database
 | ||
| 	var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
 | ||
| 	var $true = '1'; 			/// string that represents TRUE for a database
 | ||
| 	var $false = '0'; 			/// string that represents FALSE for a database
 | ||
| 	var $replaceQuote = "\\'"; 	/// string to use to replace quotes
 | ||
| 	var $nameQuote = '"';		/// string to use to quote identifiers and names
 | ||
| 	var $charSet=false; 		/// character set to use - only for interbase, postgres and oci8
 | ||
| 	var $metaDatabasesSQL = '';
 | ||
| 	var $metaTablesSQL = '';
 | ||
| 	var $uniqueOrderBy = false; /// All order by columns have to be unique
 | ||
| 	var $emptyDate = ' ';
 | ||
| 	var $emptyTimeStamp = ' ';
 | ||
| 	var $lastInsID = false;
 | ||
| 	//--
 | ||
| 	var $hasInsertID = false; 		/// supports autoincrement ID?
 | ||
| 	var $hasAffectedRows = false; 	/// supports affected rows for update/delete?
 | ||
| 	var $hasTop = false;			/// support mssql/access SELECT TOP 10 * FROM TABLE
 | ||
| 	var $hasLimit = false;			/// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
 | ||
| 	var $readOnly = false; 			/// this is a readonly database - used by phpLens
 | ||
| 	var $hasMoveFirst = false;  /// has ability to run MoveFirst(), scrolling backwards
 | ||
| 	var $hasGenID = false; 		/// can generate sequences using GenID();
 | ||
| 	var $hasTransactions = true; /// has transactions
 | ||
| 	//--
 | ||
| 	var $genID = 0; 			/// sequence id used by GenID();
 | ||
| 	var $raiseErrorFn = false; 	/// error function to call
 | ||
| 	var $isoDates = false; /// accepts dates in ISO format
 | ||
| 	var $cacheSecs = 3600; /// cache for 1 hour
 | ||
| 
 | ||
| 	// memcache
 | ||
| 	var $memCache = false; /// should we use memCache instead of caching in files
 | ||
| 	var $memCacheHost; /// memCache host
 | ||
| 	var $memCachePort = 11211; /// memCache port
 | ||
| 	var $memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
 | ||
| 
 | ||
| 	var $sysDate = false; /// name of function that returns the current date
 | ||
| 	var $sysTimeStamp = false; /// name of function that returns the current timestamp
 | ||
| 	var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
 | ||
| 	var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
 | ||
| 	
 | ||
| 	var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
 | ||
| 	var $numCacheHits = 0; 
 | ||
| 	var $numCacheMisses = 0;
 | ||
| 	var $pageExecuteCountRows = true;
 | ||
| 	var $uniqueSort = false; /// indicates that all fields in order by must be unique
 | ||
| 	var $leftOuter = false; /// operator to use for left outer join in WHERE clause
 | ||
| 	var $rightOuter = false; /// operator to use for right outer join in WHERE clause
 | ||
| 	var $ansiOuter = false; /// whether ansi outer join syntax supported
 | ||
| 	var $autoRollback = false; // autoRollback on PConnect().
 | ||
| 	var $poorAffectedRows = false; // affectedRows not working or unreliable
 | ||
| 	
 | ||
| 	var $fnExecute = false;
 | ||
| 	var $fnCacheExecute = false;
 | ||
| 	var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
 | ||
| 	var $rsPrefix = "ADORecordSet_";
 | ||
| 	
 | ||
| 	var $autoCommit = true; 	/// do not modify this yourself - actually private
 | ||
| 	var $transOff = 0; 			/// temporarily disable transactions
 | ||
| 	var $transCnt = 0; 			/// count of nested transactions
 | ||
| 	
 | ||
| 	var $fetchMode=false;
 | ||
| 	
 | ||
| 	var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
 | ||
| 	var $bulkBind = false; // enable 2D Execute array
 | ||
| 	 //
 | ||
| 	 // PRIVATE VARS
 | ||
| 	 //
 | ||
| 	var $_oldRaiseFn =  false;
 | ||
| 	var $_transOK = null;
 | ||
| 	var $_connectionID	= false;	/// The returned link identifier whenever a successful database connection is made.	
 | ||
| 	var $_errorMsg = false;		/// A variable which was used to keep the returned last error message.  The value will
 | ||
| 								/// then returned by the errorMsg() function	
 | ||
| 	var $_errorCode = false;	/// Last error code, not guaranteed to be used - only by oci8					
 | ||
| 	var $_queryID = false;		/// This variable keeps the last created result link identifier
 | ||
| 	
 | ||
| 	var $_isPersistentConnection = false;	/// A boolean variable to state whether its a persistent connection or normal connection.	*/
 | ||
| 	var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
 | ||
| 	var $_evalAll = false;
 | ||
| 	var $_affected = false;
 | ||
| 	var $_logsql = false;
 | ||
| 	var $_transmode = ''; // transaction mode
 | ||
| 	
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Constructor
 | ||
| 	 */
 | ||
| 	function ADOConnection()			
 | ||
| 	{
 | ||
| 		die('Virtual Class -- cannot instantiate');
 | ||
| 	}
 | ||
| 	
 | ||
| 	static function Version()
 | ||
| 	{
 | ||
| 	global $ADODB_vers;
 | ||
| 	
 | ||
| 		$ok = preg_match( '/^[Vv]([0-9\.]+)/', $ADODB_vers, $matches );
 | ||
| 		if (!$ok) return (float) substr($ADODB_vers,1);
 | ||
| 		else return $matches[1];
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 		Get server version info...
 | ||
| 		
 | ||
| 		@returns An array with 2 elements: $arr['string'] is the description string, 
 | ||
| 			and $arr[version] is the version (also a string).
 | ||
| 	*/
 | ||
| 	function ServerInfo()
 | ||
| 	{
 | ||
| 		return array('description' => '', 'version' => '');
 | ||
| 	}
 | ||
| 	
 | ||
| 	function IsConnected()
 | ||
| 	{
 | ||
|     	return !empty($this->_connectionID);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function _findvers($str)
 | ||
| 	{
 | ||
| 		if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
 | ||
| 		else return '';
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* All error messages go through this bottleneck function.
 | ||
| 	* You can define your own handler by defining the function name in ADODB_OUTP.
 | ||
| 	*/
 | ||
| 	static function outp($msg,$newline=true)
 | ||
| 	{
 | ||
| 	global $ADODB_FLUSH,$ADODB_OUTP;
 | ||
| 	
 | ||
| 		if (defined('ADODB_OUTP')) {
 | ||
| 			$fn = ADODB_OUTP;
 | ||
| 			$fn($msg,$newline);
 | ||
| 			return;
 | ||
| 		} else if (isset($ADODB_OUTP)) {
 | ||
| 			$fn = $ADODB_OUTP;
 | ||
| 			$fn($msg,$newline);
 | ||
| 			return;
 | ||
| 		}
 | ||
| 		
 | ||
| 		if ($newline) $msg .= "<br>\n";
 | ||
| 		
 | ||
| 		if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
 | ||
| 		else echo strip_tags($msg);
 | ||
| 	
 | ||
| 		
 | ||
| 		if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); //  do not flush if output buffering enabled - useless - thx to Jesse Mullan 
 | ||
| 		
 | ||
| 	}
 | ||
| 	
 | ||
| 	function Time()
 | ||
| 	{
 | ||
| 		$rs = $this->_Execute("select $this->sysTimeStamp");
 | ||
| 		if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
 | ||
| 		
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Connect to database
 | ||
| 	 *
 | ||
| 	 * @param [argHostname]		Host to connect to
 | ||
| 	 * @param [argUsername]		Userid to login
 | ||
| 	 * @param [argPassword]		Associated password
 | ||
| 	 * @param [argDatabaseName]	database
 | ||
| 	 * @param [forceNew]		force new connection
 | ||
| 	 *
 | ||
| 	 * @return true or false
 | ||
| 	 */	  
 | ||
| 	function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) 
 | ||
| 	{
 | ||
| 		if ($argHostname != "") $this->host = $argHostname;
 | ||
| 		if ($argUsername != "") $this->user = $argUsername;
 | ||
| 		if ($argPassword != "") $this->password = 'not stored'; // not stored for security reasons
 | ||
| 		if ($argDatabaseName != "") $this->database = $argDatabaseName;		
 | ||
| 		
 | ||
| 		$this->_isPersistentConnection = false;	
 | ||
| 			
 | ||
| 		if ($forceNew) {
 | ||
| 			if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) return true;
 | ||
| 		} else {
 | ||
| 			 if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) return true;
 | ||
| 		}
 | ||
| 		if (isset($rez)) {
 | ||
| 			$err = $this->ErrorMsg();
 | ||
| 			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
 | ||
| 			$ret = false;
 | ||
| 		} else {
 | ||
| 			$err = "Missing extension for ".$this->dataProvider;
 | ||
| 			$ret = 0;
 | ||
| 		}
 | ||
| 		if ($fn = $this->raiseErrorFn) 
 | ||
| 			$fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
 | ||
| 		
 | ||
| 		
 | ||
| 		$this->_connectionID = false;
 | ||
| 		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
 | ||
| 		return $ret;
 | ||
| 	}	
 | ||
| 	
 | ||
| 	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
 | ||
| 	{
 | ||
| 		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Always force a new connection to database - currently only works with oracle
 | ||
| 	 *
 | ||
| 	 * @param [argHostname]		Host to connect to
 | ||
| 	 * @param [argUsername]		Userid to login
 | ||
| 	 * @param [argPassword]		Associated password
 | ||
| 	 * @param [argDatabaseName]	database
 | ||
| 	 *
 | ||
| 	 * @return true or false
 | ||
| 	 */	  
 | ||
| 	function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") 
 | ||
| 	{
 | ||
| 		return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Establish persistent connect to database
 | ||
| 	 *
 | ||
| 	 * @param [argHostname]		Host to connect to
 | ||
| 	 * @param [argUsername]		Userid to login
 | ||
| 	 * @param [argPassword]		Associated password
 | ||
| 	 * @param [argDatabaseName]	database
 | ||
| 	 *
 | ||
| 	 * @return return true or false
 | ||
| 	 */	
 | ||
| 	function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
 | ||
| 	{
 | ||
| 		
 | ||
| 		if (defined('ADODB_NEVER_PERSIST')) 
 | ||
| 			return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
 | ||
| 		
 | ||
| 		if ($argHostname != "") $this->host = $argHostname;
 | ||
| 		if ($argUsername != "") $this->user = $argUsername;
 | ||
| 		if ($argPassword != "") $this->password = 'not stored';
 | ||
| 		if ($argDatabaseName != "") $this->database = $argDatabaseName;		
 | ||
| 			
 | ||
| 		$this->_isPersistentConnection = true;	
 | ||
| 		
 | ||
| 		if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) return true;
 | ||
| 		if (isset($rez)) {
 | ||
| 			$err = $this->ErrorMsg();
 | ||
| 			if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
 | ||
| 			$ret = false;
 | ||
| 		} else {
 | ||
| 			$err = "Missing extension for ".$this->dataProvider;
 | ||
| 			$ret = 0;
 | ||
| 		}
 | ||
| 		if ($fn = $this->raiseErrorFn) {
 | ||
| 			$fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
 | ||
| 		}
 | ||
| 		
 | ||
| 		$this->_connectionID = false;
 | ||
| 		if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
 | ||
| 		return $ret;
 | ||
| 	}
 | ||
| 
 | ||
| 	function outp_throw($msg,$src='WARN',$sql='')
 | ||
| 	{
 | ||
| 		if (defined('ADODB_ERROR_HANDLER') &&  ADODB_ERROR_HANDLER == 'adodb_throw') {
 | ||
| 			adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
 | ||
| 			return;
 | ||
| 		} 
 | ||
| 		ADOConnection::outp($msg);
 | ||
| 	}
 | ||
| 	
 | ||
| 	// create cache class. Code is backward compat with old memcache implementation
 | ||
| 	function _CreateCache()
 | ||
| 	{
 | ||
| 	global $ADODB_CACHE, $ADODB_CACHE_CLASS;
 | ||
| 	
 | ||
| 		if ($this->memCache) {
 | ||
| 		global $ADODB_INCLUDED_MEMCACHE;
 | ||
| 		
 | ||
| 			if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
 | ||
| 				$ADODB_CACHE = new ADODB_Cache_MemCache($this);
 | ||
| 		} else
 | ||
| 				$ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
 | ||
| 		
 | ||
| 	}
 | ||
| 	
 | ||
| 	// Format date column in sql string given an input format that understands Y M D
 | ||
| 	function SQLDate($fmt, $col=false)
 | ||
| 	{	
 | ||
| 		if (!$col) $col = $this->sysDate;
 | ||
| 		return $col; // child class implement
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Should prepare the sql statement and return the stmt resource.
 | ||
| 	 * For databases that do not support this, we return the $sql. To ensure
 | ||
| 	 * compatibility with databases that do not support prepare:
 | ||
| 	 *
 | ||
| 	 *   $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
 | ||
| 	 *   $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
 | ||
| 	 *   $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
 | ||
| 	 *
 | ||
| 	 * @param sql	SQL to send to database
 | ||
| 	 *
 | ||
| 	 * @return return FALSE, or the prepared statement, or the original sql if
 | ||
| 	 * 			if the database does not support prepare.
 | ||
| 	 *
 | ||
| 	 */	
 | ||
| 	function Prepare($sql)
 | ||
| 	{
 | ||
| 		return $sql;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Some databases, eg. mssql require a different function for preparing
 | ||
| 	 * stored procedures. So we cannot use Prepare().
 | ||
| 	 *
 | ||
| 	 * Should prepare the stored procedure  and return the stmt resource.
 | ||
| 	 * For databases that do not support this, we return the $sql. To ensure
 | ||
| 	 * compatibility with databases that do not support prepare:
 | ||
| 	 *
 | ||
| 	 * @param sql	SQL to send to database
 | ||
| 	 *
 | ||
| 	 * @return return FALSE, or the prepared statement, or the original sql if
 | ||
| 	 * 			if the database does not support prepare.
 | ||
| 	 *
 | ||
| 	 */	
 | ||
| 	function PrepareSP($sql,$param=true)
 | ||
| 	{
 | ||
| 		return $this->Prepare($sql,$param);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat
 | ||
| 	*/
 | ||
| 	function Quote($s)
 | ||
| 	{
 | ||
| 		return $this->qstr($s,false);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
 | ||
| 	*/
 | ||
| 	function QMagic($s)
 | ||
| 	{
 | ||
| 		return $this->qstr($s,get_magic_quotes_gpc());
 | ||
| 	}
 | ||
| 
 | ||
| 	function q(&$s)
 | ||
| 	{
 | ||
| 		#if (!empty($this->qNull)) if ($s == 'null') return $s;
 | ||
| 		$s = $this->qstr($s,false);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally. 
 | ||
| 	*/
 | ||
| 	function ErrorNative()
 | ||
| 	{
 | ||
| 		return $this->ErrorNo();
 | ||
| 	}
 | ||
| 
 | ||
| 	
 | ||
|    /**
 | ||
| 	* PEAR DB Compat - do not use internally. 
 | ||
| 	*/
 | ||
| 	function nextId($seq_name)
 | ||
| 	{
 | ||
| 		return $this->GenID($seq_name);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	*	 Lock a row, will escalate and lock the table if row locking not supported
 | ||
| 	*	will normally free the lock at the end of the transaction
 | ||
| 	*
 | ||
| 	*  @param $table	name of table to lock
 | ||
| 	*  @param $where	where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
 | ||
| 	*/
 | ||
| 	function RowLock($table,$where,$col='1 as adodbignore')
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CommitLock($table)
 | ||
| 	{
 | ||
| 		return $this->CommitTrans();
 | ||
| 	}
 | ||
| 	
 | ||
| 	function RollbackLock($table)
 | ||
| 	{
 | ||
| 		return $this->RollbackTrans();
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally. 
 | ||
| 	*
 | ||
| 	* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
 | ||
| 	* 	for easy porting :-)
 | ||
| 	*
 | ||
| 	* @param mode	The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
 | ||
| 	* @returns		The previous fetch mode
 | ||
| 	*/
 | ||
| 	function SetFetchMode($mode)
 | ||
| 	{	
 | ||
| 		$old = $this->fetchMode;
 | ||
| 		$this->fetchMode = $mode;
 | ||
| 		
 | ||
| 		if ($old === false) {
 | ||
| 		global $ADODB_FETCH_MODE;
 | ||
| 			return $ADODB_FETCH_MODE;
 | ||
| 		}
 | ||
| 		return $old;
 | ||
| 	}
 | ||
| 	
 | ||
| 
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally. 
 | ||
| 	*/
 | ||
| 	function Query($sql, $inputarr=false)
 | ||
| 	{
 | ||
| 		$rs = $this->Execute($sql, $inputarr);
 | ||
| 		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally
 | ||
| 	*/
 | ||
| 	function LimitQuery($sql, $offset, $count, $params=false)
 | ||
| 	{
 | ||
| 		$rs = $this->SelectLimit($sql, $count, $offset, $params); 
 | ||
| 		if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally
 | ||
| 	*/
 | ||
| 	function Disconnect()
 | ||
| 	{
 | ||
| 		return $this->Close();
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 		 Returns placeholder for parameter, eg.
 | ||
| 		 $DB->Param('a')
 | ||
| 		 
 | ||
| 		 will return ':a' for Oracle, and '?' for most other databases...
 | ||
| 		 
 | ||
| 		 For databases that require positioned params, eg $1, $2, $3 for postgresql,
 | ||
| 		 	pass in Param(false) before setting the first parameter.
 | ||
| 	*/
 | ||
| 	function Param($name,$type='C')
 | ||
| 	{
 | ||
| 		return '?';
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 		InParameter and OutParameter are self-documenting versions of Parameter().
 | ||
| 	*/
 | ||
| 	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
 | ||
| 	{
 | ||
| 		return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 	*/
 | ||
| 	function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
 | ||
| 	{
 | ||
| 		return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
 | ||
| 	
 | ||
| 	}
 | ||
| 
 | ||
| 	
 | ||
| 	/* 
 | ||
| 	Usage in oracle
 | ||
| 		$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
 | ||
| 		$db->Parameter($stmt,$id,'myid');
 | ||
| 		$db->Parameter($stmt,$group,'group',64);
 | ||
| 		$db->Execute();
 | ||
| 		
 | ||
| 		@param $stmt Statement returned by Prepare() or PrepareSP().
 | ||
| 		@param $var PHP variable to bind to
 | ||
| 		@param $name Name of stored procedure variable name to bind to.
 | ||
| 		@param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
 | ||
| 		@param [$maxLen] Holds an maximum length of the variable.
 | ||
| 		@param [$type] The data type of $var. Legal values depend on driver.
 | ||
| 
 | ||
| 	*/
 | ||
| 	function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function IgnoreErrors($saveErrs=false)
 | ||
| 	{
 | ||
| 		if (!$saveErrs) {
 | ||
| 			$saveErrs = array($this->raiseErrorFn,$this->_transOK);
 | ||
| 			$this->raiseErrorFn = false;
 | ||
| 			return $saveErrs;
 | ||
| 		} else {
 | ||
| 			$this->raiseErrorFn = $saveErrs[0];
 | ||
| 			$this->_transOK = $saveErrs[1];
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 		Improved method of initiating a transaction. Used together with CompleteTrans().
 | ||
| 		Advantages include:
 | ||
| 		
 | ||
| 		a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
 | ||
| 		   Only the outermost block is treated as a transaction.<br>
 | ||
| 		b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
 | ||
| 		c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
 | ||
| 		   are disabled, making it backward compatible.
 | ||
| 	*/
 | ||
| 	function StartTrans($errfn = 'ADODB_TransMonitor')
 | ||
| 	{
 | ||
| 		if ($this->transOff > 0) {
 | ||
| 			$this->transOff += 1;
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 		
 | ||
| 		$this->_oldRaiseFn = $this->raiseErrorFn;
 | ||
| 		$this->raiseErrorFn = $errfn;
 | ||
| 		$this->_transOK = true;
 | ||
| 		
 | ||
| 		if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
 | ||
| 		$ok = $this->BeginTrans();
 | ||
| 		$this->transOff = 1;
 | ||
| 		return $ok;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 		Used together with StartTrans() to end a transaction. Monitors connection
 | ||
| 		for sql errors, and will commit or rollback as appropriate.
 | ||
| 		
 | ||
| 		@autoComplete if true, monitor sql errors and commit and rollback as appropriate, 
 | ||
| 		and if set to false force rollback even if no SQL error detected.
 | ||
| 		@returns true on commit, false on rollback.
 | ||
| 	*/
 | ||
| 	function CompleteTrans($autoComplete = true)
 | ||
| 	{
 | ||
| 		if ($this->transOff > 1) {
 | ||
| 			$this->transOff -= 1;
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 		$this->raiseErrorFn = $this->_oldRaiseFn;
 | ||
| 		
 | ||
| 		$this->transOff = 0;
 | ||
| 		if ($this->_transOK && $autoComplete) {
 | ||
| 			if (!$this->CommitTrans()) {
 | ||
| 				$this->_transOK = false;
 | ||
| 				if ($this->debug) ADOConnection::outp("Smart Commit failed");
 | ||
| 			} else
 | ||
| 				if ($this->debug) ADOConnection::outp("Smart Commit occurred");
 | ||
| 		} else {
 | ||
| 			$this->_transOK = false;
 | ||
| 			$this->RollbackTrans();
 | ||
| 			if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
 | ||
| 		}
 | ||
| 		
 | ||
| 		return $this->_transOK;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 		At the end of a StartTrans/CompleteTrans block, perform a rollback.
 | ||
| 	*/
 | ||
| 	function FailTrans()
 | ||
| 	{
 | ||
| 		if ($this->debug) 
 | ||
| 			if ($this->transOff == 0) {
 | ||
| 				ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
 | ||
| 			} else {
 | ||
| 				ADOConnection::outp("FailTrans was called");
 | ||
| 				adodb_backtrace();
 | ||
| 			}
 | ||
| 		$this->_transOK = false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 		Check if transaction has failed, only for Smart Transactions.
 | ||
| 	*/
 | ||
| 	function HasFailedTrans()
 | ||
| 	{
 | ||
| 		if ($this->transOff > 0) return $this->_transOK == false;
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Execute SQL 
 | ||
| 	 *
 | ||
| 	 * @param sql		SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
 | ||
| 	 * @param [inputarr]	holds the input data to bind to. Null elements will be set to null.
 | ||
| 	 * @return 		RecordSet or false
 | ||
| 	 */
 | ||
| 	function Execute($sql,$inputarr=false) 
 | ||
| 	{
 | ||
| 		if ($this->fnExecute) {
 | ||
| 			$fn = $this->fnExecute;
 | ||
| 			$ret = $fn($this,$sql,$inputarr);
 | ||
| 			if (isset($ret)) return $ret;
 | ||
| 		}
 | ||
| 		if ($inputarr) {
 | ||
| 			if (!is_array($inputarr)) $inputarr = array($inputarr);
 | ||
| 			
 | ||
| 			$element0 = reset($inputarr);
 | ||
| 			# is_object check because oci8 descriptors can be passed in
 | ||
| 			$array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
 | ||
| 			//remove extra memory copy of input -mikefedyk
 | ||
| 			unset($element0);
 | ||
| 			
 | ||
| 			if (!is_array($sql) && !$this->_bindInputArray) {
 | ||
| 				$sqlarr = explode('?',$sql);
 | ||
| 				$nparams = sizeof($sqlarr)-1;
 | ||
| 				if (!$array_2d) $inputarr = array($inputarr);
 | ||
| 				foreach($inputarr as $arr) {
 | ||
| 					$sql = ''; $i = 0;
 | ||
| 					//Use each() instead of foreach to reduce memory usage -mikefedyk
 | ||
| 					while(list(, $v) = each($arr)) {
 | ||
| 						$sql .= $sqlarr[$i];
 | ||
| 						// from Ron Baldwin <ron.baldwin#sourceprose.com>
 | ||
| 						// Only quote string types	
 | ||
| 						$typ = gettype($v);
 | ||
| 						if ($typ == 'string')
 | ||
| 							//New memory copy of input created here -mikefedyk
 | ||
| 							$sql .= $this->qstr($v);
 | ||
| 						else if ($typ == 'double')
 | ||
| 							$sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
 | ||
| 						else if ($typ == 'boolean')
 | ||
| 							$sql .= $v ? $this->true : $this->false;
 | ||
| 						else if ($typ == 'object') {
 | ||
| 							if (method_exists($v, '__toString')) $sql .= $this->qstr($v->__toString());
 | ||
| 							else $sql .= $this->qstr((string) $v);
 | ||
| 						} else if ($v === null)
 | ||
| 							$sql .= 'NULL';
 | ||
| 						else
 | ||
| 							$sql .= $v;
 | ||
| 						$i += 1;
 | ||
| 						
 | ||
| 						if ($i == $nparams) break;
 | ||
| 					} // while
 | ||
| 					if (isset($sqlarr[$i])) {
 | ||
| 						$sql .= $sqlarr[$i];
 | ||
| 						if ($i+1 != sizeof($sqlarr)) $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
 | ||
| 					} else if ($i != sizeof($sqlarr))	
 | ||
| 						$this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
 | ||
| 		
 | ||
| 					$ret = $this->_Execute($sql);
 | ||
| 					if (!$ret) return $ret;
 | ||
| 				}	
 | ||
| 			} else {
 | ||
| 				if ($array_2d) {
 | ||
| 					if (is_string($sql))
 | ||
| 						$stmt = $this->Prepare($sql);
 | ||
| 					else
 | ||
| 						$stmt = $sql;
 | ||
| 						
 | ||
| 					foreach($inputarr as $arr) {
 | ||
| 						$ret = $this->_Execute($stmt,$arr);
 | ||
| 						if (!$ret) return $ret;
 | ||
| 					}
 | ||
| 				} else {
 | ||
| 					$ret = $this->_Execute($sql,$inputarr);
 | ||
| 				}
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			$ret = $this->_Execute($sql,false);
 | ||
| 		}
 | ||
| 
 | ||
| 		return $ret;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function _Execute($sql,$inputarr=false)
 | ||
| 	{
 | ||
| 		if ($this->debug) {
 | ||
| 			global $ADODB_INCLUDED_LIB;
 | ||
| 			if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 			$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
 | ||
| 		} else {
 | ||
| 			$this->_queryID = @$this->_query($sql,$inputarr);
 | ||
| 		}
 | ||
| 		
 | ||
| 		/************************
 | ||
| 		// OK, query executed
 | ||
| 		*************************/
 | ||
| 
 | ||
| 		if ($this->_queryID === false) { // error handling if query fails
 | ||
| 			if ($this->debug == 99) adodb_backtrace(true,5);	
 | ||
| 			$fn = $this->raiseErrorFn;
 | ||
| 			if ($fn) {
 | ||
| 				$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
 | ||
| 			} 
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		} 
 | ||
| 		
 | ||
| 		if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
 | ||
| 			$rsclass = $this->rsPrefix.'empty';
 | ||
| 			$rs = (class_exists($rsclass)) ? new $rsclass():  new ADORecordSet_empty();
 | ||
| 			
 | ||
| 			return $rs;
 | ||
| 		}
 | ||
| 		
 | ||
| 		// return real recordset from select statement
 | ||
| 		$rsclass = $this->rsPrefix.$this->databaseType;
 | ||
| 		$rs = new $rsclass($this->_queryID,$this->fetchMode);
 | ||
| 		$rs->connection = $this; // Pablo suggestion
 | ||
| 		$rs->Init();
 | ||
| 		if (is_array($sql)) $rs->sql = $sql[0];
 | ||
| 		else $rs->sql = $sql;
 | ||
| 		if ($rs->_numOfRows <= 0) {
 | ||
| 		global $ADODB_COUNTRECS;
 | ||
| 			if ($ADODB_COUNTRECS) {
 | ||
| 				if (!$rs->EOF) { 
 | ||
| 					$rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
 | ||
| 					$rs->_queryID = $this->_queryID;
 | ||
| 				} else
 | ||
| 					$rs->_numOfRows = 0;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 
 | ||
| 	function CreateSequence($seqname='adodbseq',$startID=1)
 | ||
| 	{
 | ||
| 		if (empty($this->_genSeqSQL)) return false;
 | ||
| 		return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
 | ||
| 	}
 | ||
| 
 | ||
| 	function DropSequence($seqname='adodbseq')
 | ||
| 	{
 | ||
| 		if (empty($this->_dropSeqSQL)) return false;
 | ||
| 		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Generates a sequence id and stores it in $this->genID;
 | ||
| 	 * GenID is only available if $this->hasGenID = true;
 | ||
| 	 *
 | ||
| 	 * @param seqname		name of sequence to use
 | ||
| 	 * @param startID		if sequence does not exist, start at this ID
 | ||
| 	 * @return		0 if not supported, otherwise a sequence id
 | ||
| 	 */
 | ||
| 	function GenID($seqname='adodbseq',$startID=1)
 | ||
| 	{
 | ||
| 		if (!$this->hasGenID) {
 | ||
| 			return 0; // formerly returns false pre 1.60
 | ||
| 		}
 | ||
| 		
 | ||
| 		$getnext = sprintf($this->_genIDSQL,$seqname);
 | ||
| 		
 | ||
| 		$holdtransOK = $this->_transOK;
 | ||
| 		
 | ||
| 		$save_handler = $this->raiseErrorFn;
 | ||
| 		$this->raiseErrorFn = '';
 | ||
| 		@($rs = $this->Execute($getnext));
 | ||
| 		$this->raiseErrorFn = $save_handler;
 | ||
| 		
 | ||
| 		if (!$rs) {
 | ||
| 			$this->_transOK = $holdtransOK; //if the status was ok before reset
 | ||
| 			$createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
 | ||
| 			$rs = $this->Execute($getnext);
 | ||
| 		}
 | ||
| 		if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
 | ||
| 		else $this->genID = 0; // false
 | ||
| 	
 | ||
| 		if ($rs) $rs->Close();
 | ||
| 
 | ||
| 		return $this->genID;
 | ||
| 	}	
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
 | ||
| 	 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
 | ||
| 	 * @return  the last inserted ID. Not all databases support this.
 | ||
| 	 */ 
 | ||
| 	function Insert_ID($table='',$column='')
 | ||
| 	{
 | ||
| 		if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
 | ||
| 		if ($this->hasInsertID) return $this->_insertid($table,$column);
 | ||
| 		if ($this->debug) {
 | ||
| 			ADOConnection::outp( '<p>Insert_ID error</p>');
 | ||
| 			adodb_backtrace();
 | ||
| 		}
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
 | ||
| 	 *
 | ||
| 	 * @return  the last inserted ID. All databases support this. But aware possible
 | ||
| 	 * problems in multiuser environments. Heavy test this before deploying.
 | ||
| 	 */ 
 | ||
| 	function PO_Insert_ID($table="", $id="") 
 | ||
| 	{
 | ||
| 	   if ($this->hasInsertID){
 | ||
| 		   return $this->Insert_ID($table,$id);
 | ||
| 	   } else {
 | ||
| 		   return $this->GetOne("SELECT MAX($id) FROM $table");
 | ||
| 	   }
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	* @return # rows affected by UPDATE/DELETE
 | ||
| 	*/ 
 | ||
| 	function Affected_Rows()
 | ||
| 	{
 | ||
| 		if ($this->hasAffectedRows) {
 | ||
| 			if ($this->fnExecute === 'adodb_log_sql') {
 | ||
| 				if ($this->_logsql && $this->_affected !== false) return $this->_affected;
 | ||
| 			}
 | ||
| 			$val = $this->_affectedrows();
 | ||
| 			return ($val < 0) ? false : $val;
 | ||
| 		}
 | ||
| 				  
 | ||
| 		if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @return  the last error message
 | ||
| 	 */
 | ||
| 	function ErrorMsg()
 | ||
| 	{
 | ||
| 		if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
 | ||
| 		else return '';
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @return the last error number. Normally 0 means no error.
 | ||
| 	 */
 | ||
| 	function ErrorNo() 
 | ||
| 	{
 | ||
| 		return ($this->_errorMsg) ? -1 : 0;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function MetaError($err=false)
 | ||
| 	{
 | ||
| 		include_once(ADODB_DIR."/adodb-error.inc.php");
 | ||
| 		if ($err === false) $err = $this->ErrorNo();
 | ||
| 		return adodb_error($this->dataProvider,$this->databaseType,$err);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function MetaErrorMsg($errno)
 | ||
| 	{
 | ||
| 		include_once(ADODB_DIR."/adodb-error.inc.php");
 | ||
| 		return adodb_errormsg($errno);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @returns an array with the primary key columns in it.
 | ||
| 	 */
 | ||
| 	function MetaPrimaryKeys($table, $owner=false)
 | ||
| 	{
 | ||
| 	// owner not used in base class - see oci8
 | ||
| 		$p = array();
 | ||
| 		$objs = $this->MetaColumns($table);
 | ||
| 		if ($objs) {
 | ||
| 			foreach($objs as $v) {
 | ||
| 				if (!empty($v->primary_key))
 | ||
| 					$p[] = $v->name;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		if (sizeof($p)) return $p;
 | ||
| 		if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
 | ||
| 			return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @returns assoc array where keys are tables, and values are foreign keys
 | ||
| 	 */
 | ||
| 	function MetaForeignKeys($table, $owner=false, $upper=false)
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	/**
 | ||
| 	 * Choose a database to connect to. Many databases do not support this.
 | ||
| 	 *
 | ||
| 	 * @param dbName 	is the name of the database to select
 | ||
| 	 * @return 		true or false
 | ||
| 	 */
 | ||
| 	function SelectDB($dbName) 
 | ||
| 	{return false;}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Will select, getting rows from $offset (1-based), for $nrows. 
 | ||
| 	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
 | ||
| 	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
 | ||
| 	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
 | ||
| 	* eg. 
 | ||
| 	*  SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
 | ||
| 	*  SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
 | ||
| 	*
 | ||
| 	* Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
 | ||
| 	* BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
 | ||
| 	*
 | ||
| 	* @param sql
 | ||
| 	* @param [offset]	is the row to start calculations from (1-based)
 | ||
| 	* @param [nrows]		is the number of rows to get
 | ||
| 	* @param [inputarr]	array of bind variables
 | ||
| 	* @param [secs2cache]		is a private parameter only used by jlim
 | ||
| 	* @return		the recordset ($rs->databaseType == 'array')
 | ||
|  	*/
 | ||
| 	function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
 | ||
| 	{
 | ||
| 		if ($this->hasTop && $nrows > 0) {
 | ||
| 		// suggested by Reinhard Balling. Access requires top after distinct 
 | ||
| 		 // Informix requires first before distinct - F Riosa
 | ||
| 			$ismssql = (strpos($this->databaseType,'mssql') !== false);
 | ||
| 			if ($ismssql) $isaccess = false;
 | ||
| 			else $isaccess = (strpos($this->databaseType,'access') !== false);
 | ||
| 			
 | ||
| 			if ($offset <= 	0) {
 | ||
| 				
 | ||
| 					// access includes ties in result
 | ||
| 					if ($isaccess) {
 | ||
| 						$sql = preg_replace(
 | ||
| 						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
 | ||
| 
 | ||
| 						if ($secs2cache != 0) {
 | ||
| 							$ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
 | ||
| 						} else {
 | ||
| 							$ret = $this->Execute($sql,$inputarr);
 | ||
| 						}
 | ||
| 						return $ret; // PHP5 fix
 | ||
| 					} else if ($ismssql){
 | ||
| 						$sql = preg_replace(
 | ||
| 						'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
 | ||
| 					} else {
 | ||
| 						$sql = preg_replace(
 | ||
| 						'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
 | ||
| 					}
 | ||
| 			} else {
 | ||
| 				$nn = $nrows + $offset;
 | ||
| 				if ($isaccess || $ismssql) {
 | ||
| 					$sql = preg_replace(
 | ||
| 					'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
 | ||
| 				} else {
 | ||
| 					$sql = preg_replace(
 | ||
| 					'/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 		
 | ||
| 		// if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer  rows
 | ||
| 		// 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
 | ||
| 		global $ADODB_COUNTRECS;
 | ||
| 		
 | ||
| 		$savec = $ADODB_COUNTRECS;
 | ||
| 		$ADODB_COUNTRECS = false;
 | ||
| 			
 | ||
| 
 | ||
| 		if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
 | ||
| 		else $rs = $this->Execute($sql,$inputarr);
 | ||
| 		
 | ||
| 		$ADODB_COUNTRECS = $savec;
 | ||
| 		if ($rs && !$rs->EOF) {
 | ||
| 			$rs = $this->_rs2rs($rs,$nrows,$offset);
 | ||
| 		}
 | ||
| 		//print_r($rs);
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Create serializable recordset. Breaks rs link to connection.
 | ||
| 	*
 | ||
| 	* @param rs			the recordset to serialize
 | ||
| 	*/
 | ||
| 	function SerializableRS(&$rs)
 | ||
| 	{
 | ||
| 		$rs2 = $this->_rs2rs($rs);
 | ||
| 		$ignore = false;
 | ||
| 		$rs2->connection = $ignore;
 | ||
| 		
 | ||
| 		return $rs2;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Convert database recordset to an array recordset
 | ||
| 	* input recordset's cursor should be at beginning, and
 | ||
| 	* old $rs will be closed.
 | ||
| 	*
 | ||
| 	* @param rs			the recordset to copy
 | ||
| 	* @param [nrows]  	number of rows to retrieve (optional)
 | ||
| 	* @param [offset] 	offset by number of rows (optional)
 | ||
| 	* @return 			the new recordset
 | ||
| 	*/
 | ||
| 	function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
 | ||
| 	{
 | ||
| 		if (! $rs) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$dbtype = $rs->databaseType;
 | ||
| 		if (!$dbtype) {
 | ||
| 			$rs = $rs;  // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
 | ||
| 			return $rs;
 | ||
| 		}
 | ||
| 		if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
 | ||
| 			$rs->MoveFirst();
 | ||
| 			$rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
 | ||
| 			return $rs;
 | ||
| 		}
 | ||
| 		$flds = array();
 | ||
| 		for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
 | ||
| 			$flds[] = $rs->FetchField($i);
 | ||
| 		}
 | ||
| 
 | ||
| 		$arr = $rs->GetArrayLimit($nrows,$offset);
 | ||
| 		//print_r($arr);
 | ||
| 		if ($close) $rs->Close();
 | ||
| 		
 | ||
| 		$arrayClass = $this->arrayClass;
 | ||
| 		
 | ||
| 		$rs2 = new $arrayClass();
 | ||
| 		$rs2->connection = $this;
 | ||
| 		$rs2->sql = $rs->sql;
 | ||
| 		$rs2->dataProvider = $this->dataProvider;
 | ||
| 		$rs2->InitArrayFields($arr,$flds);
 | ||
| 		$rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
 | ||
| 		return $rs2;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 	* Return all rows. Compat with PEAR DB
 | ||
| 	*/
 | ||
| 	function GetAll($sql, $inputarr=false)
 | ||
| 	{
 | ||
| 		$arr = $this->GetArray($sql,$inputarr);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
 | ||
| 	{
 | ||
| 		$rs = $this->Execute($sql, $inputarr);
 | ||
| 		if (!$rs) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$arr = $rs->GetAssoc($force_array,$first2cols);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
 | ||
| 	{
 | ||
| 		if (!is_numeric($secs2cache)) {
 | ||
| 			$first2cols = $force_array;
 | ||
| 			$force_array = $inputarr;
 | ||
| 		}
 | ||
| 		$rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
 | ||
| 		if (!$rs) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$arr = $rs->GetAssoc($force_array,$first2cols);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return first element of first row of sql statement. Recordset is disposed
 | ||
| 	* for you.
 | ||
| 	*
 | ||
| 	* @param sql			SQL statement
 | ||
| 	* @param [inputarr]		input bind array
 | ||
| 	*/
 | ||
| 	function GetOne($sql,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
 | ||
| 		$crecs = $ADODB_COUNTRECS;
 | ||
| 		$ADODB_COUNTRECS = false;
 | ||
| 		
 | ||
| 		$ret = false;
 | ||
| 		$rs = $this->Execute($sql,$inputarr);
 | ||
| 		if ($rs) {	
 | ||
| 			if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
 | ||
| 			else $ret = reset($rs->fields);
 | ||
| 			
 | ||
| 			$rs->Close();
 | ||
| 		}
 | ||
| 		$ADODB_COUNTRECS = $crecs;
 | ||
| 		return $ret;
 | ||
| 	}
 | ||
| 	
 | ||
| 	// $where should include 'WHERE fld=value'
 | ||
| 	function GetMedian($table, $field,$where = '')
 | ||
| 	{
 | ||
| 		$total = $this->GetOne("select count(*) from $table $where");
 | ||
| 		if (!$total) return false;
 | ||
| 	
 | ||
| 		$midrow = (integer) ($total/2);
 | ||
| 		$rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
 | ||
| 		if ($rs && !$rs->EOF) return reset($rs->fields);
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_GETONE_EOF;
 | ||
| 		$ret = false;
 | ||
| 		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
 | ||
| 		if ($rs) {
 | ||
| 			if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
 | ||
| 			else $ret = reset($rs->fields);
 | ||
| 			$rs->Close();
 | ||
| 		} 
 | ||
| 		
 | ||
| 		return $ret;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetCol($sql, $inputarr = false, $trim = false)
 | ||
| 	{
 | ||
| 	  	
 | ||
| 	  	$rs = $this->Execute($sql, $inputarr);
 | ||
| 	  	if ($rs) {
 | ||
| 			$rv = array();
 | ||
| 	   		if ($trim) {
 | ||
| 				while (!$rs->EOF) {
 | ||
| 					$rv[] = trim(reset($rs->fields));
 | ||
| 					$rs->MoveNext();
 | ||
| 		   		}
 | ||
| 			} else {
 | ||
| 				while (!$rs->EOF) {
 | ||
| 					$rv[] = reset($rs->fields);
 | ||
| 					$rs->MoveNext();
 | ||
| 		   		}
 | ||
| 			}
 | ||
| 	   		$rs->Close();
 | ||
| 	  	} else
 | ||
| 			$rv = false;
 | ||
| 	  	return $rv;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
 | ||
| 	{
 | ||
| 	  	$rs = $this->CacheExecute($secs, $sql, $inputarr);
 | ||
| 	  	if ($rs) {
 | ||
| 			$rv = array();
 | ||
| 			if ($trim) {
 | ||
| 				while (!$rs->EOF) {
 | ||
| 					$rv[] = trim(reset($rs->fields));
 | ||
| 					$rs->MoveNext();
 | ||
| 		   		}
 | ||
| 			} else {
 | ||
| 				while (!$rs->EOF) {
 | ||
| 					$rv[] = reset($rs->fields);
 | ||
| 					$rs->MoveNext();
 | ||
| 		   		}
 | ||
| 			}
 | ||
| 	   		$rs->Close();
 | ||
| 	  	} else
 | ||
| 			$rv = false;
 | ||
| 			
 | ||
| 	  	return $rv;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function Transpose(&$rs,$addfieldnames=true)
 | ||
| 	{
 | ||
| 		$rs2 = $this->_rs2rs($rs);
 | ||
| 		$false = false;
 | ||
| 		if (!$rs2) return $false;
 | ||
| 		
 | ||
| 		$rs2->_transpose($addfieldnames);
 | ||
| 		return $rs2;
 | ||
| 	}
 | ||
|  
 | ||
| 	/*
 | ||
| 		Calculate the offset of a date for a particular database and generate
 | ||
| 			appropriate SQL. Useful for calculating future/past dates and storing
 | ||
| 			in a database.
 | ||
| 			
 | ||
| 		If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
 | ||
| 	*/
 | ||
| 	function OffsetDate($dayFraction,$date=false)
 | ||
| 	{		
 | ||
| 		if (!$date) $date = $this->sysDate;
 | ||
| 		return  '('.$date.'+'.$dayFraction.')';
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	*
 | ||
| 	* @param sql			SQL statement
 | ||
| 	* @param [inputarr]		input bind array
 | ||
| 	*/
 | ||
| 	function GetArray($sql,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_COUNTRECS;
 | ||
| 		
 | ||
| 		$savec = $ADODB_COUNTRECS;
 | ||
| 		$ADODB_COUNTRECS = false;
 | ||
| 		$rs = $this->Execute($sql,$inputarr);
 | ||
| 		$ADODB_COUNTRECS = $savec;
 | ||
| 		if (!$rs) 
 | ||
| 			if (defined('ADODB_PEAR')) {
 | ||
| 				$cls = ADODB_PEAR_Error();
 | ||
| 				return $cls;
 | ||
| 			} else {
 | ||
| 				$false = false;
 | ||
| 				return $false;
 | ||
| 			}
 | ||
| 		$arr = $rs->GetArray();
 | ||
| 		$rs->Close();
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CacheGetAll($secs2cache,$sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 		$arr = $this->CacheGetArray($secs2cache,$sql,$inputarr);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CacheGetArray($secs2cache,$sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_COUNTRECS;
 | ||
| 		
 | ||
| 		$savec = $ADODB_COUNTRECS;
 | ||
| 		$ADODB_COUNTRECS = false;
 | ||
| 		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
 | ||
| 		$ADODB_COUNTRECS = $savec;
 | ||
| 		
 | ||
| 		if (!$rs) 
 | ||
| 			if (defined('ADODB_PEAR')) {
 | ||
| 				$cls = ADODB_PEAR_Error();
 | ||
| 				return $cls;
 | ||
| 			} else {
 | ||
| 				$false = false;
 | ||
| 				return $false;
 | ||
| 			}
 | ||
| 		$arr = $rs->GetArray();
 | ||
| 		$rs->Close();
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetRandRow($sql, $arr= false)
 | ||
| 	{
 | ||
| 		$rezarr = $this->GetAll($sql, $arr);
 | ||
| 		$sz = sizeof($rezarr);
 | ||
| 		return $rezarr[abs(rand()) % $sz];
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return one row of sql statement. Recordset is disposed for you.
 | ||
| 	*
 | ||
| 	* @param sql			SQL statement
 | ||
| 	* @param [inputarr]		input bind array
 | ||
| 	*/
 | ||
| 	function GetRow($sql,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_COUNTRECS;
 | ||
| 		$crecs = $ADODB_COUNTRECS;
 | ||
| 		$ADODB_COUNTRECS = false;
 | ||
| 		
 | ||
| 		$rs = $this->Execute($sql,$inputarr);
 | ||
| 		
 | ||
| 		$ADODB_COUNTRECS = $crecs;
 | ||
| 		if ($rs) {
 | ||
| 			if (!$rs->EOF) $arr = $rs->fields;
 | ||
| 			else $arr = array();
 | ||
| 			$rs->Close();
 | ||
| 			return $arr;
 | ||
| 		}
 | ||
| 		
 | ||
| 		$false = false;
 | ||
| 		return $false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function CacheGetRow($secs2cache,$sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 		$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
 | ||
| 		if ($rs) {
 | ||
| 			if (!$rs->EOF) $arr = $rs->fields;
 | ||
| 			else $arr = array();
 | ||
| 			
 | ||
| 			$rs->Close();
 | ||
| 			return $arr;
 | ||
| 		}
 | ||
| 		$false = false;
 | ||
| 		return $false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Insert or replace a single record. Note: this is not the same as MySQL's replace. 
 | ||
| 	* ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
 | ||
| 	* Also note that no table locking is done currently, so it is possible that the
 | ||
| 	* record be inserted twice by two programs...
 | ||
| 	*
 | ||
| 	* $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
 | ||
| 	*
 | ||
| 	* $table		table name
 | ||
| 	* $fieldArray	associative array of data (you must quote strings yourself).
 | ||
| 	* $keyCol		the primary key field name or if compound key, array of field names
 | ||
| 	* autoQuote		set to true to use a hueristic to quote strings. Works with nulls and numbers
 | ||
| 	*					but does not work with dates nor SQL functions.
 | ||
| 	* has_autoinc	the primary key is an auto-inc field, so skip in insert.
 | ||
| 	*
 | ||
| 	* Currently blob replace not supported
 | ||
| 	*
 | ||
| 	* returns 0 = fail, 1 = update, 2 = insert 
 | ||
| 	*/
 | ||
| 	
 | ||
| 	function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		
 | ||
| 		return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Will select, getting rows from $offset (1-based), for $nrows. 
 | ||
| 	* This simulates the MySQL "select * from table limit $offset,$nrows" , and
 | ||
| 	* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
 | ||
| 	* MySQL and PostgreSQL parameter ordering is the opposite of the other.
 | ||
| 	* eg. 
 | ||
| 	*  CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
 | ||
| 	*  CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
 | ||
| 	*
 | ||
| 	* BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
 | ||
| 	*
 | ||
| 	* @param [secs2cache]	seconds to cache data, set to 0 to force query. This is optional
 | ||
| 	* @param sql
 | ||
| 	* @param [offset]	is the row to start calculations from (1-based)
 | ||
| 	* @param [nrows]	is the number of rows to get
 | ||
| 	* @param [inputarr]	array of bind variables
 | ||
| 	* @return		the recordset ($rs->databaseType == 'array')
 | ||
|  	*/
 | ||
| 	function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
 | ||
| 	{	
 | ||
| 		if (!is_numeric($secs2cache)) {
 | ||
| 			if ($sql === false) $sql = -1;
 | ||
| 			if ($offset == -1) $offset = false;
 | ||
| 									  // sql,	nrows, offset,inputarr
 | ||
| 			$rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
 | ||
| 		} else {
 | ||
| 			if ($sql === false) $this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
 | ||
| 			$rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
 | ||
| 		}
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Flush cached recordsets that match a particular $sql statement. 
 | ||
| 	* If $sql == false, then we purge all files in the cache.
 | ||
|  	*/
 | ||
| 	
 | ||
| 	/**
 | ||
|    * Flush cached recordsets that match a particular $sql statement. 
 | ||
|    * If $sql == false, then we purge all files in the cache.
 | ||
|     */
 | ||
| 	function CacheFlush($sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_CACHE_DIR, $ADODB_CACHE;
 | ||
| 		
 | ||
| 		if (empty($ADODB_CACHE)) return false;
 | ||
| 		
 | ||
| 		if (!$sql) {
 | ||
| 			 $ADODB_CACHE->flushall($this->debug);
 | ||
| 	         return;
 | ||
| 	    }
 | ||
| 		
 | ||
| 		$f = $this->_gencachename($sql.serialize($inputarr),false);
 | ||
| 		return $ADODB_CACHE->flushcache($f, $this->debug);
 | ||
| 	}
 | ||
|    
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Private function to generate filename for caching.
 | ||
| 	* Filename is generated based on:
 | ||
| 	*
 | ||
| 	*  - sql statement
 | ||
| 	*  - database type (oci8, ibase, ifx, etc)
 | ||
| 	*  - database name
 | ||
| 	*  - userid
 | ||
| 	*  - setFetchMode (adodb 4.23)
 | ||
| 	*
 | ||
| 	* When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). 
 | ||
| 	* Assuming that we can have 50,000 files per directory with good performance, 
 | ||
| 	* then we can scale to 12.8 million unique cached recordsets. Wow!
 | ||
|  	*/
 | ||
| 	function _gencachename($sql,$createdir)
 | ||
| 	{
 | ||
| 	global $ADODB_CACHE, $ADODB_CACHE_DIR;
 | ||
| 		
 | ||
| 		if ($this->fetchMode === false) { 
 | ||
| 		global $ADODB_FETCH_MODE;
 | ||
| 			$mode = $ADODB_FETCH_MODE;
 | ||
| 		} else {
 | ||
| 			$mode = $this->fetchMode;
 | ||
| 		}
 | ||
| 		$m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
 | ||
| 		if (!$ADODB_CACHE->createdir) return $m;
 | ||
| 		if (!$createdir) $dir = $ADODB_CACHE->getdirname($m);
 | ||
| 		else $dir = $ADODB_CACHE->createdir($m, $this->debug);
 | ||
| 		
 | ||
| 		return $dir.'/adodb_'.$m.'.cache';
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Execute SQL, caching recordsets.
 | ||
| 	 *
 | ||
| 	 * @param [secs2cache]	seconds to cache data, set to 0 to force query. 
 | ||
| 	 *					  This is an optional parameter.
 | ||
| 	 * @param sql		SQL statement to execute
 | ||
| 	 * @param [inputarr]	holds the input data  to bind to
 | ||
| 	 * @return 		RecordSet or false
 | ||
| 	 */
 | ||
| 	function CacheExecute($secs2cache,$sql=false,$inputarr=false)
 | ||
| 	{
 | ||
| 	global $ADODB_CACHE;
 | ||
| 	
 | ||
| 		if (empty($ADODB_CACHE)) $this->_CreateCache();
 | ||
| 		
 | ||
| 		if (!is_numeric($secs2cache)) {
 | ||
| 			$inputarr = $sql;
 | ||
| 			$sql = $secs2cache;
 | ||
| 			$secs2cache = $this->cacheSecs;
 | ||
| 		}
 | ||
| 		
 | ||
| 		if (is_array($sql)) {
 | ||
| 			$sqlparam = $sql;
 | ||
| 			$sql = $sql[0];
 | ||
| 		} else
 | ||
| 			$sqlparam = $sql;
 | ||
| 			
 | ||
| 		
 | ||
| 		$md5file = $this->_gencachename($sql.serialize($inputarr),true);
 | ||
| 		$err = '';
 | ||
| 		
 | ||
| 		if ($secs2cache > 0){
 | ||
| 			$rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
 | ||
| 			$this->numCacheHits += 1;
 | ||
| 		} else {
 | ||
| 			$err='Timeout 1';
 | ||
| 			$rs = false;
 | ||
| 			$this->numCacheMisses += 1;
 | ||
| 		}
 | ||
| 		
 | ||
| 		if (!$rs) {
 | ||
| 		// no cached rs found
 | ||
| 			if ($this->debug) {
 | ||
| 				if (get_magic_quotes_runtime() && !$this->memCache) {
 | ||
| 					ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
 | ||
| 				}
 | ||
| 				if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
 | ||
| 			}
 | ||
| 			
 | ||
| 			$rs = $this->Execute($sqlparam,$inputarr);
 | ||
| 
 | ||
| 			if ($rs) {
 | ||
| 
 | ||
| 				$eof = $rs->EOF;
 | ||
| 				$rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
 | ||
| 				$rs->timeCreated = time(); // used by caching
 | ||
| 				$txt = _rs2serialize($rs,false,$sql); // serialize
 | ||
| 	
 | ||
| 				$ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
 | ||
| 				if (!$ok) {
 | ||
| 					if ($ok === false) {
 | ||
| 						$em = 'Cache write error';
 | ||
| 						$en = -32000;
 | ||
| 						
 | ||
| 						if ($fn = $this->raiseErrorFn) {
 | ||
| 							$fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
 | ||
| 						}
 | ||
| 					} else {
 | ||
| 						$em = 'Cache file locked warning';
 | ||
| 						$en = -32001;
 | ||
| 						// do not call error handling for just a warning
 | ||
| 					}
 | ||
| 					
 | ||
| 					if ($this->debug) ADOConnection::outp( " ".$em);
 | ||
| 				}
 | ||
| 				if ($rs->EOF && !$eof) {
 | ||
| 					$rs->MoveFirst();
 | ||
| 					//$rs = csv2rs($md5file,$err);		
 | ||
| 					$rs->connection = $this; // Pablo suggestion
 | ||
| 				}  
 | ||
| 				
 | ||
| 			} else if (!$this->memCache)
 | ||
| 				$ADODB_CACHE->flushcache($md5file);
 | ||
| 		} else {
 | ||
| 			$this->_errorMsg = '';
 | ||
| 			$this->_errorCode = 0;
 | ||
| 			
 | ||
| 			if ($this->fnCacheExecute) {
 | ||
| 				$fn = $this->fnCacheExecute;
 | ||
| 				$fn($this, $secs2cache, $sql, $inputarr);
 | ||
| 			}
 | ||
| 		// ok, set cached object found
 | ||
| 			$rs->connection = $this; // Pablo suggestion
 | ||
| 			if ($this->debug){ 			
 | ||
| 				if ($this->debug == 99) adodb_backtrace();
 | ||
| 				$inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
 | ||
| 				$ttl = $rs->timeCreated + $secs2cache - time();
 | ||
| 				$s = is_array($sql) ? $sql[0] : $sql;
 | ||
| 				if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
 | ||
| 				
 | ||
| 				ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/* 
 | ||
| 		Similar to PEAR DB's autoExecute(), except that 
 | ||
| 		$mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
 | ||
| 		If $mode == 'UPDATE', then $where is compulsory as a safety measure.
 | ||
| 		
 | ||
| 		$forceUpdate means that even if the data has not changed, perform update.
 | ||
| 	 */
 | ||
| 	function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false) 
 | ||
| 	{
 | ||
| 		$false = false;
 | ||
| 		$sql = 'SELECT * FROM '.$table;  
 | ||
| 		if ($where!==FALSE) $sql .= ' WHERE '.$where;
 | ||
| 		else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
 | ||
| 			$this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause','AutoExecute');
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$rs = $this->SelectLimit($sql,1);
 | ||
| 		if (!$rs) return $false; // table does not exist
 | ||
| 		$rs->tableName = $table;
 | ||
| 		$rs->sql = $sql;
 | ||
| 		
 | ||
| 		switch((string) $mode) {
 | ||
| 		case 'UPDATE':
 | ||
| 		case '2':
 | ||
| 			$sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
 | ||
| 			break;
 | ||
| 		case 'INSERT':
 | ||
| 		case '1':
 | ||
| 			$sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
 | ||
| 			break;
 | ||
| 		default:
 | ||
| 			$this->outp_throw("AutoExecute: Unknown mode=$mode",'AutoExecute');
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$ret = false;
 | ||
| 		if ($sql) $ret = $this->Execute($sql);
 | ||
| 		if ($ret) $ret = true;
 | ||
| 		return $ret;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Generates an Update Query based on an existing recordset.
 | ||
| 	 * $arrFields is an associative array of fields with the value
 | ||
| 	 * that should be assigned.
 | ||
| 	 *
 | ||
| 	 * Note: This function should only be used on a recordset
 | ||
| 	 *	   that is run against a single table and sql should only 
 | ||
| 	 *		 be a simple select stmt with no groupby/orderby/limit
 | ||
| 	 *
 | ||
| 	 * "Jonathan Younger" <jyounger@unilab.com>
 | ||
|   	 */
 | ||
| 	function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 
 | ||
|         //********************************************************//
 | ||
|         //This is here to maintain compatibility
 | ||
|         //with older adodb versions. Sets force type to force nulls if $forcenulls is set.
 | ||
| 		if (!isset($force)) {
 | ||
| 				global $ADODB_FORCE_TYPE;
 | ||
| 			    $force = $ADODB_FORCE_TYPE;
 | ||
| 		}
 | ||
| 		//********************************************************//
 | ||
| 
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Generates an Insert Query based on an existing recordset.
 | ||
| 	 * $arrFields is an associative array of fields with the value
 | ||
| 	 * that should be assigned.
 | ||
| 	 *
 | ||
| 	 * Note: This function should only be used on a recordset
 | ||
| 	 *	   that is run against a single table.
 | ||
|   	 */
 | ||
| 	function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
 | ||
| 	{	
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (!isset($force)) {
 | ||
| 			global $ADODB_FORCE_TYPE;
 | ||
| 			$force = $ADODB_FORCE_TYPE;
 | ||
| 			
 | ||
| 		}
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
 | ||
| 	}
 | ||
| 	
 | ||
| 
 | ||
| 	/**
 | ||
| 	* Update a blob column, given a where clause. There are more sophisticated
 | ||
| 	* blob handling functions that we could have implemented, but all require
 | ||
| 	* a very complex API. Instead we have chosen something that is extremely
 | ||
| 	* simple to understand and use. 
 | ||
| 	*
 | ||
| 	* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
 | ||
| 	*
 | ||
| 	* Usage to update a $blobvalue which has a primary key blob_id=1 into a 
 | ||
| 	* field blobtable.blobcolumn:
 | ||
| 	*
 | ||
| 	*	UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
 | ||
| 	*
 | ||
| 	* Insert example:
 | ||
| 	*
 | ||
| 	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
 | ||
| 	*	$conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
 | ||
| 	*/
 | ||
| 	
 | ||
| 	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
 | ||
| 	{
 | ||
| 		return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	* Usage:
 | ||
| 	*	UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
 | ||
| 	*	
 | ||
| 	*	$blobtype supports 'BLOB' and 'CLOB'
 | ||
| 	*
 | ||
| 	*	$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
 | ||
| 	*	$conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
 | ||
| 	*/
 | ||
| 	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
 | ||
| 	{
 | ||
| 		$fd = fopen($path,'rb');
 | ||
| 		if ($fd === false) return false;
 | ||
| 		$val = fread($fd,filesize($path));
 | ||
| 		fclose($fd);
 | ||
| 		return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function BlobDecode($blob)
 | ||
| 	{
 | ||
| 		return $blob;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function BlobEncode($blob)
 | ||
| 	{
 | ||
| 		return $blob;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function SetCharSet($charset)
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function IfNull( $field, $ifNull ) 
 | ||
| 	{
 | ||
| 		return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
 | ||
| 	}
 | ||
| 	
 | ||
| 	function LogSQL($enable=true)
 | ||
| 	{
 | ||
| 		include_once(ADODB_DIR.'/adodb-perf.inc.php');
 | ||
| 		
 | ||
| 		if ($enable) $this->fnExecute = 'adodb_log_sql';
 | ||
| 		else $this->fnExecute = false;
 | ||
| 		
 | ||
| 		$old = $this->_logsql;	
 | ||
| 		$this->_logsql = $enable;
 | ||
| 		if ($enable && !$old) $this->_affected = false;
 | ||
| 		return $old;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetCharSet()
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Usage:
 | ||
| 	*	UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
 | ||
| 	*
 | ||
| 	*	$conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
 | ||
| 	*	$conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
 | ||
| 	*/
 | ||
| 	function UpdateClob($table,$column,$val,$where)
 | ||
| 	{
 | ||
| 		return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
 | ||
| 	}
 | ||
| 	
 | ||
| 	// not the fastest implementation - quick and dirty - jlim
 | ||
| 	// for best performance, use the actual $rs->MetaType().
 | ||
| 	function MetaType($t,$len=-1,$fieldobj=false)
 | ||
| 	{
 | ||
| 		
 | ||
| 		if (empty($this->_metars)) {
 | ||
| 			$rsclass = $this->rsPrefix.$this->databaseType;
 | ||
| 			$this->_metars = new $rsclass(false,$this->fetchMode); 
 | ||
| 			$this->_metars->connection = $this;
 | ||
| 		}
 | ||
| 		return $this->_metars->MetaType($t,$len,$fieldobj);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	*  Change the SQL connection locale to a specified locale.
 | ||
| 	*  This is used to get the date formats written depending on the client locale.
 | ||
| 	*/
 | ||
| 	function SetDateLocale($locale = 'En')
 | ||
| 	{
 | ||
| 		$this->locale = $locale;
 | ||
| 		switch (strtoupper($locale))
 | ||
| 		{
 | ||
| 			case 'EN':
 | ||
| 				$this->fmtDate="'Y-m-d'";
 | ||
| 				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
 | ||
| 				break;
 | ||
| 				
 | ||
| 			case 'US':
 | ||
| 				$this->fmtDate = "'m-d-Y'";
 | ||
| 				$this->fmtTimeStamp = "'m-d-Y H:i:s'";
 | ||
| 				break;
 | ||
| 				
 | ||
| 			case 'PT_BR': 	
 | ||
| 			case 'NL':
 | ||
| 			case 'FR':
 | ||
| 			case 'RO':
 | ||
| 			case 'IT':
 | ||
| 				$this->fmtDate="'d-m-Y'";
 | ||
| 				$this->fmtTimeStamp = "'d-m-Y H:i:s'";
 | ||
| 				break;
 | ||
| 				
 | ||
| 			case 'GE':
 | ||
| 				$this->fmtDate="'d.m.Y'";
 | ||
| 				$this->fmtTimeStamp = "'d.m.Y H:i:s'";
 | ||
| 				break;
 | ||
| 				
 | ||
| 			default:
 | ||
| 				$this->fmtDate="'Y-m-d'";
 | ||
| 				$this->fmtTimeStamp = "'Y-m-d H:i:s'";
 | ||
| 				break;
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * GetActiveRecordsClass Performs an 'ALL' query 
 | ||
| 	 * 
 | ||
| 	 * @param mixed $class This string represents the class of the current active record
 | ||
| 	 * @param mixed $table Table used by the active record object
 | ||
| 	 * @param mixed $whereOrderBy Where, order, by clauses
 | ||
| 	 * @param mixed $bindarr 
 | ||
| 	 * @param mixed $primkeyArr 
 | ||
| 	 * @param array $extra Query extras: limit, offset...
 | ||
| 	 * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
 | ||
| 	 * @access public
 | ||
| 	 * @return void
 | ||
| 	 */
 | ||
| 	function GetActiveRecordsClass(
 | ||
| 			$class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
 | ||
| 			$extra=array(),
 | ||
| 			$relations=array())
 | ||
| 	{
 | ||
| 	global $_ADODB_ACTIVE_DBS;
 | ||
| 		## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
 | ||
| 		## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
 | ||
| 		if (!isset($_ADODB_ACTIVE_DBS))include_once(ADODB_DIR.'/adodb-active-record.inc.php');
 | ||
| 		return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
 | ||
| 	{
 | ||
| 		$arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Close Connection
 | ||
| 	 */
 | ||
| 	function Close()
 | ||
| 	{
 | ||
| 		$rez = $this->_close();
 | ||
| 		$this->_connectionID = false;
 | ||
| 		return $rez;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
 | ||
| 	 *
 | ||
| 	 * @return true if succeeded or false if database does not support transactions
 | ||
| 	 */
 | ||
| 	function BeginTrans() 
 | ||
| 	{
 | ||
| 		if ($this->debug) ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/* set transaction mode */
 | ||
| 	function SetTransactionMode( $transaction_mode ) 
 | ||
| 	{
 | ||
| 		$transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
 | ||
| 		$this->_transmode  = $transaction_mode;
 | ||
| 	}
 | ||
| /*
 | ||
| http://msdn2.microsoft.com/en-US/ms173763.aspx
 | ||
| http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
 | ||
| http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
 | ||
| http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
 | ||
| */
 | ||
| 	function MetaTransaction($mode,$db)
 | ||
| 	{
 | ||
| 		$mode = strtoupper($mode);
 | ||
| 		$mode = str_replace('ISOLATION LEVEL ','',$mode);
 | ||
| 		
 | ||
| 		switch($mode) {
 | ||
| 
 | ||
| 		case 'READ UNCOMMITTED':
 | ||
| 			switch($db) { 
 | ||
| 			case 'oci8':
 | ||
| 			case 'oracle':
 | ||
| 				return 'ISOLATION LEVEL READ COMMITTED';
 | ||
| 			default:
 | ||
| 				return 'ISOLATION LEVEL READ UNCOMMITTED';
 | ||
| 			}
 | ||
| 			break;
 | ||
| 					
 | ||
| 		case 'READ COMMITTED':
 | ||
| 				return 'ISOLATION LEVEL READ COMMITTED';
 | ||
| 			break;
 | ||
| 			
 | ||
| 		case 'REPEATABLE READ':
 | ||
| 			switch($db) {
 | ||
| 			case 'oci8':
 | ||
| 			case 'oracle':
 | ||
| 				return 'ISOLATION LEVEL SERIALIZABLE';
 | ||
| 			default:
 | ||
| 				return 'ISOLATION LEVEL REPEATABLE READ';
 | ||
| 			}
 | ||
| 			break;
 | ||
| 			
 | ||
| 		case 'SERIALIZABLE':
 | ||
| 				return 'ISOLATION LEVEL SERIALIZABLE';
 | ||
| 			break;
 | ||
| 			
 | ||
| 		default:
 | ||
| 			return $mode;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * If database does not support transactions, always return true as data always commited
 | ||
| 	 *
 | ||
| 	 * @param $ok  set to false to rollback transaction, true to commit
 | ||
| 	 *
 | ||
| 	 * @return true/false.
 | ||
| 	 */
 | ||
| 	function CommitTrans($ok=true) 
 | ||
| 	{ return true;}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * If database does not support transactions, rollbacks always fail, so return false
 | ||
| 	 *
 | ||
| 	 * @return true/false.
 | ||
| 	 */
 | ||
| 	function RollbackTrans() 
 | ||
| 	{ return false;}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * return the databases that the driver can connect to. 
 | ||
| 	 * Some databases will return an empty array.
 | ||
| 	 *
 | ||
| 	 * @return an array of database names.
 | ||
| 	 */
 | ||
| 		function MetaDatabases() 
 | ||
| 		{
 | ||
| 		global $ADODB_FETCH_MODE;
 | ||
| 		
 | ||
| 			if ($this->metaDatabasesSQL) {
 | ||
| 				$save = $ADODB_FETCH_MODE; 
 | ||
| 				$ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
 | ||
| 				
 | ||
| 				if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
 | ||
| 				
 | ||
| 				$arr = $this->GetCol($this->metaDatabasesSQL);
 | ||
| 				if (isset($savem)) $this->SetFetchMode($savem);
 | ||
| 				$ADODB_FETCH_MODE = $save; 
 | ||
| 			
 | ||
| 				return $arr;
 | ||
| 			}
 | ||
| 			
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 	
 | ||
| 		
 | ||
| 	/**
 | ||
| 	 * @param ttype can either be 'VIEW' or 'TABLE' or false. 
 | ||
| 	 * 		If false, both views and tables are returned.
 | ||
| 	 *		"VIEW" returns only views
 | ||
| 	 *		"TABLE" returns only tables
 | ||
| 	 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
 | ||
| 	 * @param mask  is the input mask - only supported by oci8 and postgresql
 | ||
| 	 *
 | ||
| 	 * @return  array of tables for current database.
 | ||
| 	 */ 
 | ||
| 	function MetaTables($ttype=false,$showSchema=false,$mask=false) 
 | ||
| 	{
 | ||
| 	global $ADODB_FETCH_MODE;
 | ||
| 	
 | ||
| 		
 | ||
| 		$false = false;
 | ||
| 		if ($mask) {
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		if ($this->metaTablesSQL) {
 | ||
| 			$save = $ADODB_FETCH_MODE; 
 | ||
| 			$ADODB_FETCH_MODE = ADODB_FETCH_NUM; 
 | ||
| 			
 | ||
| 			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
 | ||
| 			
 | ||
| 			$rs = $this->Execute($this->metaTablesSQL);
 | ||
| 			if (isset($savem)) $this->SetFetchMode($savem);
 | ||
| 			$ADODB_FETCH_MODE = $save; 
 | ||
| 			
 | ||
| 			if ($rs === false) return $false;
 | ||
| 			$arr = $rs->GetArray();
 | ||
| 			$arr2 = array();
 | ||
| 			
 | ||
| 			if ($hast = ($ttype && isset($arr[0][1]))) { 
 | ||
| 				$showt = strncmp($ttype,'T',1);
 | ||
| 			}
 | ||
| 			
 | ||
| 			for ($i=0; $i < sizeof($arr); $i++) {
 | ||
| 				if ($hast) {
 | ||
| 					if ($showt == 0) {
 | ||
| 						if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
 | ||
| 					} else {
 | ||
| 						if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
 | ||
| 					}
 | ||
| 				} else
 | ||
| 					$arr2[] = trim($arr[$i][0]);
 | ||
| 			}
 | ||
| 			$rs->Close();
 | ||
| 			return $arr2;
 | ||
| 		}
 | ||
| 		return $false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function _findschema(&$table,&$schema)
 | ||
| 	{
 | ||
| 		if (!$schema && ($at = strpos($table,'.')) !== false) {
 | ||
| 			$schema = substr($table,0,$at);
 | ||
| 			$table = substr($table,$at+1);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * List columns in a database as an array of ADOFieldObjects. 
 | ||
| 	 * See top of file for definition of object.
 | ||
| 	 *
 | ||
| 	 * @param $table	table name to query
 | ||
| 	 * @param $normalize	makes table name case-insensitive (required by some databases)
 | ||
| 	 * @schema is optional database schema to use - not supported by all databases.
 | ||
| 	 *
 | ||
| 	 * @return  array of ADOFieldObjects for current table.
 | ||
| 	 */
 | ||
| 	function MetaColumns($table,$normalize=true) 
 | ||
| 	{
 | ||
| 	global $ADODB_FETCH_MODE;
 | ||
| 		
 | ||
| 		$false = false;
 | ||
| 		
 | ||
| 		if (!empty($this->metaColumnsSQL)) {
 | ||
| 		
 | ||
| 			$schema = false;
 | ||
| 			$this->_findschema($table,$schema);
 | ||
| 		
 | ||
| 			$save = $ADODB_FETCH_MODE;
 | ||
| 			$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 | ||
| 			if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
 | ||
| 			$rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
 | ||
| 			if (isset($savem)) $this->SetFetchMode($savem);
 | ||
| 			$ADODB_FETCH_MODE = $save;
 | ||
| 			if ($rs === false || $rs->EOF) return $false;
 | ||
| 
 | ||
| 			$retarr = array();
 | ||
| 			while (!$rs->EOF) { //print_r($rs->fields);
 | ||
| 				$fld = new ADOFieldObject();
 | ||
| 				$fld->name = $rs->fields[0];
 | ||
| 				$fld->type = $rs->fields[1];
 | ||
| 				if (isset($rs->fields[3]) && $rs->fields[3]) {
 | ||
| 					if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
 | ||
| 					$fld->scale = $rs->fields[4];
 | ||
| 					if ($fld->scale>0) $fld->max_length += 1;
 | ||
| 				} else
 | ||
| 					$fld->max_length = $rs->fields[2];
 | ||
| 					
 | ||
| 				if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;	
 | ||
| 				else $retarr[strtoupper($fld->name)] = $fld;
 | ||
| 				$rs->MoveNext();
 | ||
| 			}
 | ||
| 			$rs->Close();
 | ||
| 			return $retarr;	
 | ||
| 		}
 | ||
| 		return $false;
 | ||
| 	}
 | ||
| 	
 | ||
|     /**
 | ||
|       * List indexes on a table as an array.
 | ||
|       * @param table  table name to query
 | ||
|       * @param primary true to only show primary keys. Not actually used for most databases
 | ||
| 	  *
 | ||
|       * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
 | ||
| 	  
 | ||
| 		 Array (
 | ||
| 		    [name_of_index] => Array
 | ||
| 		      (
 | ||
| 	          [unique] => true or false
 | ||
| 	          [columns] => Array
 | ||
| 	          (
 | ||
| 	          	[0] => firstname
 | ||
| 		      	[1] => lastname
 | ||
| 	          )
 | ||
| 		)		
 | ||
|       */
 | ||
|      function MetaIndexes($table, $primary = false, $owner = false)
 | ||
|      {
 | ||
| 	 		$false = false;
 | ||
|             return $false;
 | ||
|      }
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * List columns names in a table as an array. 
 | ||
| 	 * @param table	table name to query
 | ||
| 	 *
 | ||
| 	 * @return  array of column names for current table.
 | ||
| 	 */ 
 | ||
| 	function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) 
 | ||
| 	{
 | ||
| 		$objarr = $this->MetaColumns($table);
 | ||
| 		if (!is_array($objarr)) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$arr = array();
 | ||
| 		if ($numIndexes) {
 | ||
| 			$i = 0;
 | ||
| 			if ($useattnum) {
 | ||
| 				foreach($objarr as $v) 
 | ||
| 					$arr[$v->attnum] = $v->name;
 | ||
| 				
 | ||
| 			} else
 | ||
| 				foreach($objarr as $v) $arr[$i++] = $v->name;
 | ||
| 		} else
 | ||
| 			foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
 | ||
| 		
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 			
 | ||
| 	/**
 | ||
| 	 * Different SQL databases used different methods to combine strings together.
 | ||
| 	 * This function provides a wrapper. 
 | ||
| 	 * 
 | ||
| 	 * param s	variable number of string parameters
 | ||
| 	 *
 | ||
| 	 * Usage: $db->Concat($str1,$str2);
 | ||
| 	 * 
 | ||
| 	 * @return concatenated string
 | ||
| 	 */ 	 
 | ||
| 	function Concat()
 | ||
| 	{	
 | ||
| 		$arr = func_get_args();
 | ||
| 		return implode($this->concat_operator, $arr);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Converts a date "d" to a string that the database can understand.
 | ||
| 	 *
 | ||
| 	 * @param d	a date in Unix date time format.
 | ||
| 	 *
 | ||
| 	 * @return  date string in database date format
 | ||
| 	 */
 | ||
| 	function DBDate($d, $isfld=false)
 | ||
| 	{
 | ||
| 		if (empty($d) && $d !== 0) return 'null';
 | ||
| 		if ($isfld) return $d;
 | ||
| 		
 | ||
| 		if (is_object($d)) return $d->format($this->fmtDate);
 | ||
| 		
 | ||
| 		
 | ||
| 		if (is_string($d) && !is_numeric($d)) {
 | ||
| 			if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
 | ||
| 			if ($this->isoDates) return "'$d'";
 | ||
| 			$d = ADOConnection::UnixDate($d);
 | ||
| 		}
 | ||
| 
 | ||
| 		return adodb_date($this->fmtDate,$d);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function BindDate($d)
 | ||
| 	{
 | ||
| 		$d = $this->DBDate($d);
 | ||
| 		if (strncmp($d,"'",1)) return $d;
 | ||
| 		
 | ||
| 		return substr($d,1,strlen($d)-2);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function BindTimeStamp($d)
 | ||
| 	{
 | ||
| 		$d = $this->DBTimeStamp($d);
 | ||
| 		if (strncmp($d,"'",1)) return $d;
 | ||
| 		
 | ||
| 		return substr($d,1,strlen($d)-2);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Converts a timestamp "ts" to a string that the database can understand.
 | ||
| 	 *
 | ||
| 	 * @param ts	a timestamp in Unix date time format.
 | ||
| 	 *
 | ||
| 	 * @return  timestamp string in database timestamp format
 | ||
| 	 */
 | ||
| 	function DBTimeStamp($ts,$isfld=false)
 | ||
| 	{
 | ||
| 		if (empty($ts) && $ts !== 0) return 'null';
 | ||
| 		if ($isfld) return $ts;
 | ||
| 		if (is_object($ts)) return $ts->format($this->fmtTimeStamp);
 | ||
| 		
 | ||
| 		# strlen(14) allows YYYYMMDDHHMMSS format
 | ||
| 		if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) 
 | ||
| 			return adodb_date($this->fmtTimeStamp,$ts);
 | ||
| 		
 | ||
| 		if ($ts === 'null') return $ts;
 | ||
| 		if ($this->isoDates && strlen($ts) !== 14) return "'$ts'";
 | ||
| 		
 | ||
| 		$ts = ADOConnection::UnixTimeStamp($ts);
 | ||
| 		return adodb_date($this->fmtTimeStamp,$ts);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Also in ADORecordSet.
 | ||
| 	 * @param $v is a date string in YYYY-MM-DD format
 | ||
| 	 *
 | ||
| 	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
 | ||
| 	 */
 | ||
| 	static function UnixDate($v)
 | ||
| 	{
 | ||
| 		if (is_object($v)) {
 | ||
| 		// odbtp support
 | ||
| 		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
 | ||
| 			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
 | ||
| 		}
 | ||
| 	
 | ||
| 		if (is_numeric($v) && strlen($v) !== 8) return $v;
 | ||
| 		if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", 
 | ||
| 			($v), $rr)) return false;
 | ||
| 
 | ||
| 		if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
 | ||
| 		// h-m-s-MM-DD-YY
 | ||
| 		return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
 | ||
| 	}
 | ||
| 	
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Also in ADORecordSet.
 | ||
| 	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
 | ||
| 	 *
 | ||
| 	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
 | ||
| 	 */
 | ||
| 	static function UnixTimeStamp($v)
 | ||
| 	{
 | ||
| 		if (is_object($v)) {
 | ||
| 		// odbtp support
 | ||
| 		//( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
 | ||
| 			return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
 | ||
| 		}
 | ||
| 		
 | ||
| 		if (!preg_match( 
 | ||
| 			"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", 
 | ||
| 			($v), $rr)) return false;
 | ||
| 			
 | ||
| 		if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
 | ||
| 	
 | ||
| 		// h-m-s-MM-DD-YY
 | ||
| 		if (!isset($rr[5])) return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
 | ||
| 		return  @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Also in ADORecordSet.
 | ||
| 	 *
 | ||
| 	 * Format database date based on user defined format.
 | ||
| 	 *
 | ||
| 	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
 | ||
| 	 * @param fmt 	is the format to apply to it, using date()
 | ||
| 	 *
 | ||
| 	 * @return a date formated as user desires
 | ||
| 	 */
 | ||
| 	 
 | ||
| 	function UserDate($v,$fmt='Y-m-d',$gmt=false)
 | ||
| 	{
 | ||
| 		$tt = $this->UnixDate($v);
 | ||
| 
 | ||
| 		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
 | ||
| 		if (($tt === false || $tt == -1) && $v != false) return $v;
 | ||
| 		else if ($tt == 0) return $this->emptyDate;
 | ||
| 		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
 | ||
| 		}
 | ||
| 		
 | ||
| 		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
 | ||
| 	
 | ||
| 	}
 | ||
| 	
 | ||
| 		/**
 | ||
| 	 *
 | ||
| 	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
 | ||
| 	 * @param fmt 	is the format to apply to it, using date()
 | ||
| 	 *
 | ||
| 	 * @return a timestamp formated as user desires
 | ||
| 	 */
 | ||
| 	function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
 | ||
| 	{
 | ||
| 		if (!isset($v)) return $this->emptyTimeStamp;
 | ||
| 		# strlen(14) allows YYYYMMDDHHMMSS format
 | ||
| 		if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
 | ||
| 		$tt = $this->UnixTimeStamp($v);
 | ||
| 		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
 | ||
| 		if (($tt === false || $tt == -1) && $v != false) return $v;
 | ||
| 		if ($tt == 0) return $this->emptyTimeStamp;
 | ||
| 		return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
 | ||
| 	}
 | ||
| 	
 | ||
| 	function escape($s,$magic_quotes=false)
 | ||
| 	{
 | ||
| 		return $this->addq($s,$magic_quotes);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Quotes a string, without prefixing nor appending quotes. 
 | ||
| 	*/
 | ||
| 	function addq($s,$magic_quotes=false)
 | ||
| 	{
 | ||
| 		if (!$magic_quotes) {
 | ||
| 		
 | ||
| 			if ($this->replaceQuote[0] == '\\'){
 | ||
| 				// only since php 4.0.5
 | ||
| 				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
 | ||
| 				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
 | ||
| 			}
 | ||
| 			return  str_replace("'",$this->replaceQuote,$s);
 | ||
| 		}
 | ||
| 		
 | ||
| 		// undo magic quotes for "
 | ||
| 		$s = str_replace('\\"','"',$s);
 | ||
| 		
 | ||
| 		if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase'))  // ' already quoted, no need to change anything
 | ||
| 			return $s;
 | ||
| 		else {// change \' to '' for sybase/mssql
 | ||
| 			$s = str_replace('\\\\','\\',$s);
 | ||
| 			return str_replace("\\'",$this->replaceQuote,$s);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Correctly quotes a string so that all strings are escaped. We prefix and append
 | ||
| 	 * to the string single-quotes.
 | ||
| 	 * An example is  $db->qstr("Don't bother",magic_quotes_runtime());
 | ||
| 	 * 
 | ||
| 	 * @param s			the string to quote
 | ||
| 	 * @param [magic_quotes]	if $s is GET/POST var, set to get_magic_quotes_gpc().
 | ||
| 	 *				This undoes the stupidity of magic quotes for GPC.
 | ||
| 	 *
 | ||
| 	 * @return  quoted string to be sent back to database
 | ||
| 	 */
 | ||
| 	function qstr($s,$magic_quotes=false)
 | ||
| 	{	
 | ||
| 		if (!$magic_quotes) {
 | ||
| 		
 | ||
| 			if ($this->replaceQuote[0] == '\\'){
 | ||
| 				// only since php 4.0.5
 | ||
| 				$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
 | ||
| 				//$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
 | ||
| 			}
 | ||
| 			return  "'".str_replace("'",$this->replaceQuote,$s)."'";
 | ||
| 		}
 | ||
| 		
 | ||
| 		// undo magic quotes for "
 | ||
| 		$s = str_replace('\\"','"',$s);
 | ||
| 		
 | ||
| 		if ($this->replaceQuote == "\\'" || ini_get('magic_quotes_sybase'))  // ' already quoted, no need to change anything
 | ||
| 			return "'$s'";
 | ||
| 		else {// change \' to '' for sybase/mssql
 | ||
| 			$s = str_replace('\\\\','\\',$s);
 | ||
| 			return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Will select the supplied $page number from a recordset, given that it is paginated in pages of 
 | ||
| 	* $nrows rows per page. It also saves two boolean values saying if the given page is the first 
 | ||
| 	* and/or last one of the recordset. Added by Iv<49>n Oliva to provide recordset pagination.
 | ||
| 	*
 | ||
| 	* See readme.htm#ex8 for an example of usage.
 | ||
| 	*
 | ||
| 	* @param sql
 | ||
| 	* @param nrows		is the number of rows per page to get
 | ||
| 	* @param page		is the page number to get (1-based)
 | ||
| 	* @param [inputarr]	array of bind variables
 | ||
| 	* @param [secs2cache]		is a private parameter only used by jlim
 | ||
| 	* @return		the recordset ($rs->databaseType == 'array')
 | ||
| 	*
 | ||
| 	* NOTE: phpLens uses a different algorithm and does not use PageExecute().
 | ||
| 	*
 | ||
| 	*/
 | ||
| 	function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) 
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		if ($this->pageExecuteCountRows) $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
 | ||
| 		else $rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 	
 | ||
| 		
 | ||
| 	/**
 | ||
| 	* Will select the supplied $page number from a recordset, given that it is paginated in pages of 
 | ||
| 	* $nrows rows per page. It also saves two boolean values saying if the given page is the first 
 | ||
| 	* and/or last one of the recordset. Added by Iv<49>n Oliva to provide recordset pagination.
 | ||
| 	*
 | ||
| 	* @param secs2cache	seconds to cache data, set to 0 to force query
 | ||
| 	* @param sql
 | ||
| 	* @param nrows		is the number of rows per page to get
 | ||
| 	* @param page		is the page number to get (1-based)
 | ||
| 	* @param [inputarr]	array of bind variables
 | ||
| 	* @return		the recordset ($rs->databaseType == 'array')
 | ||
| 	*/
 | ||
| 	function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) 
 | ||
| 	{
 | ||
| 		/*switch($this->dataProvider) {
 | ||
| 		case 'postgres':
 | ||
| 		case 'mysql': 
 | ||
| 			break;
 | ||
| 		default: $secs2cache = 0; break;
 | ||
| 		}*/
 | ||
| 		$rs = $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
 | ||
| 		return $rs;
 | ||
| 	}
 | ||
| 
 | ||
| } // end class ADOConnection
 | ||
| 	
 | ||
| 	
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADOFetchObj
 | ||
| 	//==============================================================================================	
 | ||
| 		
 | ||
| 	/**
 | ||
| 	* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
 | ||
| 	*/
 | ||
| 	class ADOFetchObj {
 | ||
| 	};
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADORecordSet_empty
 | ||
| 	//==============================================================================================	
 | ||
| 	
 | ||
| 	class ADODB_Iterator_empty implements Iterator {
 | ||
| 	
 | ||
| 	    private $rs;
 | ||
| 	
 | ||
| 	    function __construct($rs) 
 | ||
| 		{
 | ||
| 	        $this->rs = $rs;
 | ||
| 	    }
 | ||
| 	    function rewind() 
 | ||
| 		{
 | ||
| 	    }
 | ||
| 	
 | ||
| 		function valid() 
 | ||
| 		{
 | ||
| 	        return !$this->rs->EOF;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function key() 
 | ||
| 		{
 | ||
| 	        return false;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function current() 
 | ||
| 		{
 | ||
| 	        return false;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function next() 
 | ||
| 		{
 | ||
| 	    }
 | ||
| 		
 | ||
| 		function __call($func, $params)
 | ||
| 		{
 | ||
| 			return call_user_func_array(array($this->rs, $func), $params);
 | ||
| 		}
 | ||
| 		
 | ||
| 		function hasMore()
 | ||
| 		{
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 	
 | ||
| 	}
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Lightweight recordset when there are no records to be returned
 | ||
| 	*/
 | ||
| 	class ADORecordSet_empty implements IteratorAggregate
 | ||
| 	{
 | ||
| 		var $dataProvider = 'empty';
 | ||
| 		var $databaseType = false;
 | ||
| 		var $EOF = true;
 | ||
| 		var $_numOfRows = 0;
 | ||
| 		var $fields = false;
 | ||
| 		var $connection = false;
 | ||
| 		function RowCount() {return 0;}
 | ||
| 		function RecordCount() {return 0;}
 | ||
| 		function PO_RecordCount(){return 0;}
 | ||
| 		function Close(){return true;}
 | ||
| 		function FetchRow() {return false;}
 | ||
| 		function FieldCount(){ return 0;}
 | ||
| 		function Init() {}
 | ||
| 		function getIterator() {return new ADODB_Iterator_empty($this);}
 | ||
| 	}
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// DATE AND TIME FUNCTIONS
 | ||
| 	//==============================================================================================	
 | ||
| 	if (!defined('ADODB_DATE_VERSION')) include(ADODB_DIR.'/adodb-time.inc.php');
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADORecordSet
 | ||
| 	//==============================================================================================	
 | ||
| 
 | ||
| 	class ADODB_Iterator implements Iterator {
 | ||
| 	
 | ||
| 	    private $rs;
 | ||
| 	
 | ||
| 	    function __construct($rs) 
 | ||
| 		{
 | ||
| 	        $this->rs = $rs;
 | ||
| 	    }
 | ||
| 	    function rewind() 
 | ||
| 		{
 | ||
| 	        $this->rs->MoveFirst();
 | ||
| 	    }
 | ||
| 	
 | ||
| 		function valid() 
 | ||
| 		{
 | ||
| 	        return !$this->rs->EOF;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function key() 
 | ||
| 		{
 | ||
| 	        return $this->rs->_currentRow;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function current() 
 | ||
| 		{
 | ||
| 	        return $this->rs->fields;
 | ||
| 	    }
 | ||
| 		
 | ||
| 	    function next() 
 | ||
| 		{
 | ||
| 	        $this->rs->MoveNext();
 | ||
| 	    }
 | ||
| 		
 | ||
| 		function __call($func, $params)
 | ||
| 		{
 | ||
| 			return call_user_func_array(array($this->rs, $func), $params);
 | ||
| 		}
 | ||
| 	
 | ||
| 		
 | ||
| 		function hasMore()
 | ||
| 		{
 | ||
| 			return !$this->rs->EOF;
 | ||
| 		}
 | ||
| 	
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|    /**
 | ||
| 	 * RecordSet class that represents the dataset returned by the database.
 | ||
| 	 * To keep memory overhead low, this class holds only the current row in memory.
 | ||
| 	 * No prefetching of data is done, so the RecordCount() can return -1 ( which
 | ||
| 	 * means recordcount not known).
 | ||
| 	 */
 | ||
| 	class ADORecordSet implements IteratorAggregate {
 | ||
| 	/*
 | ||
| 	 * public variables	
 | ||
| 	 */
 | ||
| 	var $dataProvider = "native";
 | ||
| 	var $fields = false; 	/// holds the current row data
 | ||
| 	var $blobSize = 100; 	/// any varchar/char field this size or greater is treated as a blob
 | ||
| 							/// in other words, we use a text area for editing.
 | ||
| 	var $canSeek = false; 	/// indicates that seek is supported
 | ||
| 	var $sql; 				/// sql text
 | ||
| 	var $EOF = false;		/// Indicates that the current record position is after the last record in a Recordset object. 
 | ||
| 	
 | ||
| 	var $emptyTimeStamp = ' '; /// what to display when $time==0
 | ||
| 	var $emptyDate = ' '; /// what to display when $time==0
 | ||
| 	var $debug = false;
 | ||
| 	var $timeCreated=0; 	/// datetime in Unix format rs created -- for cached recordsets
 | ||
| 
 | ||
| 	var $bind = false; 		/// used by Fields() to hold array - should be private?
 | ||
| 	var $fetchMode;			/// default fetch mode
 | ||
| 	var $connection = false; /// the parent connection
 | ||
| 	/*
 | ||
| 	 *	private variables	
 | ||
| 	 */
 | ||
| 	var $_numOfRows = -1;	/** number of rows, or -1 */
 | ||
| 	var $_numOfFields = -1;	/** number of fields in recordset */
 | ||
| 	var $_queryID = -1;		/** This variable keeps the result link identifier.	*/
 | ||
| 	var $_currentRow = -1;	/** This variable keeps the current row in the Recordset.	*/
 | ||
| 	var $_closed = false; 	/** has recordset been closed */
 | ||
| 	var $_inited = false; 	/** Init() should only be called once */
 | ||
| 	var $_obj; 				/** Used by FetchObj */
 | ||
| 	var $_names;			/** Used by FetchObj */
 | ||
| 	
 | ||
| 	var $_currentPage = -1;	/** Added by Iv<49>n Oliva to implement recordset pagination */
 | ||
| 	var $_atFirstPage = false;	/** Added by Iv<49>n Oliva to implement recordset pagination */
 | ||
| 	var $_atLastPage = false;	/** Added by Iv<49>n Oliva to implement recordset pagination */
 | ||
| 	var $_lastPageNo = -1; 
 | ||
| 	var $_maxRecordCount = 0;
 | ||
| 	var $datetime = false;
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Constructor
 | ||
| 	 *
 | ||
| 	 * @param queryID  	this is the queryID returned by ADOConnection->_query()
 | ||
| 	 *
 | ||
| 	 */
 | ||
| 	function ADORecordSet($queryID) 
 | ||
| 	{
 | ||
| 		$this->_queryID = $queryID;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function getIterator() 
 | ||
| 	{
 | ||
|         return new ADODB_Iterator($this);
 | ||
|     }
 | ||
| 	
 | ||
| 	/* this is experimental - i don't really know what to return... */
 | ||
| 	function __toString()
 | ||
| 	{
 | ||
| 		include_once(ADODB_DIR.'/toexport.inc.php');
 | ||
| 		return _adodb_export($this,',',',',false,true);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function Init()
 | ||
| 	{
 | ||
| 		if ($this->_inited) return;
 | ||
| 		$this->_inited = true;
 | ||
| 		if ($this->_queryID) @$this->_initrs();
 | ||
| 		else {
 | ||
| 			$this->_numOfRows = 0;
 | ||
| 			$this->_numOfFields = 0;
 | ||
| 		}
 | ||
| 		if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
 | ||
| 			
 | ||
| 			$this->_currentRow = 0;
 | ||
| 			if ($this->EOF = ($this->_fetch() === false)) {
 | ||
| 				$this->_numOfRows = 0; // _numOfRows could be -1
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			$this->EOF = true;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Generate a SELECT tag string from a recordset, and return the string.
 | ||
| 	 * If the recordset has 2 cols, we treat the 1st col as the containing 
 | ||
| 	 * the text to display to the user, and 2nd col as the return value. Default
 | ||
| 	 * strings are compared with the FIRST column.
 | ||
| 	 *
 | ||
| 	 * @param name  		name of SELECT tag
 | ||
| 	 * @param [defstr]		the value to hilite. Use an array for multiple hilites for listbox.
 | ||
| 	 * @param [blank1stItem]	true to leave the 1st item in list empty
 | ||
| 	 * @param [multiple]		true for listbox, false for popup
 | ||
| 	 * @param [size]		#rows to show for listbox. not used by popup
 | ||
| 	 * @param [selectAttr]		additional attributes to defined for SELECT tag.
 | ||
| 	 *				useful for holding javascript onChange='...' handlers.
 | ||
| 	 & @param [compareFields0]	when we have 2 cols in recordset, we compare the defstr with 
 | ||
| 	 *				column 0 (1st col) if this is true. This is not documented.
 | ||
| 	 *
 | ||
| 	 * @return HTML
 | ||
| 	 *
 | ||
| 	 * changes by glen.davies@cce.ac.nz to support multiple hilited items
 | ||
| 	 */
 | ||
| 	function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
 | ||
| 			$size=0, $selectAttr='',$compareFields0=true)
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
 | ||
| 			$size, $selectAttr,$compareFields0);
 | ||
| 	}
 | ||
| 	
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Generate a SELECT tag string from a recordset, and return the string.
 | ||
| 	 * If the recordset has 2 cols, we treat the 1st col as the containing 
 | ||
| 	 * the text to display to the user, and 2nd col as the return value. Default
 | ||
| 	 * strings are compared with the SECOND column.
 | ||
| 	 *
 | ||
| 	 */
 | ||
| 	function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')	
 | ||
| 	{
 | ||
| 		return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
 | ||
| 			$size, $selectAttr,false);
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 		Grouped Menu
 | ||
| 	*/
 | ||
| 	function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
 | ||
| 			$size=0, $selectAttr='')
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
 | ||
| 			$size, $selectAttr,false);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * return recordset as a 2-dimensional array.
 | ||
| 	 *
 | ||
| 	 * @param [nRows]  is the number of rows to return. -1 means every row.
 | ||
| 	 *
 | ||
| 	 * @return an array indexed by the rows (0-based) from the recordset
 | ||
| 	 */
 | ||
| 	function GetArray($nRows = -1) 
 | ||
| 	{
 | ||
| 	global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
 | ||
| 		$results = adodb_getall($this,$nRows);
 | ||
| 		return $results;
 | ||
| 	}
 | ||
| 		$results = array();
 | ||
| 		$cnt = 0;
 | ||
| 		while (!$this->EOF && $nRows != $cnt) {
 | ||
| 			$results[] = $this->fields;
 | ||
| 			$this->MoveNext();
 | ||
| 			$cnt++;
 | ||
| 		}
 | ||
| 		return $results;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetAll($nRows = -1)
 | ||
| 	{
 | ||
| 		$arr = $this->GetArray($nRows);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 	* Some databases allow multiple recordsets to be returned. This function
 | ||
| 	* will return true if there is a next recordset, or false if no more.
 | ||
| 	*/
 | ||
| 	function NextRecordSet()
 | ||
| 	{
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * return recordset as a 2-dimensional array. 
 | ||
| 	 * Helper function for ADOConnection->SelectLimit()
 | ||
| 	 *
 | ||
| 	 * @param offset	is the row to start calculations from (1-based)
 | ||
| 	 * @param [nrows]	is the number of rows to return
 | ||
| 	 *
 | ||
| 	 * @return an array indexed by the rows (0-based) from the recordset
 | ||
| 	 */
 | ||
| 	function GetArrayLimit($nrows,$offset=-1) 
 | ||
| 	{	
 | ||
| 		if ($offset <= 0) {
 | ||
| 			$arr = $this->GetArray($nrows);
 | ||
| 			return $arr;
 | ||
| 		} 
 | ||
| 		
 | ||
| 		$this->Move($offset);
 | ||
| 		
 | ||
| 		$results = array();
 | ||
| 		$cnt = 0;
 | ||
| 		while (!$this->EOF && $nrows != $cnt) {
 | ||
| 			$results[$cnt++] = $this->fields;
 | ||
| 			$this->MoveNext();
 | ||
| 		}
 | ||
| 		
 | ||
| 		return $results;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Synonym for GetArray() for compatibility with ADO.
 | ||
| 	 *
 | ||
| 	 * @param [nRows]  is the number of rows to return. -1 means every row.
 | ||
| 	 *
 | ||
| 	 * @return an array indexed by the rows (0-based) from the recordset
 | ||
| 	 */
 | ||
| 	function GetRows($nRows = -1) 
 | ||
| 	{
 | ||
| 		$arr = $this->GetArray($nRows);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns. 
 | ||
| 	 * The first column is treated as the key and is not included in the array. 
 | ||
| 	 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
 | ||
| 	 * $force_array == true.
 | ||
| 	 *
 | ||
| 	 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
 | ||
| 	 * 	array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
 | ||
| 	 * 	read the source.
 | ||
| 	 *
 | ||
| 	 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and 
 | ||
| 	 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
 | ||
| 	 *
 | ||
| 	 * @return an associative array indexed by the first column of the array, 
 | ||
| 	 * 	or false if the  data has less than 2 cols.
 | ||
| 	 */
 | ||
| 	function GetAssoc($force_array = false, $first2cols = false) 
 | ||
| 	{
 | ||
| 	global $ADODB_EXTENSION;
 | ||
| 	
 | ||
| 		$cols = $this->_numOfFields;
 | ||
| 		if ($cols < 2) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$numIndex = isset($this->fields[0]);
 | ||
| 		$results = array();
 | ||
| 		
 | ||
| 		if (!$first2cols && ($cols > 2 || $force_array)) {
 | ||
| 			if ($ADODB_EXTENSION) {
 | ||
| 				if ($numIndex) {
 | ||
| 					while (!$this->EOF) {
 | ||
| 						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
 | ||
| 						adodb_movenext($this);
 | ||
| 					}
 | ||
| 				} else {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// Fix for array_slice re-numbering numeric associative keys
 | ||
| 						$keys = array_slice(array_keys($this->fields), 1);
 | ||
| 						$sliced_array = array();
 | ||
| 
 | ||
| 						foreach($keys as $key) {
 | ||
| 							$sliced_array[$key] = $this->fields[$key];
 | ||
| 						}
 | ||
| 						
 | ||
| 						$results[trim(reset($this->fields))] = $sliced_array;
 | ||
| 						adodb_movenext($this);
 | ||
| 					}
 | ||
| 				}
 | ||
| 			} else {
 | ||
| 				if ($numIndex) {
 | ||
| 					while (!$this->EOF) {
 | ||
| 						$results[trim($this->fields[0])] = array_slice($this->fields, 1);
 | ||
| 						$this->MoveNext();
 | ||
| 					}
 | ||
| 				} else {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// Fix for array_slice re-numbering numeric associative keys
 | ||
| 						$keys = array_slice(array_keys($this->fields), 1);
 | ||
| 						$sliced_array = array();
 | ||
| 
 | ||
| 						foreach($keys as $key) {
 | ||
| 							$sliced_array[$key] = $this->fields[$key];
 | ||
| 						}
 | ||
| 						
 | ||
| 						$results[trim(reset($this->fields))] = $sliced_array;
 | ||
| 						$this->MoveNext();
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			if ($ADODB_EXTENSION) {
 | ||
| 				// return scalar values
 | ||
| 				if ($numIndex) {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
 | ||
| 						$results[trim(($this->fields[0]))] = $this->fields[1];
 | ||
| 						adodb_movenext($this);
 | ||
| 					}
 | ||
| 				} else {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
 | ||
| 						$v1 = trim(reset($this->fields));
 | ||
| 						$v2 = ''.next($this->fields); 
 | ||
| 						$results[$v1] = $v2;
 | ||
| 						adodb_movenext($this);
 | ||
| 					}
 | ||
| 				}
 | ||
| 			} else {
 | ||
| 				if ($numIndex) {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
 | ||
| 						$results[trim(($this->fields[0]))] = $this->fields[1];
 | ||
| 						$this->MoveNext();
 | ||
| 					}
 | ||
| 				} else {
 | ||
| 					while (!$this->EOF) {
 | ||
| 					// some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
 | ||
| 						$v1 = trim(reset($this->fields));
 | ||
| 						$v2 = ''.next($this->fields); 
 | ||
| 						$results[$v1] = $v2;
 | ||
| 						$this->MoveNext();
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 		
 | ||
| 		$ref = $results; # workaround accelerator incompat with PHP 4.4 :(
 | ||
| 		return $ref; 
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 *
 | ||
| 	 * @param v  	is the character timestamp in YYYY-MM-DD hh:mm:ss format
 | ||
| 	 * @param fmt 	is the format to apply to it, using date()
 | ||
| 	 *
 | ||
| 	 * @return a timestamp formated as user desires
 | ||
| 	 */
 | ||
| 	function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
 | ||
| 	{
 | ||
| 		if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
 | ||
| 		$tt = $this->UnixTimeStamp($v);
 | ||
| 		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
 | ||
| 		if (($tt === false || $tt == -1) && $v != false) return $v;
 | ||
| 		if ($tt === 0) return $this->emptyTimeStamp;
 | ||
| 		return adodb_date($fmt,$tt);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @param v  	is the character date in YYYY-MM-DD format, returned by database
 | ||
| 	 * @param fmt 	is the format to apply to it, using date()
 | ||
| 	 *
 | ||
| 	 * @return a date formated as user desires
 | ||
| 	 */
 | ||
| 	function UserDate($v,$fmt='Y-m-d')
 | ||
| 	{
 | ||
| 		$tt = $this->UnixDate($v);
 | ||
| 		// $tt == -1 if pre TIMESTAMP_FIRST_YEAR
 | ||
| 		if (($tt === false || $tt == -1) && $v != false) return $v;
 | ||
| 		else if ($tt == 0) return $this->emptyDate;
 | ||
| 		else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
 | ||
| 		}
 | ||
| 		return adodb_date($fmt,$tt);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @param $v is a date string in YYYY-MM-DD format
 | ||
| 	 *
 | ||
| 	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
 | ||
| 	 */
 | ||
| 	static function UnixDate($v)
 | ||
| 	{
 | ||
| 		return ADOConnection::UnixDate($v);
 | ||
| 	}
 | ||
| 	
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
 | ||
| 	 *
 | ||
| 	 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
 | ||
| 	 */
 | ||
| 	static function UnixTimeStamp($v)
 | ||
| 	{
 | ||
| 		return ADOConnection::UnixTimeStamp($v);
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB Compat - do not use internally
 | ||
| 	*/
 | ||
| 	function Free()
 | ||
| 	{
 | ||
| 		return $this->Close();
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB compat, number of rows
 | ||
| 	*/
 | ||
| 	function NumRows()
 | ||
| 	{
 | ||
| 		return $this->_numOfRows;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* PEAR DB compat, number of cols
 | ||
| 	*/
 | ||
| 	function NumCols()
 | ||
| 	{
 | ||
| 		return $this->_numOfFields;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Fetch a row, returning false if no more rows. 
 | ||
| 	* This is PEAR DB compat mode.
 | ||
| 	*
 | ||
| 	* @return false or array containing the current record
 | ||
| 	*/
 | ||
| 	function FetchRow()
 | ||
| 	{
 | ||
| 		if ($this->EOF) {
 | ||
| 			$false = false;
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		$arr = $this->fields;
 | ||
| 		$this->_currentRow++;
 | ||
| 		if (!$this->_fetch()) $this->EOF = true;
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Fetch a row, returning PEAR_Error if no more rows. 
 | ||
| 	* This is PEAR DB compat mode.
 | ||
| 	*
 | ||
| 	* @return DB_OK or error object
 | ||
| 	*/
 | ||
| 	function FetchInto(&$arr)
 | ||
| 	{
 | ||
| 		if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
 | ||
| 		$arr = $this->fields;
 | ||
| 		$this->MoveNext();
 | ||
| 		return 1; // DB_OK
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Move to the first row in the recordset. Many databases do NOT support this.
 | ||
| 	 *
 | ||
| 	 * @return true or false
 | ||
| 	 */
 | ||
| 	function MoveFirst() 
 | ||
| 	{
 | ||
| 		if ($this->_currentRow == 0) return true;
 | ||
| 		return $this->Move(0);			
 | ||
| 	}			
 | ||
| 
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Move to the last row in the recordset. 
 | ||
| 	 *
 | ||
| 	 * @return true or false
 | ||
| 	 */
 | ||
| 	function MoveLast() 
 | ||
| 	{
 | ||
| 		if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
 | ||
| 		if ($this->EOF) return false;
 | ||
| 		while (!$this->EOF) {
 | ||
| 			$f = $this->fields;
 | ||
| 			$this->MoveNext();
 | ||
| 		}
 | ||
| 		$this->fields = $f;
 | ||
| 		$this->EOF = false;
 | ||
| 		return true;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Move to next record in the recordset.
 | ||
| 	 *
 | ||
| 	 * @return true if there still rows available, or false if there are no more rows (EOF).
 | ||
| 	 */
 | ||
| 	function MoveNext() 
 | ||
| 	{
 | ||
| 		if (!$this->EOF) {
 | ||
| 			$this->_currentRow++;
 | ||
| 			if ($this->_fetch()) return true;
 | ||
| 		}
 | ||
| 		$this->EOF = true;
 | ||
| 		/* -- tested error handling when scrolling cursor -- seems useless.
 | ||
| 		$conn = $this->connection;
 | ||
| 		if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
 | ||
| 			$fn = $conn->raiseErrorFn;
 | ||
| 			$fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
 | ||
| 		}
 | ||
| 		*/
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Random access to a specific row in the recordset. Some databases do not support
 | ||
| 	 * access to previous rows in the databases (no scrolling backwards).
 | ||
| 	 *
 | ||
| 	 * @param rowNumber is the row to move to (0-based)
 | ||
| 	 *
 | ||
| 	 * @return true if there still rows available, or false if there are no more rows (EOF).
 | ||
| 	 */
 | ||
| 	function Move($rowNumber = 0) 
 | ||
| 	{
 | ||
| 		$this->EOF = false;
 | ||
| 		if ($rowNumber == $this->_currentRow) return true;
 | ||
| 		if ($rowNumber >= $this->_numOfRows)
 | ||
| 	   		if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
 | ||
|   				
 | ||
| 		if ($this->canSeek) { 
 | ||
| 	
 | ||
| 			if ($this->_seek($rowNumber)) {
 | ||
| 				$this->_currentRow = $rowNumber;
 | ||
| 				if ($this->_fetch()) {
 | ||
| 					return true;
 | ||
| 				}
 | ||
| 			} else {
 | ||
| 				$this->EOF = true;
 | ||
| 				return false;
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			if ($rowNumber < $this->_currentRow) return false;
 | ||
| 			global $ADODB_EXTENSION;
 | ||
| 			if ($ADODB_EXTENSION) {
 | ||
| 				while (!$this->EOF && $this->_currentRow < $rowNumber) {
 | ||
| 					adodb_movenext($this);
 | ||
| 				}
 | ||
| 			} else {
 | ||
| 			
 | ||
| 				while (! $this->EOF && $this->_currentRow < $rowNumber) {
 | ||
| 					$this->_currentRow++;
 | ||
| 					
 | ||
| 					if (!$this->_fetch()) $this->EOF = true;
 | ||
| 				}
 | ||
| 			}
 | ||
| 			return !($this->EOF);
 | ||
| 		}
 | ||
| 		
 | ||
| 		$this->fields = false;	
 | ||
| 		$this->EOF = true;
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 	
 | ||
| 		
 | ||
| 	/**
 | ||
| 	 * Get the value of a field in the current row by column name.
 | ||
| 	 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
 | ||
| 	 * 
 | ||
| 	 * @param colname  is the field to access
 | ||
| 	 *
 | ||
| 	 * @return the value of $colname column
 | ||
| 	 */
 | ||
| 	function Fields($colname)
 | ||
| 	{
 | ||
| 		return $this->fields[$colname];
 | ||
| 	}
 | ||
| 	
 | ||
| 	function GetAssocKeys($upper=true)
 | ||
| 	{
 | ||
| 		$this->bind = array();
 | ||
| 		for ($i=0; $i < $this->_numOfFields; $i++) {
 | ||
| 			$o = $this->FetchField($i);
 | ||
| 			if ($upper === 2) $this->bind[$o->name] = $i;
 | ||
| 			else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
|   /**
 | ||
|    * Use associative array to get fields array for databases that do not support
 | ||
|    * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
 | ||
|    *
 | ||
|    * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
 | ||
|    * before you execute your SQL statement, and access $rs->fields['col'] directly.
 | ||
|    *
 | ||
|    * $upper  0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
 | ||
|    */
 | ||
| 	function GetRowAssoc($upper=1)
 | ||
| 	{
 | ||
| 		$record = array();
 | ||
| 	 //	if (!$this->fields) return $record;
 | ||
| 		
 | ||
| 	   	if (!$this->bind) {
 | ||
| 			$this->GetAssocKeys($upper);
 | ||
| 		}
 | ||
| 		
 | ||
| 		foreach($this->bind as $k => $v) {
 | ||
| 			$record[$k] = $this->fields[$v];
 | ||
| 		}
 | ||
| 
 | ||
| 		return $record;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Clean up recordset
 | ||
| 	 *
 | ||
| 	 * @return true or false
 | ||
| 	 */
 | ||
| 	function Close() 
 | ||
| 	{
 | ||
| 		// free connection object - this seems to globally free the object
 | ||
| 		// and not merely the reference, so don't do this...
 | ||
| 		// $this->connection = false; 
 | ||
| 		if (!$this->_closed) {
 | ||
| 			$this->_closed = true;
 | ||
| 			return $this->_close();		
 | ||
| 		} else
 | ||
| 			return true;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * synonyms RecordCount and RowCount	
 | ||
| 	 *
 | ||
| 	 * @return the number of rows or -1 if this is not supported
 | ||
| 	 */
 | ||
| 	function RecordCount() {return $this->_numOfRows;}
 | ||
| 	
 | ||
| 	
 | ||
| 	/*
 | ||
| 	* If we are using PageExecute(), this will return the maximum possible rows
 | ||
| 	* that can be returned when paging a recordset.
 | ||
| 	*/
 | ||
| 	function MaxRecordCount()
 | ||
| 	{
 | ||
| 		return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * synonyms RecordCount and RowCount	
 | ||
| 	 *
 | ||
| 	 * @return the number of rows or -1 if this is not supported
 | ||
| 	 */
 | ||
| 	function RowCount() {return $this->_numOfRows;} 
 | ||
| 	
 | ||
| 
 | ||
| 	 /**
 | ||
| 	 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
 | ||
| 	 *
 | ||
| 	 * @return  the number of records from a previous SELECT. All databases support this.
 | ||
| 	 *
 | ||
| 	 * But aware possible problems in multiuser environments. For better speed the table
 | ||
| 	 * must be indexed by the condition. Heavy test this before deploying.
 | ||
| 	 */ 
 | ||
| 	function PO_RecordCount($table="", $condition="") {
 | ||
| 		
 | ||
| 		$lnumrows = $this->_numOfRows;
 | ||
| 		// the database doesn't support native recordcount, so we do a workaround
 | ||
| 		if ($lnumrows == -1 && $this->connection) {
 | ||
| 			IF ($table) {
 | ||
| 				if ($condition) $condition = " WHERE " . $condition; 
 | ||
| 				$resultrows = $this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
 | ||
| 				if ($resultrows) $lnumrows = reset($resultrows->fields);
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return $lnumrows;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
 | ||
| 	 */
 | ||
| 	function CurrentRow() {return $this->_currentRow;}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * synonym for CurrentRow -- for ADO compat
 | ||
| 	 *
 | ||
| 	 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
 | ||
| 	 */
 | ||
| 	function AbsolutePosition() {return $this->_currentRow;}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * @return the number of columns in the recordset. Some databases will set this to 0
 | ||
| 	 * if no records are returned, others will return the number of columns in the query.
 | ||
| 	 */
 | ||
| 	function FieldCount() {return $this->_numOfFields;}   
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the ADOFieldObject of a specific column.
 | ||
| 	 *
 | ||
| 	 * @param fieldoffset	is the column position to access(0-based).
 | ||
| 	 *
 | ||
| 	 * @return the ADOFieldObject for that column, or false.
 | ||
| 	 */
 | ||
| 	function FetchField($fieldoffset = -1) 
 | ||
| 	{
 | ||
| 		// must be defined by child class
 | ||
| 		
 | ||
| 		$false = false;
 | ||
| 		return $false;
 | ||
| 	}	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Get the ADOFieldObjects of all columns in an array.
 | ||
| 	 *
 | ||
| 	 */
 | ||
| 	function FieldTypesArray()
 | ||
| 	{
 | ||
| 		$arr = array();
 | ||
| 		for ($i=0, $max=$this->_numOfFields; $i < $max; $i++) 
 | ||
| 			$arr[] = $this->FetchField($i);
 | ||
| 		return $arr;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return the fields array of the current row as an object for convenience.
 | ||
| 	* The default case is lowercase field names.
 | ||
| 	*
 | ||
| 	* @return the object with the properties set to the fields of the current row
 | ||
| 	*/
 | ||
| 	function FetchObj()
 | ||
| 	{
 | ||
| 		$o = $this->FetchObject(false);
 | ||
| 		return $o;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return the fields array of the current row as an object for convenience.
 | ||
| 	* The default case is uppercase.
 | ||
| 	* 
 | ||
| 	* @param $isupper to set the object property names to uppercase
 | ||
| 	*
 | ||
| 	* @return the object with the properties set to the fields of the current row
 | ||
| 	*/
 | ||
| 	function FetchObject($isupper=true)
 | ||
| 	{
 | ||
| 		if (empty($this->_obj)) {
 | ||
| 			$this->_obj = new ADOFetchObj();
 | ||
| 			$this->_names = array();
 | ||
| 			for ($i=0; $i <$this->_numOfFields; $i++) {
 | ||
| 				$f = $this->FetchField($i);
 | ||
| 				$this->_names[] = $f->name;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		$i = 0;
 | ||
| 		if (PHP_VERSION >= 5) $o = clone($this->_obj);
 | ||
| 		else $o = $this->_obj;
 | ||
| 	
 | ||
| 		for ($i=0; $i <$this->_numOfFields; $i++) {
 | ||
| 			$name = $this->_names[$i];
 | ||
| 			if ($isupper) $n = strtoupper($name);
 | ||
| 			else $n = $name;
 | ||
| 			
 | ||
| 			$o->$n = $this->Fields($name);
 | ||
| 		}
 | ||
| 		return $o;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return the fields array of the current row as an object for convenience.
 | ||
| 	* The default is lower-case field names.
 | ||
| 	* 
 | ||
| 	* @return the object with the properties set to the fields of the current row,
 | ||
| 	* 	or false if EOF
 | ||
| 	*
 | ||
| 	* Fixed bug reported by tim@orotech.net
 | ||
| 	*/
 | ||
| 	function FetchNextObj()
 | ||
| 	{
 | ||
| 		$o = $this->FetchNextObject(false);
 | ||
| 		return $o;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	* Return the fields array of the current row as an object for convenience. 
 | ||
| 	* The default is upper case field names.
 | ||
| 	* 
 | ||
| 	* @param $isupper to set the object property names to uppercase
 | ||
| 	*
 | ||
| 	* @return the object with the properties set to the fields of the current row,
 | ||
| 	* 	or false if EOF
 | ||
| 	*
 | ||
| 	* Fixed bug reported by tim@orotech.net
 | ||
| 	*/
 | ||
| 	function FetchNextObject($isupper=true)
 | ||
| 	{
 | ||
| 		$o = false;
 | ||
| 		if ($this->_numOfRows != 0 && !$this->EOF) {
 | ||
| 			$o = $this->FetchObject($isupper);	
 | ||
| 			$this->_currentRow++;
 | ||
| 			if ($this->_fetch()) return $o;
 | ||
| 		}
 | ||
| 		$this->EOF = true;
 | ||
| 		return $o;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Get the metatype of the column. This is used for formatting. This is because
 | ||
| 	 * many databases use different names for the same type, so we transform the original
 | ||
| 	 * type to our standardised version which uses 1 character codes:
 | ||
| 	 *
 | ||
| 	 * @param t  is the type passed in. Normally is ADOFieldObject->type.
 | ||
| 	 * @param len is the maximum length of that field. This is because we treat character
 | ||
| 	 * 	fields bigger than a certain size as a 'B' (blob).
 | ||
| 	 * @param fieldobj is the field object returned by the database driver. Can hold
 | ||
| 	 *	additional info (eg. primary_key for mysql).
 | ||
| 	 * 
 | ||
| 	 * @return the general type of the data: 
 | ||
| 	 *	C for character < 250 chars
 | ||
| 	 *	X for teXt (>= 250 chars)
 | ||
| 	 *	B for Binary
 | ||
| 	 * 	N for numeric or floating point
 | ||
| 	 *	D for date
 | ||
| 	 *	T for timestamp
 | ||
| 	 * 	L for logical/Boolean
 | ||
| 	 *	I for integer
 | ||
| 	 *	R for autoincrement counter/integer
 | ||
| 	 * 
 | ||
| 	 *
 | ||
| 	*/
 | ||
| 	function MetaType($t,$len=-1,$fieldobj=false)
 | ||
| 	{
 | ||
| 		if (is_object($t)) {
 | ||
| 			$fieldobj = $t;
 | ||
| 			$t = $fieldobj->type;
 | ||
| 			$len = $fieldobj->max_length;
 | ||
| 		}
 | ||
| 	// changed in 2.32 to hashing instead of switch stmt for speed...
 | ||
| 	static $typeMap = array(
 | ||
| 		'VARCHAR' => 'C',
 | ||
| 		'VARCHAR2' => 'C',
 | ||
| 		'CHAR' => 'C',
 | ||
| 		'C' => 'C',
 | ||
| 		'STRING' => 'C',
 | ||
| 		'NCHAR' => 'C',
 | ||
| 		'NVARCHAR' => 'C',
 | ||
| 		'VARYING' => 'C',
 | ||
| 		'BPCHAR' => 'C',
 | ||
| 		'CHARACTER' => 'C',
 | ||
| 		'INTERVAL' => 'C',  # Postgres
 | ||
| 		'MACADDR' => 'C', # postgres
 | ||
| 		'VAR_STRING' => 'C', # mysql
 | ||
| 		##
 | ||
| 		'LONGCHAR' => 'X',
 | ||
| 		'TEXT' => 'X',
 | ||
| 		'NTEXT' => 'X',
 | ||
| 		'M' => 'X',
 | ||
| 		'X' => 'X',
 | ||
| 		'CLOB' => 'X',
 | ||
| 		'NCLOB' => 'X',
 | ||
| 		'LVARCHAR' => 'X',
 | ||
| 		##
 | ||
| 		'BLOB' => 'B',
 | ||
| 		'IMAGE' => 'B',
 | ||
| 		'BINARY' => 'B',
 | ||
| 		'VARBINARY' => 'B',
 | ||
| 		'LONGBINARY' => 'B',
 | ||
| 		'B' => 'B',
 | ||
| 		##
 | ||
| 		'YEAR' => 'D', // mysql
 | ||
| 		'DATE' => 'D',
 | ||
| 		'D' => 'D',
 | ||
| 		##
 | ||
| 		'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
 | ||
| 		##
 | ||
| 		'SMALLDATETIME' => 'T',
 | ||
| 		'TIME' => 'T',
 | ||
| 		'TIMESTAMP' => 'T',
 | ||
| 		'DATETIME' => 'T',
 | ||
| 		'TIMESTAMPTZ' => 'T',
 | ||
| 		'T' => 'T',
 | ||
| 		'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
 | ||
| 		##
 | ||
| 		'BOOL' => 'L',
 | ||
| 		'BOOLEAN' => 'L', 
 | ||
| 		'BIT' => 'L',
 | ||
| 		'L' => 'L',
 | ||
| 		##
 | ||
| 		'COUNTER' => 'R',
 | ||
| 		'R' => 'R',
 | ||
| 		'SERIAL' => 'R', // ifx
 | ||
| 		'INT IDENTITY' => 'R',
 | ||
| 		##
 | ||
| 		'INT' => 'I',
 | ||
| 		'INT2' => 'I',
 | ||
| 		'INT4' => 'I',
 | ||
| 		'INT8' => 'I',
 | ||
| 		'INTEGER' => 'I',
 | ||
| 		'INTEGER UNSIGNED' => 'I',
 | ||
| 		'SHORT' => 'I',
 | ||
| 		'TINYINT' => 'I',
 | ||
| 		'SMALLINT' => 'I',
 | ||
| 		'I' => 'I',
 | ||
| 		##
 | ||
| 		'LONG' => 'N', // interbase is numeric, oci8 is blob
 | ||
| 		'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
 | ||
| 		'DECIMAL' => 'N',
 | ||
| 		'DEC' => 'N',
 | ||
| 		'REAL' => 'N',
 | ||
| 		'DOUBLE' => 'N',
 | ||
| 		'DOUBLE PRECISION' => 'N',
 | ||
| 		'SMALLFLOAT' => 'N',
 | ||
| 		'FLOAT' => 'N',
 | ||
| 		'NUMBER' => 'N',
 | ||
| 		'NUM' => 'N',
 | ||
| 		'NUMERIC' => 'N',
 | ||
| 		'MONEY' => 'N',
 | ||
| 		
 | ||
| 		## informix 9.2
 | ||
| 		'SQLINT' => 'I', 
 | ||
| 		'SQLSERIAL' => 'I', 
 | ||
| 		'SQLSMINT' => 'I', 
 | ||
| 		'SQLSMFLOAT' => 'N', 
 | ||
| 		'SQLFLOAT' => 'N', 
 | ||
| 		'SQLMONEY' => 'N', 
 | ||
| 		'SQLDECIMAL' => 'N', 
 | ||
| 		'SQLDATE' => 'D', 
 | ||
| 		'SQLVCHAR' => 'C', 
 | ||
| 		'SQLCHAR' => 'C', 
 | ||
| 		'SQLDTIME' => 'T', 
 | ||
| 		'SQLINTERVAL' => 'N', 
 | ||
| 		'SQLBYTES' => 'B', 
 | ||
| 		'SQLTEXT' => 'X',
 | ||
| 		 ## informix 10
 | ||
| 		"SQLINT8" => 'I8',
 | ||
| 		"SQLSERIAL8" => 'I8',
 | ||
| 		"SQLNCHAR" => 'C',
 | ||
| 		"SQLNVCHAR" => 'C',
 | ||
| 		"SQLLVARCHAR" => 'X',
 | ||
| 		"SQLBOOL" => 'L'
 | ||
| 		);
 | ||
| 		
 | ||
| 		$tmap = false;
 | ||
| 		$t = strtoupper($t);
 | ||
| 		$tmap = (isset($typeMap[$t])) ? $typeMap[$t] : 'N';
 | ||
| 		switch ($tmap) {
 | ||
| 		case 'C':
 | ||
| 		
 | ||
| 			// is the char field is too long, return as text field... 
 | ||
| 			if ($this->blobSize >= 0) {
 | ||
| 				if ($len > $this->blobSize) return 'X';
 | ||
| 			} else if ($len > 250) {
 | ||
| 				return 'X';
 | ||
| 			}
 | ||
| 			return 'C';
 | ||
| 			
 | ||
| 		case 'I':
 | ||
| 			if (!empty($fieldobj->primary_key)) return 'R';
 | ||
| 			return 'I';
 | ||
| 		
 | ||
| 		case false:
 | ||
| 			return 'N';
 | ||
| 			
 | ||
| 		case 'B':
 | ||
| 			 if (isset($fieldobj->binary)) 
 | ||
| 				 return ($fieldobj->binary) ? 'B' : 'X';
 | ||
| 			return 'B';
 | ||
| 		
 | ||
| 		case 'D':
 | ||
| 			if (!empty($this->connection) && !empty($this->connection->datetime)) return 'T';
 | ||
| 			return 'D';
 | ||
| 			
 | ||
| 		default: 
 | ||
| 			if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
 | ||
| 			return $tmap;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	function _close() {}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * set/returns the current recordset page when paginating
 | ||
| 	 */
 | ||
| 	function AbsolutePage($page=-1)
 | ||
| 	{
 | ||
| 		if ($page != -1) $this->_currentPage = $page;
 | ||
| 		return $this->_currentPage;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * set/returns the status of the atFirstPage flag when paginating
 | ||
| 	 */
 | ||
| 	function AtFirstPage($status=false)
 | ||
| 	{
 | ||
| 		if ($status != false) $this->_atFirstPage = $status;
 | ||
| 		return $this->_atFirstPage;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function LastPageNo($page = false)
 | ||
| 	{
 | ||
| 		if ($page != false) $this->_lastPageNo = $page;
 | ||
| 		return $this->_lastPageNo;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * set/returns the status of the atLastPage flag when paginating
 | ||
| 	 */
 | ||
| 	function AtLastPage($status=false)
 | ||
| 	{
 | ||
| 		if ($status != false) $this->_atLastPage = $status;
 | ||
| 		return $this->_atLastPage;
 | ||
| 	}
 | ||
| 	
 | ||
| } // end class ADORecordSet
 | ||
| 	
 | ||
| 	//==============================================================================================	
 | ||
| 	// CLASS ADORecordSet_array
 | ||
| 	//==============================================================================================	
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * This class encapsulates the concept of a recordset created in memory
 | ||
| 	 * as an array. This is useful for the creation of cached recordsets.
 | ||
| 	 * 
 | ||
| 	 * Note that the constructor is different from the standard ADORecordSet
 | ||
| 	 */
 | ||
| 	
 | ||
| 	class ADORecordSet_array extends ADORecordSet
 | ||
| 	{
 | ||
| 		var $databaseType = 'array';
 | ||
| 
 | ||
| 		var $_array; 	// holds the 2-dimensional data array
 | ||
| 		var $_types;	// the array of types of each column (C B I L M)
 | ||
| 		var $_colnames;	// names of each column in array
 | ||
| 		var $_skiprow1;	// skip 1st row because it holds column names
 | ||
| 		var $_fieldobjects; // holds array of field objects
 | ||
| 		var $canSeek = true;
 | ||
| 		var $affectedrows = false;
 | ||
| 		var $insertid = false;
 | ||
| 		var $sql = '';
 | ||
| 		var $compat = false;
 | ||
| 		/**
 | ||
| 		 * Constructor
 | ||
| 		 *
 | ||
| 		 */
 | ||
| 		function ADORecordSet_array($fakeid=1)
 | ||
| 		{
 | ||
| 		global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
 | ||
| 		
 | ||
| 			// fetch() on EOF does not delete $this->fields
 | ||
| 			$this->compat = !empty($ADODB_COMPAT_FETCH);
 | ||
| 			$this->ADORecordSet($fakeid); // fake queryID		
 | ||
| 			$this->fetchMode = $ADODB_FETCH_MODE;
 | ||
| 		}
 | ||
| 		
 | ||
| 		function _transpose($addfieldnames=true)
 | ||
| 		{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 			
 | ||
| 			if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 			$hdr = true;
 | ||
| 			
 | ||
| 			$fobjs = $addfieldnames ? $this->_fieldobjects : false;
 | ||
| 			adodb_transpose($this->_array, $newarr, $hdr, $fobjs);
 | ||
| 			//adodb_pr($newarr);
 | ||
| 			
 | ||
| 			$this->_skiprow1 = false;
 | ||
| 			$this->_array = $newarr;
 | ||
| 			$this->_colnames = $hdr;
 | ||
| 			
 | ||
| 			adodb_probetypes($newarr,$this->_types);
 | ||
| 		
 | ||
| 			$this->_fieldobjects = array();
 | ||
| 			
 | ||
| 			foreach($hdr as $k => $name) {
 | ||
| 				$f = new ADOFieldObject();
 | ||
| 				$f->name = $name;
 | ||
| 				$f->type = $this->_types[$k];
 | ||
| 				$f->max_length = -1;
 | ||
| 				$this->_fieldobjects[] = $f;
 | ||
| 			}
 | ||
| 			$this->fields = reset($this->_array);
 | ||
| 			
 | ||
| 			$this->_initrs();
 | ||
| 			
 | ||
| 		}
 | ||
| 		
 | ||
| 		/**
 | ||
| 		 * Setup the array.
 | ||
| 		 *
 | ||
| 		 * @param array		is a 2-dimensional array holding the data.
 | ||
| 		 *			The first row should hold the column names 
 | ||
| 		 *			unless paramter $colnames is used.
 | ||
| 		 * @param typearr	holds an array of types. These are the same types 
 | ||
| 		 *			used in MetaTypes (C,B,L,I,N).
 | ||
| 		 * @param [colnames]	array of column names. If set, then the first row of
 | ||
| 		 *			$array should not hold the column names.
 | ||
| 		 */
 | ||
| 		function InitArray($array,$typearr,$colnames=false)
 | ||
| 		{
 | ||
| 			$this->_array = $array;
 | ||
| 			$this->_types = $typearr;	
 | ||
| 			if ($colnames) {
 | ||
| 				$this->_skiprow1 = false;
 | ||
| 				$this->_colnames = $colnames;
 | ||
| 			} else  {
 | ||
| 				$this->_skiprow1 = true;
 | ||
| 				$this->_colnames = $array[0];
 | ||
| 			}
 | ||
| 			$this->Init();
 | ||
| 		}
 | ||
| 		/**
 | ||
| 		 * Setup the Array and datatype file objects
 | ||
| 		 *
 | ||
| 		 * @param array		is a 2-dimensional array holding the data.
 | ||
| 		 *			The first row should hold the column names 
 | ||
| 		 *			unless paramter $colnames is used.
 | ||
| 		 * @param fieldarr	holds an array of ADOFieldObject's.
 | ||
| 		 */
 | ||
| 		function InitArrayFields(&$array,&$fieldarr)
 | ||
| 		{
 | ||
| 			$this->_array = $array;
 | ||
| 			$this->_skiprow1= false;
 | ||
| 			if ($fieldarr) {
 | ||
| 				$this->_fieldobjects = $fieldarr;
 | ||
| 			} 
 | ||
| 			$this->Init();
 | ||
| 		}
 | ||
| 		
 | ||
| 		function GetArray($nRows=-1)
 | ||
| 		{
 | ||
| 			if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
 | ||
| 				return $this->_array;
 | ||
| 			} else {
 | ||
| 				$arr = ADORecordSet::GetArray($nRows);
 | ||
| 				return $arr;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		
 | ||
| 		function _initrs()
 | ||
| 		{
 | ||
| 			$this->_numOfRows =  sizeof($this->_array);
 | ||
| 			if ($this->_skiprow1) $this->_numOfRows -= 1;
 | ||
| 		
 | ||
| 			$this->_numOfFields =(isset($this->_fieldobjects)) ?
 | ||
| 				 sizeof($this->_fieldobjects):sizeof($this->_types);
 | ||
| 		}
 | ||
| 		
 | ||
| 		/* Use associative array to get fields array */
 | ||
| 		function Fields($colname)
 | ||
| 		{
 | ||
| 			$mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
 | ||
| 			
 | ||
| 			if ($mode & ADODB_FETCH_ASSOC) {
 | ||
| 				if (!isset($this->fields[$colname]) && !is_null($this->fields[$colname])) $colname = strtolower($colname);
 | ||
| 				return $this->fields[$colname];
 | ||
| 			}
 | ||
| 			if (!$this->bind) {
 | ||
| 				$this->bind = array();
 | ||
| 				for ($i=0; $i < $this->_numOfFields; $i++) {
 | ||
| 					$o = $this->FetchField($i);
 | ||
| 					$this->bind[strtoupper($o->name)] = $i;
 | ||
| 				}
 | ||
| 			}
 | ||
| 			return $this->fields[$this->bind[strtoupper($colname)]];
 | ||
| 		}
 | ||
| 		
 | ||
| 		function FetchField($fieldOffset = -1) 
 | ||
| 		{
 | ||
| 			if (isset($this->_fieldobjects)) {
 | ||
| 				return $this->_fieldobjects[$fieldOffset];
 | ||
| 			}
 | ||
| 			$o =  new ADOFieldObject();
 | ||
| 			$o->name = $this->_colnames[$fieldOffset];
 | ||
| 			$o->type =  $this->_types[$fieldOffset];
 | ||
| 			$o->max_length = -1; // length not known
 | ||
| 			
 | ||
| 			return $o;
 | ||
| 		}
 | ||
| 			
 | ||
| 		function _seek($row)
 | ||
| 		{
 | ||
| 			if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
 | ||
| 				$this->_currentRow = $row;
 | ||
| 				if ($this->_skiprow1) $row += 1;
 | ||
| 				$this->fields = $this->_array[$row];
 | ||
| 				return true;
 | ||
| 			}
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 		
 | ||
| 		function MoveNext() 
 | ||
| 		{
 | ||
| 			if (!$this->EOF) {		
 | ||
| 				$this->_currentRow++;
 | ||
| 				
 | ||
| 				$pos = $this->_currentRow;
 | ||
| 				
 | ||
| 				if ($this->_numOfRows <= $pos) {
 | ||
| 					if (!$this->compat) $this->fields = false;
 | ||
| 				} else {
 | ||
| 					if ($this->_skiprow1) $pos += 1;
 | ||
| 					$this->fields = $this->_array[$pos];
 | ||
| 					return true;
 | ||
| 				}		
 | ||
| 				$this->EOF = true;
 | ||
| 			}
 | ||
| 			
 | ||
| 			return false;
 | ||
| 		}	
 | ||
| 	
 | ||
| 		function _fetch()
 | ||
| 		{
 | ||
| 			$pos = $this->_currentRow;
 | ||
| 			
 | ||
| 			if ($this->_numOfRows <= $pos) {
 | ||
| 				if (!$this->compat) $this->fields = false;
 | ||
| 				return false;
 | ||
| 			}
 | ||
| 			if ($this->_skiprow1) $pos += 1;
 | ||
| 			$this->fields = $this->_array[$pos];
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 		
 | ||
| 		function _close() 
 | ||
| 		{
 | ||
| 			return true;	
 | ||
| 		}
 | ||
| 	
 | ||
| 	} // ADORecordSet_array
 | ||
| 
 | ||
| 	//==============================================================================================	
 | ||
| 	// HELPER FUNCTIONS
 | ||
| 	//==============================================================================================			
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Synonym for ADOLoadCode. Private function. Do not use.
 | ||
| 	 *
 | ||
| 	 * @deprecated
 | ||
| 	 */
 | ||
| 	function ADOLoadDB($dbType) 
 | ||
| 	{ 
 | ||
| 		return ADOLoadCode($dbType);
 | ||
| 	}
 | ||
| 		
 | ||
| 	/**
 | ||
| 	 * Load the code for a specific database driver. Private function. Do not use.
 | ||
| 	 */
 | ||
| 	function ADOLoadCode($dbType) 
 | ||
| 	{
 | ||
| 	global $ADODB_LASTDB;
 | ||
| 	
 | ||
| 		if (!$dbType) return false;
 | ||
| 		$db = strtolower($dbType);
 | ||
| 		switch ($db) {
 | ||
| 			case 'ado': 
 | ||
| 				if (PHP_VERSION >= 5) $db = 'ado5';
 | ||
| 				$class = 'ado'; 
 | ||
| 				break;
 | ||
| 			case 'ifx':
 | ||
| 			case 'maxsql': $class = $db = 'mysqlt'; break;
 | ||
| 			case 'postgres':
 | ||
| 			case 'postgres8':
 | ||
| 			case 'pgsql': $class = $db = 'postgres7'; break;
 | ||
| 			default:
 | ||
| 				$class = $db; break;
 | ||
| 		}
 | ||
| 		
 | ||
| 		$file = ADODB_DIR."/drivers/adodb-".$db.".inc.php";
 | ||
| 		@include_once($file);
 | ||
| 		$ADODB_LASTDB = $class;
 | ||
| 		if (class_exists("ADODB_" . $class)) return $class;
 | ||
| 		
 | ||
| 		//ADOConnection::outp(adodb_pr(get_declared_classes(),true));
 | ||
| 		if (!file_exists($file)) ADOConnection::outp("Missing file: $file");
 | ||
| 		else ADOConnection::outp("Syntax error in file: $file");
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * synonym for ADONewConnection for people like me who cannot remember the correct name
 | ||
| 	 */
 | ||
| 	function NewADOConnection($db='')
 | ||
| 	{
 | ||
| 		$tmp = ADONewConnection($db);
 | ||
| 		return $tmp;
 | ||
| 	}
 | ||
| 	
 | ||
| 	/**
 | ||
| 	 * Instantiate a new Connection class for a specific database driver.
 | ||
| 	 *
 | ||
| 	 * @param [db]  is the database Connection object to create. If undefined,
 | ||
| 	 * 	use the last database driver that was loaded by ADOLoadCode().
 | ||
| 	 *
 | ||
| 	 * @return the freshly created instance of the Connection class.
 | ||
| 	 */
 | ||
| 	function ADONewConnection($db='')
 | ||
| 	{
 | ||
| 	GLOBAL $ADODB_NEWCONNECTION, $ADODB_LASTDB;
 | ||
| 		
 | ||
| 		if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
 | ||
| 		$errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
 | ||
| 		$false = false;
 | ||
| 		if (($at = strpos($db,'://')) !== FALSE) {
 | ||
| 			$origdsn = $db;
 | ||
| 			$fakedsn = 'fake'.substr($origdsn,$at);
 | ||
| 			if (($at2 = strpos($origdsn,'@/')) !== FALSE) {
 | ||
| 				// special handling of oracle, which might not have host
 | ||
| 				$fakedsn = str_replace('@/','@adodb-fakehost/',$fakedsn);
 | ||
| 			}
 | ||
| 			
 | ||
| 			 if ((strpos($origdsn, 'sqlite')) !== FALSE) {
 | ||
|              // special handling for SQLite, it only might have the path to the database file.
 | ||
|              // If you try to connect to a SQLite database using a dsn like 'sqlite:///path/to/database', the 'parse_url' php function
 | ||
|              // will throw you an exception with a message such as "unable to parse url"
 | ||
|                 list($scheme, $path) = explode('://', $origdsn);
 | ||
|                 $dsna['scheme'] = $scheme;
 | ||
| 				if ($qmark = strpos($path,'?')) {
 | ||
| 					$dsn['query'] = substr($path,$qmark+1);
 | ||
| 					$path = substr($path,0,$qmark);
 | ||
| 				}
 | ||
|             	$dsna['path'] = '/' . urlencode($path);
 | ||
| 			} else
 | ||
| 				$dsna = @parse_url($fakedsn);
 | ||
| 				
 | ||
| 			if (!$dsna) {
 | ||
| 				return $false;
 | ||
| 			}
 | ||
| 			$dsna['scheme'] = substr($origdsn,0,$at);
 | ||
| 			if ($at2 !== FALSE) {
 | ||
| 				$dsna['host'] = '';
 | ||
| 			}
 | ||
| 			
 | ||
| 			if (strncmp($origdsn,'pdo',3) == 0) {
 | ||
| 				$sch = explode('_',$dsna['scheme']);
 | ||
| 				if (sizeof($sch)>1) {
 | ||
| 				
 | ||
| 					$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
 | ||
| 					if ($sch[1] == 'sqlite')
 | ||
| 						$dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
 | ||
| 					else
 | ||
| 						$dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
 | ||
| 					$dsna['scheme'] = 'pdo';
 | ||
| 				}
 | ||
| 			}
 | ||
| 			
 | ||
| 			$db = @$dsna['scheme'];
 | ||
| 			if (!$db) return $false;
 | ||
| 			$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
 | ||
| 			$dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
 | ||
| 			$dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
 | ||
| 			$dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
 | ||
| 			
 | ||
| 			if (isset($dsna['query'])) {
 | ||
| 				$opt1 = explode('&',$dsna['query']);
 | ||
| 				foreach($opt1 as $k => $v) {
 | ||
| 					$arr = explode('=',$v);
 | ||
| 					$opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
 | ||
| 				}
 | ||
| 			} else $opt = array();
 | ||
| 		}
 | ||
| 	/*
 | ||
| 	 *  phptype: Database backend used in PHP (mysql, odbc etc.)
 | ||
| 	 *  dbsyntax: Database used with regards to SQL syntax etc.
 | ||
| 	 *  protocol: Communication protocol to use (tcp, unix etc.)
 | ||
| 	 *  hostspec: Host specification (hostname[:port])
 | ||
| 	 *  database: Database to use on the DBMS server
 | ||
| 	 *  username: User name for login
 | ||
| 	 *  password: Password for login
 | ||
| 	 */
 | ||
| 		if (!empty($ADODB_NEWCONNECTION)) {
 | ||
| 			$obj = $ADODB_NEWCONNECTION($db);
 | ||
| 
 | ||
| 		} 
 | ||
| 		
 | ||
| 		if(empty($obj)) {
 | ||
| 		
 | ||
| 			if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
 | ||
| 			if (empty($db)) $db = $ADODB_LASTDB;
 | ||
| 			
 | ||
| 			if ($db != $ADODB_LASTDB) $db = ADOLoadCode($db);
 | ||
| 			
 | ||
| 			if (!$db) {
 | ||
| 				if (isset($origdsn)) $db = $origdsn;
 | ||
| 				if ($errorfn) {
 | ||
| 					// raise an error
 | ||
| 					$ignore = false;
 | ||
| 					$errorfn('ADONewConnection', 'ADONewConnection', -998,
 | ||
| 							 "could not load the database driver for '$db'",
 | ||
| 							 $db,false,$ignore);
 | ||
| 				} else
 | ||
| 					 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
 | ||
| 					
 | ||
| 				return $false;
 | ||
| 			}
 | ||
| 			
 | ||
| 			$cls = 'ADODB_'.$db;
 | ||
| 			if (!class_exists($cls)) {
 | ||
| 				adodb_backtrace();
 | ||
| 				return $false;
 | ||
| 			}
 | ||
| 			
 | ||
| 			$obj = new $cls();
 | ||
| 		}
 | ||
| 		
 | ||
| 		# constructor should not fail
 | ||
| 		if ($obj) {
 | ||
| 			if ($errorfn)  $obj->raiseErrorFn = $errorfn;
 | ||
| 			if (isset($dsna)) {
 | ||
| 				if (isset($dsna['port'])) $obj->port = $dsna['port'];
 | ||
| 				foreach($opt as $k => $v) {
 | ||
| 					switch(strtolower($k)) {
 | ||
| 					case 'new':
 | ||
| 										$nconnect = true; $persist = true; break;
 | ||
| 					case 'persist':
 | ||
| 					case 'persistent': 	$persist = $v; break;
 | ||
| 					case 'debug':		$obj->debug = (integer) $v; break;
 | ||
| 					#ibase
 | ||
| 					case 'role':		$obj->role = $v; break;
 | ||
| 					case 'dialect': 	$obj->dialect = (integer) $v; break;
 | ||
| 					case 'charset':		$obj->charset = $v; $obj->charSet=$v; break;
 | ||
| 					case 'buffers':		$obj->buffers = $v; break;
 | ||
| 					case 'fetchmode':   $obj->SetFetchMode($v); break;
 | ||
| 					#ado
 | ||
| 					case 'charpage':	$obj->charPage = $v; break;
 | ||
| 					#mysql, mysqli
 | ||
| 					case 'clientflags': $obj->clientFlags = $v; break;
 | ||
| 					#mysql, mysqli, postgres
 | ||
| 					case 'port': $obj->port = $v; break;
 | ||
| 					#mysqli
 | ||
| 					case 'socket': $obj->socket = $v; break;
 | ||
| 					#oci8
 | ||
| 					case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
 | ||
| 					case 'cachesecs': $obj->cacheSecs = $v; break;
 | ||
| 					case 'memcache': 
 | ||
| 						$varr = explode(':',$v);
 | ||
| 						$vlen = sizeof($varr);
 | ||
| 						if ($vlen == 0) break;	
 | ||
| 						$obj->memCache = true;
 | ||
| 						$obj->memCacheHost = explode(',',$varr[0]);
 | ||
| 						if ($vlen == 1) break;	
 | ||
| 						$obj->memCachePort = $varr[1];
 | ||
| 						if ($vlen == 2) break;	
 | ||
| 						$obj->memCacheCompress = $varr[2] ?  true : false;
 | ||
| 						break;
 | ||
| 					}
 | ||
| 				}
 | ||
| 				if (empty($persist))
 | ||
| 					$ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
 | ||
| 				else if (empty($nconnect))
 | ||
| 					$ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
 | ||
| 				else
 | ||
| 					$ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
 | ||
| 					
 | ||
| 				if (!$ok) return $false;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return $obj;
 | ||
| 	}
 | ||
| 	
 | ||
| 	
 | ||
| 	
 | ||
| 	// $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
 | ||
| 	function _adodb_getdriver($provider,$drivername,$perf=false)
 | ||
| 	{
 | ||
| 		switch ($provider) {
 | ||
| 		case 'odbtp':   if (strncmp('odbtp_',$drivername,6)==0) return substr($drivername,6); 
 | ||
| 		case 'odbc' :   if (strncmp('odbc_',$drivername,5)==0) return substr($drivername,5); 
 | ||
| 		case 'ado'  :   if (strncmp('ado_',$drivername,4)==0) return substr($drivername,4);
 | ||
| 		case 'native':  break;
 | ||
| 		default:
 | ||
| 			return $provider;
 | ||
| 		}
 | ||
| 		
 | ||
| 		switch($drivername) {
 | ||
| 		case 'mysqlt':
 | ||
| 		case 'mysqli': 
 | ||
| 				$drivername='mysql'; 
 | ||
| 				break;
 | ||
| 		case 'postgres7':
 | ||
| 		case 'postgres8':
 | ||
| 				$drivername = 'postgres'; 
 | ||
| 				break;	
 | ||
| 		case 'firebird15': $drivername = 'firebird'; break;
 | ||
| 		case 'oracle': $drivername = 'oci8'; break;
 | ||
| 		case 'access': if ($perf) $drivername = ''; break;
 | ||
| 		case 'db2'   : break;
 | ||
| 		case 'sapdb' : break;
 | ||
| 		default:
 | ||
| 			$drivername = 'generic';
 | ||
| 			break;
 | ||
| 		}
 | ||
| 		return $drivername;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function NewPerfMonitor(&$conn)
 | ||
| 	{
 | ||
| 		$false = false;
 | ||
| 		$drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
 | ||
| 		if (!$drivername || $drivername == 'generic') return $false;
 | ||
| 		include_once(ADODB_DIR.'/adodb-perf.inc.php');
 | ||
| 		@include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
 | ||
| 		$class = "Perf_$drivername";
 | ||
| 		if (!class_exists($class)) return $false;
 | ||
| 		$perf = new $class($conn);
 | ||
| 		
 | ||
| 		return $perf;
 | ||
| 	}
 | ||
| 	
 | ||
| 	function NewDataDictionary(&$conn,$drivername=false)
 | ||
| 	{
 | ||
| 		$false = false;
 | ||
| 		if (!$drivername) $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
 | ||
| 
 | ||
| 		include_once(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		include_once(ADODB_DIR.'/adodb-datadict.inc.php');
 | ||
| 		$path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
 | ||
| 
 | ||
| 		if (!file_exists($path)) {
 | ||
| 			ADOConnection::outp("Dictionary driver '$path' not available");
 | ||
| 			return $false;
 | ||
| 		}
 | ||
| 		include_once($path);
 | ||
| 		$class = "ADODB2_$drivername";
 | ||
| 		$dict = new $class();
 | ||
| 		$dict->dataProvider = $conn->dataProvider;
 | ||
| 		$dict->connection = $conn;
 | ||
| 		$dict->upperName = strtoupper($drivername);
 | ||
| 		$dict->quote = $conn->nameQuote;
 | ||
| 		if (!empty($conn->_connectionID))
 | ||
| 			$dict->serverInfo = $conn->ServerInfo();
 | ||
| 		
 | ||
| 		return $dict;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	
 | ||
| 	/*
 | ||
| 		Perform a print_r, with pre tags for better formatting.
 | ||
| 	*/
 | ||
| 	function adodb_pr($var,$as_string=false)
 | ||
| 	{
 | ||
| 		if ($as_string) ob_start();
 | ||
| 		
 | ||
| 		if (isset($_SERVER['HTTP_USER_AGENT'])) { 
 | ||
| 			echo " <pre>\n";print_r($var);echo "</pre>\n";
 | ||
| 		} else
 | ||
| 			print_r($var);
 | ||
| 			
 | ||
| 		if ($as_string) {
 | ||
| 			$s = ob_get_contents();
 | ||
| 			ob_end_clean();
 | ||
| 			return $s;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	
 | ||
| 	/*
 | ||
| 		Perform a stack-crawl and pretty print it.
 | ||
| 		
 | ||
| 		@param printOrArr  Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
 | ||
| 		@param levels Number of levels to display
 | ||
| 	*/
 | ||
| 	function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null)
 | ||
| 	{
 | ||
| 		global $ADODB_INCLUDED_LIB;
 | ||
| 		if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
 | ||
| 		return _adodb_backtrace($printOrArr,$levels,0,$ishtml);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| }
 | ||
| ?>
 |