qcubed/framework

View on GitHub
includes/database/QOracleDatabase.class.php

Summary

Maintainability
F
1 wk
Test Coverage
<?php
/**
 * EXPERIMENTAL Oracle database adapter.
 *
 * @package DatabaseAdapters
 * @author Riccardo Tacconi, Ago Luberg, enzo - Eduardo Garcia
 
*/

class QOracleDatabase extends QDatabaseBase {
    const Adapter = 'Oracle Database Adapter';
    protected $objOracle;

    protected $EscapeIdentifierBegin = '';
    protected $EscapeIdentifierEnd = '';
    protected $blnOnlyFullGroupBy = true;
    protected $debug;
    protected $commitMode;

    public function SqlLimitVariablePrefix($strLimitInfo) {
        // MySQL uses Limit by Suffixes (via a LIMIT clause)
        // Prefix is not used, therefore, return null
        return null;
    }

    public function SqlLimitVariableSuffix($strLimitInfo) {
        // Setup limit suffix (if applicable) via a LIMIT clause
        // the parameter receives 10 or 60,10 from instance
        // then from Mysql style must be converted to the Oracle way
        if (strlen($strLimitInfo)) {
            if (strpos($strLimitInfo, ';') !== false)
            throw new Exception('Invalid Semicolon in LIMIT Info');
            if (strpos($strLimitInfo, '`') !== false)
            throw new Exception('Invalid Backtick in LIMIT Info');

            //if there is a ',' we change it to ORACLE ROWNUM

            if(strpos($strLimitInfo,',') !== false){
                // we have to add a lot of code to make a sql statement with
                //pagination FROM x to y
                // Oracle doesnt support LIMT, or OFFSET or TOP

                $array_limit = explode(',',$strLimitInfo);
                //array_limit[0] is the MIN row to fetch
                //max_row is MAX row to fetch

                $max_row = $array_limit[0] + $array_limit[1];

                return "_LIMIT2_) a where ROWNUM <= $max_row ) where rnum  > $array_limit[0]";
            }
            else{
                //The string LIMIT will be parsed in Query Method
                return "_LIMIT1_) where rownum <= $strLimitInfo";
            }
        }

        return null;
    }

    public function SqlSortByVariable($strSortByInfo) {
        // Setup sorting information (if applicable) via a ORDER BY clause
        if (strlen($strSortByInfo)) {
            if (strpos($strSortByInfo, ';') !== false)
            throw new Exception('Invalid Semicolon in ORDER BY Info');
            if (strpos($strSortByInfo, '`') !== false)
            throw new Exception('Invalid Backtick in ORDER BY Info');

            return "ORDER BY $strSortByInfo";
        }
            
        return null;
    }

    public function Connect() {
        // Lookup Adapter-Specific Connection Properties
        $strServer = $this->Server;
        $strName = $this->Database;
        $strUsername = $this->Username;
        $strPassword = $this->Password;
        $strPort = $this->Port;
        //echo "DB vars: ".$this->__get('Username');

        // Connect to the Database Server
        $this->objOracle = ociplogon($strUsername, $strPassword, "$strServer/$strName",'UTF8');

        if (!$this->objOracle)
            throw new QOracleDatabaseException("Unable to connect to Database", -1, null);

        if ($objOracle_error=oci_error())
            throw new QOracleDatabaseException($objOracle_error['message'],$objOracle_error['code'] , null);

        // Set to AutoCommit
        //$this->NonQuery('SET AUTOCOMMIT=1;');
        //Set SORT with special characteres
        //$this->NonQuery("ALTER SESSION SET NLS_SORT=SPANISH");
        // Update "Connected" Flag
        $this->blnConnectedFlag = true;
        
        $this->NonQuery("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD hh24:mi:ss'");
        //$this->NonQuery("ALTER SESSION SET NLS_LANGUAGE='AMERICAN'");
        // to use . instead of , for floating point
        //$this->NonQuery("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
    }

    public function __get($strName) {
        switch ($strName) {
            case 'AffectedRows':
                return $this->objOracle->affected_rows;
            case 'EscapeIdentifierBegin':
                return $this->EscapeIdentifierBegin;
            case 'EscapeIdentifierEnd':
                return $this->EscapeIdentifierEnd;
            default:
                try {
                    return parent::__get($strName);
                } catch (QCallerException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
        }
    }

  /**
   * Prepares Oracle statement for execution
   *
   * @param string $strQuery
   * @return resource
   */
  public function Parse ($strQuery) {
    // Connect if Applicable
    if (!$this->blnConnectedFlag) $this->Connect();
    return oci_parse($this->objOracle,$strQuery);
  }


  /**
   * Executes prepared statement and returns usable object
   *
   * @param resource $objResult
   * @return QOracleDatabaseResult
   */
  protected function Execute ($objResult) {
    $blnReturn = false;
    if($objResult)
    {
      if($this->commitMode) {
        $blnReturn = oci_execute($objResult,OCI_DEFAULT);
      }
      else {
          $blnReturn = @oci_execute($objResult);
          if (!$blnReturn) {
            $objOracle_error=oci_error($objResult);
          }
      }
    }

    if ($objOracle_error=oci_error())
    {
      throw new QOracleDatabaseException($objOracle_error['message'],$objOracle_error['code'] , null);
    }

    return $blnReturn;
  }


    protected function ExecuteQuery($strQuery) {
        
        // Connect if Applicable
        if (!$this->blnConnectedFlag) $this->Connect();

        //looks for escaped characters like \' and replace them for two single
        //quotes. this is needed because, Oracle, diferently from other SGDB
        // do not understand \ do parse quotes and double quotes

        $strQuery = str_replace("\'","''",$strQuery);

        //remove backslash from escaped characters like \" and \\
        //Oracle has a problem only with single quotes escaped
        $strQuery = stripslashes($strQuery);

        //oracle does not accept the word AS to alias a table
        $strQuery = str_replace(' AS ',' ',$strQuery);

        //oracle does not accept formated SQL statement,
        // so strip off /n/t/r
        $strQuery=trim($strQuery);

        // called for displaying the pages except first page... see the else clause
        if(!strpos($strQuery,'_LIMIT2_')===false){
            //check if SqlLimitVariableSuffix was called
            // and if it as a "composed limit" ( from x to y)
            //if so, add the rest of the query, and it will look like this
            // http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html

            $strQuery ="SELECT * FROM ( SELECT /*+ FIRST_ROWS(n) */ a.*, ROWNUM 
                             rnum from (". str_replace('_LIMIT2_','',$strQuery);

        }
        // called for displaying the first page
        else if(!strpos($strQuery,'_LIMIT1_')===false){
            //check if SqlLimitVariableSuffix was called
            // and if it as a "simple limit" (TOP N)
            //if so, add the rest of the query, and it will look like this
            // http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html

            $strQuery ="SELECT * FROM(". str_replace('_LIMIT1_','',$strQuery);
        }
            
        // Perform the Query
        //print $strQuery; die();
        $objResult = oci_parse($this->objOracle,$strQuery);

        if($objResult)
        {
            if($this->commitMode) {
                oci_execute($objResult,OCI_DEFAULT);
            }
            else {
                //die($strQuery);
                oci_execute($objResult);
            }
        }

        if ($objOracle_error=oci_error())
        {
            throw new QOracleDatabaseException($objOracle_error['message'],$objOracle_error['code'] , null);
        }

        // Return the Result

        $objOracleDatabaseResult = new QOracleDatabaseResult($objResult, $this);
        return $objOracleDatabaseResult;
    }



    protected function ExecuteNonQuery($strNonQuery) {

        // Connect if Applicable
        if (!$this->blnConnectedFlag) $this->Connect();
        
        //looks for escaped characters like \' and replace them for two single
        //quotes. this is needed because, Oracle, diferently from other SGDB
        // do not understand \ do parse quotes and double quotes

        $strNonQuery = str_replace("\'","''",$strNonQuery);

        //remove backslash from escaped characters like \" and \\
        //Oracle has a problem only with single quotes escaped
        $strNonQuery = stripslashes($strNonQuery);

        // Perform the Query
        $objResult = oci_parse($this->objOracle,$strNonQuery);

        if($objResult)
        {
            if($this->commitMode) {
                oci_execute($objResult,OCI_DEFAULT);
            }
            else {
                oci_execute($objResult);
            }
        }

        if ($objOracle_error=oci_error())
        {
            throw new QOracleDatabaseException($objOracle_error['message'],$objOracle_error['code'] , null);
        }
    }

    public function GetTables() {
        $objResult = $this->Query("select table_name from tabs order by table_name");

        $strToReturn = array();
        while ($strRowArray = $objResult->FetchRow())
        {
            array_push($strToReturn, $strRowArray[0]);
        }
        return $strToReturn;
    }

    public function GetFieldsForTable($strTableName) {
        $objResult = $this->Query(sprintf("select * from user_tab_columns where table_name = '%s'",strtoupper($strTableName)));

        $objFields = array();
        while ($objRow = $objResult->FetchRow())
        {
            array_push($objFields, new QOracleDatabaseField($objRow, $this));
        }
        return $objFields;
    }

    //it returns the last inserted row for the current session
    public function InsertId($strTableName = null, $strColumnName = null) {
        $seqName = substr($strTableName,0, 25) . "_SEQ";
        $objResult = $this->Query("select "."$seqName".".currval from dual");
        $strDbRow = $objResult->FetchRow();
        return QType::Cast($strDbRow[0], QType::Integer);        
    }

    public function Close() {
        oci_close($this->objOracle);

        // Update Connected Flag
        $this->blnConnectedFlag = false;
    }

    protected function ExecuteTransactionBegin() {
        // Set to AutoCommit
        //$this->NonQuery('SET AUTOCOMMIT OFF;');
        $this->commitMode = true;
    }

    protected function ExecuteTransactionCommit() {
        /*$this->NonQuery('COMMIT;');
        // Set to AutoCommit
        $this->NonQuery('SET AUTOCOMMIT OFF;');*/
        oci_commit($this->objOracle);
    }

    protected function ExecuteTransactionRollBack() {
        /*$this->NonQuery('ROLLBACK;');
        // Set to AutoCommit
        $this->NonQuery('SET AUTOCOMMIT OFF;');*/
        oci_rollback($this->objOracle);
    }

    public function GetIndexesForTable($strTableName) {
        $objIndexArray = array();
        $objResult = $this->Query(sprintf("select ui.index_name,ui.index_type,ui.uniqueness,uc.constraint_type FROM user_indexes ui left join user_constraints uc on (ui.index_name=uc.index_name) WHERE ui.table_name= '%s'",strtoupper($strTableName)));
        while ($objRow = $objResult->FetchRow())
        {
            $ColumnNameArray = array();
            $objResult2 = $this->Query(sprintf("select * from user_ind_columns where index_name='%s' order by  column_position",$objRow[0]));
            while ($objRow2 = $objResult2->FetchRow())
            {
                array_push($ColumnNameArray, $objRow2[2]);
            }
            $blnUnique = strcmp($objRow[2],"UNIQUE")?false:true;
            $blnPrimaryKey = strcmp($objRow[3],"P")?false:true;
            $objIndex = new QDatabaseIndex($objRow[0], $blnPrimaryKey, $blnUnique, $ColumnNameArray);
            array_push($objIndexArray, $objIndex);
        }
        //SHOW The Obj Index
        // echo "<pre>";
        // print_r($objIndexArray);
        // echo "</pre>";
        //*/
            
        return $objIndexArray;
    }

    public function GetForeignKeysForTable($strTableName) {

        $objForeignKeysArray = array();
        $objResult = $this->Query(sprintf("select uc1.constraint_name,uc2.table_name,uc2.constraint_name from user_constraints uc1,user_constraints uc2 where uc1.r_constraint_name=uc2.constraint_name and uc1.constraint_type='R' and uc1.table_name='%s'",strtoupper($strTableName)));
        while ($objRow = $objResult->FetchRow())
        {
            $ColumnNameArray = array();
            $objResult2 = $this->Query(sprintf("select * from user_cons_columns where constraint_name = '%s' order by  position",$objRow[0]));
            while ($objRow2 = $objResult2->FetchRow())
            {
                array_push($ColumnNameArray, $objRow2[3]);
            }

            $ColumnNameArray2 = array();
            $objResult3 = $this->Query(sprintf("select * from user_cons_columns where constraint_name = '%s' order by  position",strtoupper($objRow[2])));
            while ($objRow3 = $objResult3->FetchRow())
            {
                array_push($ColumnNameArray2, $objRow3[3]);
            }

            $objIndex = new QDatabaseForeignKey($objRow[0], $ColumnNameArray,$objRow[1],$ColumnNameArray2);
            array_push($objForeignKeysArray, $objIndex);
        }
            
  /*    SHOW The Obj ForeingKey
   echo "<pre>";
   print_r($objForeignKeysArray);
   echo "</pre>";
   */
            
            

  return $objForeignKeysArray;
    }

    // MySql defines KeyDefinition to be [OPTIONAL_NAME] ([COL], ...)
    // If the key name exists, this will parse it out and return it
    private function ParseNameFromKeyDefinition($strKeyDefinition) {
        $strKeyDefinition = trim($strKeyDefinition);

        $intPosition = strpos($strKeyDefinition, '(');

        if ($intPosition === false)
        throw new Exception("Invalid Key Definition: $strKeyDefinition");
        else if ($intPosition == 0)
        // No Key Name Defined
        return null;

        // If we're here, then we have a key name defined
        $strName = trim(substr($strKeyDefinition, 0, $intPosition));

        // Rip Out leading and trailing "`" character (if applicable)
        if (substr($strName, 0, 1) == '`')
        return substr($strName, 1, strlen($strName) - 2);
        else
        return $strName;
    }

    // MySql defines KeyDefinition to be [OPTIONAL_NAME] ([COL], ...)
    // This will return an array of strings that are the names [COL], etc.
    private function ParseColumnNameArrayFromKeyDefinition($strKeyDefinition) {
        $strKeyDefinition = trim($strKeyDefinition);

        // Get rid of the opening "(" and the closing ")"
        $intPosition = strpos($strKeyDefinition, '(');
        if ($intPosition === false)
        throw new Exception("Invalid Key Definition: $strKeyDefinition");
        $strKeyDefinition = trim(substr($strKeyDefinition, $intPosition + 1));

        $intPosition = strpos($strKeyDefinition, ')');
        if ($intPosition === false)
        throw new Exception("Invalid Key Definition: $strKeyDefinition");
        $strKeyDefinition = trim(substr($strKeyDefinition, 0, $intPosition));

        // Create the Array
        // TODO: Current method doesn't support key names with commas or parenthesis in them!
        $strToReturn = explode(',', $strKeyDefinition);

        // Take out trailing and leading "`" character in each name (if applicable)
        for ($intIndex = 0; $intIndex < count($strToReturn); $intIndex++) {
            $strColumn = $strToReturn[$intIndex];

            if (substr($strColumn, 0, 1) == '`')
            $strColumn = substr($strColumn, 1, strpos($strColumn, '`', 1) - 1);

            $strToReturn[$intIndex] = $strColumn;
        }

        return $strToReturn;
    }

    private function ParseForIndexes($strCreateStatement) {
        // MySql nicely splits each object in a table into it's own line
        // Split the create statement into lines, and then pull out anything
        // that says "PRIMARY KEY", "UNIQUE KEY", or just plain ol' "KEY"
        $strLineArray = explode("\n", $strCreateStatement);
        $objIndexArray = array();
        // We don't care about the first line or the last line
        for ($intIndex = 1; $intIndex < (count($strLineArray) - 1); $intIndex++) {
            $strLine = $strLineArray[$intIndex];

            // Each object has a two-space indent
            // So this is a key object if any of those key-related words exist at position 2
            switch (2) {
                case (strpos($strLine, 'PRIMARY KEY')):
                    $strKeyDefinition = substr($strLine, strlen('  PRIMARY KEY '));

                    $strKeyName = $this->ParseNameFromKeyDefinition($strKeyDefinition);
                    $strColumnNameArray = $this->ParseColumnNameArrayFromKeyDefinition($strKeyDefinition);

                    $objIndex = new QDatabaseIndex($strKeyName, $blnPrimaryKey = true, $blnUnique = true, $strColumnNameArray);
                    array_push($objIndexArray, $objIndex);
                    break;

                case (strpos($strLine, 'UNIQUE KEY')):
                    $strKeyDefinition = substr($strLine, strlen('  UNIQUE KEY '));

                    $strKeyName = $this->ParseNameFromKeyDefinition($strKeyDefinition);
                    $strColumnNameArray = $this->ParseColumnNameArrayFromKeyDefinition($strKeyDefinition);

                    $objIndex = new QDatabaseIndex($strKeyName, $blnPrimaryKey = false, $blnUnique = true, $strColumnNameArray);
                    array_push($objIndexArray, $objIndex);
                    break;

                case (strpos($strLine, 'KEY')):
                    $strKeyDefinition = substr($strLine, strlen('  KEY '));

                    $strKeyName = $this->ParseNameFromKeyDefinition($strKeyDefinition);
                    $strColumnNameArray = $this->ParseColumnNameArrayFromKeyDefinition($strKeyDefinition);

                    $objIndex = new QDatabaseIndex($strKeyName, $blnPrimaryKey = false, $blnUnique = false, $strColumnNameArray);
                    array_push($objIndexArray, $objIndex);
                    break;
            }
        }

        return $objIndexArray;
    }

    private function ParseForInnoDbForeignKeys($strCreateStatement) {
        // MySql nicely splits each object in a table into it's own line
        // Split the create statement into lines, and then pull out anything
        // that starts with "CONSTRAINT" and contains "FOREIGN KEY"
        $strLineArray = explode("\n", $strCreateStatement);

        $objForeignKeyArray = array();

        // We don't care about the first line or the last line
        for ($intIndex = 1; $intIndex < (count($strLineArray) - 1); $intIndex++) {
            $strLine = $strLineArray[$intIndex];

            // Check to see if the line:
            // * Starts with "CONSTRAINT" at position 2 AND
            // * contains "FOREIGN KEY"
            if ((strpos($strLine, "CONSTRAINT") == 2) &&
            (strpos($strLine, "FOREIGN KEY") !== false)) {
                $strLine = substr($strLine, strlen('  CONSTRAINT '));
                    
                // By the end of the following lines, we will end up with a strTokenArray
                // Index 0: the FK name
                // Index 1: the list of columns that are the foreign key
                // Index 2: the table which this FK references
                // Index 3: the list of columns which this FK references
                $strTokenArray = split(' FOREIGN KEY ', $strLine);
                $strTokenArray[1] = split(' REFERENCES ', $strTokenArray[1]);
                $strTokenArray[2] = $strTokenArray[1][1];
                $strTokenArray[1] = $strTokenArray[1][0];
                $strTokenArray[2] = split(' ', $strTokenArray[2]);
                $strTokenArray[3] = $strTokenArray[2][1];
                $strTokenArray[2] = $strTokenArray[2][0];
                    
                // Cleanup, and change Index 1 and Index 3 to be an array based on the
                // parsed column name list
                if (substr($strTokenArray[0], 0, 1) == '`')
                $strTokenArray[0] = substr($strTokenArray[0], 1, strlen($strTokenArray[0]) - 2);
                $strTokenArray[1] = $this->ParseColumnNameArrayFromKeyDefinition($strTokenArray[1]);
                if (substr($strTokenArray[2], 0, 1) == '`')
                $strTokenArray[2] = substr($strTokenArray[2], 1, strlen($strTokenArray[2]) - 2);
                $strTokenArray[3] = $this->ParseColumnNameArrayFromKeyDefinition($strTokenArray[3]);
                    
                // Create the FK object and add it to the return array
                $objForeignKey = new QDatabaseForeignKey($strTokenArray[0], $strTokenArray[1], $strTokenArray[2], $strTokenArray[3]);
                array_push($objForeignKeyArray, $objForeignKey);
                    
                // Ensure the FK object has matching column numbers (or else, throw)
                if ((count($objForeignKey->ColumnNameArray) == 0) ||
                (count($objForeignKey->ColumnNameArray) != count($objForeignKey->ReferenceColumnNameArray)))
                throw new Exception("Invalid Foreign Key definition: $strLine");
            }
        }
        return $objForeignKeyArray;
    }

    private function GetCreateStatementForTable($strTableName) {
        // Use the MySQL "SHOW CREATE TABLE" functionality to get the table's Create statement
        $objResult = $this->Query(sprintf('SHOW CREATE TABLE `%s`', $strTableName));
        $objRow = $objResult->FetchRow();
        $strCreateTable = $objRow[1];
        $strCreateTable = str_replace("\r", "", $strCreateTable);
        return $strCreateTable;
    }

    private function GetTableTypeForCreateStatement($strCreateStatement) {
        // Table Type is in the last line of the Create Statement, "TYPE=DbTableType"
        $strLineArray = explode("\n", $strCreateStatement);
        $strFinalLine = strtoupper($strLineArray[count($strLineArray) - 1]);

        if (substr($strFinalLine, 0, 7) == ') TYPE=') {
            return trim(substr($strFinalLine, 7));
        } else if (substr($strFinalLine, 0, 9) == ') ENGINE=') {
            return trim(substr($strFinalLine, 9));
        } else
        throw new Exception("Invalid Table Description");
    }

    public function ExplainStatement($sql) {
        return $this->Query("EXPLAIN PLAN FOR " . $sql);
    }
}

class QOracleDatabaseException extends QDatabaseExceptionBase {
    public function __construct($strMessage, $intNumber, $strQuery) {
        parent::__construct(sprintf("Oracle Error: %s", $strMessage), 2);
        $this->intErrorNumber = $intNumber;
        $this->strQuery = $strQuery;
    }
}

/**
 * Class to handle results sent by database upon querying
 */
class QOracleDatabaseResult extends QDatabaseResultBase {
    protected $objOracleResult;
    protected $objDb;

    public function __construct($objResult, QOracleDatabase $objDb) {
        $this->objOracleResult = $objResult;
        $this->objDb = $objDb;
    }

    public function FetchArray() {
        return oci_fetch_array($this->objOracleResult);
    }

    public function FetchObject() {
        return oci_fetch_object($this->objOracleResult);
    }

    public function FetchFields() {
        return null; // not implemented
    }

    public function FetchField() {
        return null; // not implemented
    }

    public function FetchRow() {
        return oci_fetch_row($this->objOracleResult);
    }

    public function CountRows() {
        $nr_rows =sizeof(oci_fetch_array($this->objOracleResult,OCI_NUM));
        if($nr_rows >1)
        return ($nr_rows/2);//divide by 2 because it makes OCI_BOTH by default, so ir repeats the results twice
        else
        return 0;
    }

    public function CountFields() {
        return oci_num_fields($this->objOracleResult);
    }

    public function Close() {
        $this->objOracleResult->free();
    }

    public function GetNextRow() {
        $strColumnArray = $this->FetchArray();

        if ($strColumnArray)
        return new QOracleDatabaseRow($strColumnArray);
        else
        return null;
    }

    public function GetRows() {
        $objDbRowArray = array();
        while ($objDbRow = $this->GetNextRow())
        array_push($objDbRowArray, $objDbRow);
        return $objDbRowArray;
    }
}

class QOracleDatabaseRow extends QDatabaseRowBase {
    protected $strColumnArray;

    public function __construct($strColumnArray) {
        $this->strColumnArray = $strColumnArray;
    }

    /**
     * Gets the value of a column from a result row returned by the database
     *
     * @param string                  $strColumnName Name of te column
     * @param null|QDatabaseFieldType $strColumnType Data type
     *
     * @return mixed
     */
    public function GetColumn($strColumnName, $strColumnType = null) {
        if (!isset($this->strColumnArray[$strColumnName])) {
            return null;
        }
        $strColumnValue = $this->strColumnArray[$strColumnName];

        switch ($strColumnType) {
            case QDatabaseFieldType::Bit:
                // Account for single bit value
                $chrBit = $strColumnValue;
                if ((strlen($chrBit) == 1) && (ord($chrBit) == 0))
                    return false;

                // Otherwise, use PHP conditional to determine true or false
                return ($strColumnValue) ? true : false;

            case QDatabaseFieldType::Blob:
                return QType::Cast($strColumnValue->load(), QType::String);
            case QDatabaseFieldType::Char:
            case QDatabaseFieldType::VarChar:
                return QType::Cast($strColumnValue, QType::String);

            case QDatabaseFieldType::Date:
            case QDatabaseFieldType::DateTime:
            case QDatabaseFieldType::Time:
                return new QDateTime($strColumnValue);

            case QDatabaseFieldType::Float:
                return QType::Cast($strColumnValue, QType::Float);

            case QDatabaseFieldType::Integer:
                return QType::Cast($strColumnValue, QType::Integer);

            default:
                return $strColumnValue;
        }
    }

    /**
     * Tells whether a particular column exists in a returned database row
     *
     * @param string $strColumnName Name of te column
     *
     * @return bool
     */
    public function ColumnExists($strColumnName) {
        return array_key_exists($strColumnName, $this->strColumnArray);
    }
    
    public function GetColumnNameArray() {
        return $this->strColumnArray;
    }
}

class QOracleDatabaseField extends QDatabaseFieldBase {
    public function __construct($mixFieldData, $objDb = null) {
            
            
        $this->strTable =  $mixFieldData[0];
        $this->strName = $mixFieldData[1];
        $this->strType =$mixFieldData[2];
        $this->intMaxLength = $mixFieldData[5];
        $this->blnNotNull = ($mixFieldData[8]=="N")?true:false;
        $this->strDefault = $mixFieldData[11];
        $this->strOriginalTable = $this->strTable;
        $this->strOriginalName = $this->strName;
        $this->SetFieldType($mixFieldData[2]);


        /**
         *
            Implementation just like PostGreSQL Adapter
         */

          $objResult=$objDb->Query(sprintf("select position, constraint_type as type from
            user_cons_columns ucc,user_constraints uc where ucc.column_name='%s' 
            AND ucc.TABLE_NAME='%s' and ucc.TABLE_NAME=uc.TABLE_NAME and 
            uc.CONSTRAINT_NAME=ucc.CONSTRAINT_NAME and constraint_type IN ('P', 'U') order by position",$this->strOriginalName,$this->strTable));
        // TODO: unique and primary?
        // TODO: auto number (sequence)
        // TODO: not null?
        while ($mixRow = $objResult->FetchArray()) {
          if (isset($mixRow['TYPE'])) {
            if ($mixRow['TYPE'] == 'P') {
              $this->blnPrimaryKey = true;
              // auto-number
              $this->blnIdentity = true;
              break;
            }
            if ($mixRow['TYPE'] == 'U') {
              $this->blnUnique = true;
              $this->blnIdentity = false;
              $this->blnPrimaryKey = false;
            }
          }     
        }
    }

    protected function SetFieldType($OracleFieldType) {

        //Note: Info from http://download-east.oracle.com/docs/html/B10255_01/ch3.htm#1026123
        switch ($OracleFieldType) {
            case "FLOAT":
                $this->strType = QDatabaseFieldType::Float;
                break;                
            case "NUMBER":
                $this->strType = QDatabaseFieldType::Float;
                break;
            case "CHAR":
                if ($this->intMaxLength == 1)
                    $this->strType = QDatabaseFieldType::Bit;
                else
                    $this->strType = QDatabaseFieldType::Char;
                break;
            case "VARCHAR":
            case "VARCHAR2":
            case "NVARCHAR2":
            case "NCHAR":
                $this->strType = QDatabaseFieldType::VarChar;
                break;
            case "DATE":
                $this->strType = QDatabaseFieldType::DateTime;
                break;
            case "BLOB":
            case "CLOB":
            case "LONG":
            case "NCLOB":
            case "RAW":
            case "LONG RAW":
                $this->strType = QDatabaseFieldType::Blob;
                break;
            default:
                throw new Exception("Unable to determine Oracle Database Field Type: $OracleFieldType");
                break;
        }
    }
}