<?php
	/**
	 * number of SQL queries executed so far. It is up to the driver classes
	 * to keep this number updated!!
	 */
	$__pdb_num_queries = 0;
	
    /**
     * \ingroup PDb
     *
     * This class provides the base methods that all database drivers are expected to implement. Some methods
     * provide some common functionality that should be called even when the method is overwritten, so please make sure
     * to call the method of the parent class for those that need it.
     */
	class PDbDriverBase 
	{
		
		var $_dbname;
		var $_host;
		var $_user;
		var $_password;
		var $_type = 'generic';
		var $nameQuote = '`';
		var $_debug = false;
		var $_connected = false;
		var $_opts;
	
	    /**
	     * Generates a new instance of the driver. Please use PDb::getDriver() instead
	     *
	     * @see PDb::getDriver()
	     */
		function PDbDriverBase()
		{
			$this->_opts = Array();
        }
		
		/** 
		 * Allows drivers to use custom options
		 *
		 * @param key
		 * @param value
		 */
		function setDriverOpt( $key, $value )
		{
			$this->_opts[$key] = $value;
		}
		
		/**
		 * Set the driver options via an array. This method will completely replace any
		 * options that have been set up until this moment via PDbDriverBase::setDriverOpt.
		 *
		 * @param opts An associative array containing driver-specific options
		 */
		function setDriverOpts( $opts )
		{
			$this->_opts = $opts;
		}
		
		/**
		 * Executes a query and returns a PDbResultSet as a result, or false if the query wasn't
		 * successfully executed.
		 *
		 * This method must be implemented by database drivers.
		 *
		 * @param query A string with the query to execute
		 * @param page Page of records, when using pagination. Leave empty if pagination is not needed.
		 * @param itemsPerPage Amount of record per page, when using pagination.
		 * @return A PDbResultSet object if successful or false otherwise
		 */
		function Execute( $query, $page = -1, $itemsPerPage = 15 )
		{
			// to be implemented by child classes
		}
		
		/**
		 * Starts a new connection to a databse
		 *
		 * Database drivers should call this method after providing their own implementation.
		 *
		 * @param host The host to which we're initiating the connection
		 * @param username The username used to connecto the database
		 * @param password Password assigned to the user above
		 * @param dbname The name of the database to which we're connecting
		 * @return Returns true if successful or false otherwise
		 */
		function Connect( $host, $username, $password, $dbname = null )
		{
			$this->_host = $host;
			$this->_username = $username;
			$this->_password = $password;
			$this->_dbname = $dbname;
			$this->_connected = true;
			
			// extra functionality to be implemented by child classes...
		}
		/**
		 * Starts a new persistent connection to the database
		 *
		 * Database drivers should call this method after providing their own implementation.
		 *		 
		 * @param host The host to which we're initiating the connection
		 * @param username The username used to connecto the database
		 * @param password Password assigned to the user above
		 * @param dbname The name of the database to which we're connecting
		 * @return Returns true if successful or false otherwise
		 */		
		function PConnect( $host, $username, $password, $dbname )
		{
			$this->_host = $host;
			$this->_username = $username;
			$this->_password = $password;
			$this->_dbname = $dbname;
			$this->_connected = true;
			
			// extra functionality to be implemented by child classes...
		}
        /**
         * Closes the current connection to the database
         *
		 * This method must be implemented by database drivers.
         *
         * @return nothing
         */
		function Close()
		{
		    // to be implemented by child classes
		}		
		
		/**
		 * Returns the last error message that was generated by the driver
		 *
		 * This method must be implemented by database drivers.
		 *		 
		 * @return A string representing an error message, if any.		 
		 */
		function ErrorMsg()
		{
			// to be implemented by child classes
		}
		
		/**
		 * The row id as generated by the last INSERT operation.
		 *
		 * This method must be implemented by database drivers.		 
		 *
		 * @return A row id
		 */
		function Insert_ID()
		{
			// to be implemented by child classes
		}
		
		/**
		 * Returns the number of rows affected by the last UPDATE, INSERT or DELETE operation.
		 * Use PDbRecordSet::Row_Count() to retrieve the number of rows in a SELECT operation.
		 *
		 * This method must be implemented by database drivers.		 
		 * 
		 * @return the number of affected rows
		 * @see PDbRecordSet::Row_Count()
		 */
		function Affected_Rows()
		{
			// to be implemented by child classes
		}
		
		/**
		 * Returns true if the driver is currently connected to a database. Connections usually happen
		 * after Connect or PConnect are called
		 *
		 * @return true if the driver is currently conneced or false otherwise
		 * @see PDbDriverBase::PConnect()
		 * @see PDbDriverBase::Connect()
		 */
		function IsConnected()
		{
		   return( $this->_connected );
		}
		
		/**
		 * @private
		 */
		function _debugQuery( $query )
		{
		    if( $this->_debug ) {
		       print("<hr/>$query<hr/>");
		    }
		    
		    return( true );
		}
		
		/**
		 * Activates the debug stuff.
		 *
		 * @param debug Whether debug should be enabled or not
		 */
		function setDebug( $debug )
		{
		    $this->_debug = $debug;
		}
        
        /**
         * for compatibility with ADOdb. Use Db::qstr() instead
         *
         * @private
         */
        function qstr( $string )         
        {
            return( Db::qstr( $string ));
        }
        
        /**
         * Load the correct data dictionary driver. Child classes should overwrite this method
         * and provide the right driver name so that this method can be called without parameters.
         *
         * @param driverName the driver name.
         * @param A reference to the driver-specific PDbBaseDataDict object
         */
        function &getDriverDataDictionary( $driverName ) 
        {            
            $dataDictPath = PLOG_CLASS_PATH."class/database/pdb/datadict/pdb".strtolower($driverName)."datadict.class.php";
            
            // check if the driver exists at all
            if( !is_readable( $dataDictPath )) {
                throw( new Exception( "Cannot load data dictionary for driver $driverName!" ));
                die();
            }
            
            //print( "Loading datadict driver $dataDictPath...<br/>" );
            
            lt_include( $dataDictPath );
            
            $className = "PDb{$driverName}DataDict";
            $class =& new $className();
            
            $class->dataProvider = $this;
            $class->connection = &$this;
            $class->upperName = strtoupper($driverName);
            
            return( $class );
        }
    
        /**
         * @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)
        {    
            $metaTablesSQL = "SHOW TABLES";
            
            if ($showSchema && is_string($showSchema)) {
                $metaTablesSQL .= " FROM $showSchema";
            }
            
            if ($mask) {
                $mask = $this->qstr($mask);
                $metaTablesSQL .= " LIKE $mask";
            }
    
            $rs = $this->Execute($metaTablesSQL);
    
            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;
        }
    
        /**
         * @returns an array with the primary key columns in it.
         *
         * @param table
         * @param owner
         */
        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;
            return false;
        }
    
        /**
         * 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 upper uppercase table name (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)
        {
			lt_include( PLOG_CLASS_PATH."class/database/pdb/datadict/pdbfielddescobject.class.php" );

            $rs = $this->Execute(sprintf("SHOW COLUMNS FROM %s",$table));
			
            $retarr = array();			
			
			if( !$rs )
				return $retarr;
			
			while( $row = $rs->FetchRow()) {
				$fld = new PDbFieldDescObject();
				
				//
				// :TODO:
				// Is this mysql-specific stuff?? If so, it should be moved to the Mysql driver!!
				//
				$fld->name = $row["Field"];
				$type = $row["Type"];
				
				// split type into type(length):
				$fld->scale = null;
				if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
					$fld->type = $query_array[1];
					$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
					$fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
				} elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
					$fld->type = $query_array[1];
					$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
				} else {
				$fld->type = $type;
					$fld->max_length = -1;
				}
				$fld->not_null = ($row["Null"] != 'YES');
				$fld->primary_key = ($row["Key"] == 'PRI');
				$fld->auto_increment = (strpos($row["Extra"], 'auto_increment') !== false);
				$fld->binary = (strpos($type,'blob') !== false);
				$fld->unsigned = (strpos($type,'unsigned') !== false);
				
				if (!$fld->binary) {
					$d = $row["Default"];
				if ($d != '' && $d != 'NULL') {
						$fld->has_default = true;
						$fld->default_value = $d;
					} else {
						$fld->has_default = false;
					}
				}                
			
				$retarr[strtoupper($fld->name)] = $fld;
			}
            
            $rs->Close();
            return $retarr;
        }
    
        /**
         * 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)
        {
            $objarr =& $this->MetaColumns($table);
            if (!is_array($objarr)) return false;
    
            $arr = array();
            if ($numIndexes) {
                $i = 0;
                foreach($objarr as $v) $arr[$i++] = $v->name;
            } else
                foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
    
            return $arr;
        }
		
		/**
		 * Returns the number of queries executed so far
		 */
		function getNumQueries()
		{
			global $__pdb_num_queries;
			return( $__pdb_num_queries );
		}
		
		/**
		 * @return Returns the name of current character set, or 'default' if none has been explicitely selected
		 */
		function getDbCharacterSet()
		{
			return( 'default' );
		}
		
		/**
		 * Returns true if the current database supports FULLTEXT searches. This method needs to be
		 * implemented by child classes.
		 *
		 * @return true if FULLTEXT is supported
		 */
		function isFullTextSupported()
		{
			return( false );
		}
	}
?>
