Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
68.00% |
17 / 25 |
CRAP | |
62.13% |
169 / 272 |
| AiOracle | |
0.00% |
0 / 1 |
|
68.00% |
17 / 25 |
573.16 | |
61.85% |
167 / 270 |
| __construct( $dbHost, $dbUser, $dbPass, $dbName ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
6 / 6 |
|||
| connect() | |
0.00% |
0 / 1 |
5.16 | |
58.33% |
7 / 12 |
|||
| query( $query, $file, $line ) | |
0.00% |
0 / 1 |
9.00 | |
75.00% |
15 / 20 |
|||
| escapeString( $string ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| blockInjection( $string ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| fetchRow( $result ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| fetchArray( $result ) | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
| fieldName( $result, $columnOffset ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| numRows( $result ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| numFields( $result ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| fieldType( $result, $columnOffset ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| simpleFieldType( $result, $columnOffset ) | |
0.00% |
0 / 1 |
18.56 | |
88.00% |
22 / 25 |
|||
| fixBoolean( $booleanValue ) | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| fixDbBoolean( $booleanValue ) | |
100.00% |
1 / 1 |
8 | |
100.00% |
12 / 12 |
|||
| insertBlank( $tableName, $fieldName, $requiredFields = FALSE ) | |
100.00% |
1 / 1 |
4 | |
100.00% |
22 / 22 |
|||
| beginTransaction( $file = false, $line = false ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| endTransaction( $file = false, $line = false ) | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| getCurrentTimestamp( $format = 'database' ) | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
| getCurrentVersion() | |
0.00% |
0 / 1 |
3.03 | |
84.62% |
11 / 13 |
|||
| getUpgradeFiles( $fromDir ) | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 16 |
|||
| validateUpgradeFiles( $files ) | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 14 |
|||
| doUpgrade( $fromVersion, $direction ) | |
0.00% |
0 / 1 |
182 | |
0.00% |
0 / 54 |
|||
| recordSchemaChange( $action, $versionNumber ) | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
| updateLob( $tableName, $pkColumn, $pkValue, $lobColumn, $newValue, $isClob = false ) | |
0.00% |
0 / 1 |
4.08 | |
82.61% |
19 / 23 |
|||
| clobToString( $clobData ) | |
100.00% |
1 / 1 |
3 | |
100.00% |
12 / 12 |
|||
| <?php | |
| /** | |
| This file is part of the APP(ideas) database abstraction project (AiDb). | |
| Copyright 2013, APPideas | |
| AiDb is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as published | |
| by the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| AiDb is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with AiDb (in the 'resources' directory). If not, see | |
| <http://www.gnu.org/licenses/>. | |
| http://appideas.com/abstract-your-database-introduction | |
| */ | |
| require_once( "base/AiDb.php" ); | |
| require_once( "util/AiUtil.php" ); | |
| /** | |
| * An implementation of the abstraction of the Oracle database engine | |
| * | |
| * @package Ai_DatabaseAbstraction | |
| */ | |
| class AiOracle implements AiDb | |
| { | |
| /** | |
| * Database host name | |
| * @var string | |
| */ | |
| protected $mDbHost; | |
| /** | |
| * Database username | |
| * @var string | |
| */ | |
| protected $mDbUser; | |
| /** | |
| * Database password | |
| * @var string | |
| */ | |
| protected $mDbPass; | |
| /** | |
| * Database schema name | |
| * @var string | |
| */ | |
| protected $mDbName; | |
| /** | |
| * Resource created by the database connection | |
| * @var resource | |
| */ | |
| public $mConnectionResource; | |
| /** | |
| * The name of the table that holds the schema version update history | |
| * @var string | |
| */ | |
| protected $mVersionsTable = "schema_versions"; | |
| /** | |
| * The number of results that the previous query returned. | |
| * oci8 doesn't provide an accurate answer with num_rows(), so we need to track the row counf when a query is performed | |
| * See Notes here: http://www.php.net/manual/en/function.oci-num-rows.php | |
| * @var int | |
| */ | |
| protected $mResultCount; | |
| /** | |
| * Class constructor. | |
| * | |
| * Setup parameters through AiCommon and access the database through that class' instance of this class | |
| * | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $dbHost The database host name | |
| * @param string $dbUser The database username | |
| * @param mixed $dbPass The database password | |
| * @param string $dbName The name of the database | |
| */ | |
| public function __construct( $dbHost, $dbUser, $dbPass, $dbName ) | |
| { | |
| $this->mDbHost = $dbHost; | |
| $this->mDbUser = $dbUser; | |
| $this->mDbPass = $dbPass; | |
| $this->mDbName = $dbName; | |
| $this->mResultCount = 0; | |
| } // Constructor | |
| /** | |
| * Connect to the database and set the member connection resource | |
| * | |
| * @return void | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| */ | |
| public function connect() | |
| { | |
| try | |
| { | |
| $this->mConnectionResource = oci_connect( $this->mDbUser, $this->mDbPass, "(DESCRIPTION=(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = " . $this->mDbHost . ")(PORT = 1521)))(CONNECT_DATA=(SID=" . $this->mDbName . ")))"); | |
| $e = oci_error(); | |
| if( $e ) | |
| { | |
| echo "ERROR: " . $e['message'] . "\n"; | |
| } | |
| if( !$this->mConnectionResource ) | |
| { | |
| $dbError = oci_error(); | |
| throw new Exception( "CONNECTION ERROR: Could not connect to the Oracle database. Check your connection parameters in base/Common.php. Oracle said: " . $dbError['message'] ); | |
| } | |
| } | |
| catch( Exception $exception ) | |
| { | |
| // if we could not connect, halt execution and display an error | |
| throw new Exception( $exception->getMessage() ); | |
| } | |
| } // connect() | |
| /** | |
| * Perform a query of the database. | |
| * | |
| * This does not assume that strings have been escaped, so be sure to escape them. | |
| * | |
| * Use the PHP macros __FILE__ and __LINE__ for the 2nd and 3rd parameters. | |
| * | |
| * @return resource | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $query The query to perform | |
| * @param string $file The file making the query | |
| * @param int $line The line number in the querying file | |
| */ | |
| public function query( $query, $file, $line ) | |
| { | |
| $this->mResultCount = 0; | |
| // try a successful query | |
| try | |
| { | |
| $statement = oci_parse( $this->mConnectionResource, $query ); | |
| @$result = oci_execute( $statement ); | |
| if( !$result ) | |
| { | |
| $dbError = oci_error( $statement ); | |
| echo "\n\n" . $query . "\n\n"; | |
| throw new Exception( "QUERY ERROR: In file " . $file . ", line number " . $line . ", Oracle said: " . $dbError['message'] . "\n\nThe query was: \n" . $query . "\n" ); | |
| } | |
| // the return value is only hit on no exception | |
| // set the result count variable for select statments | |
| if( eregi( 'select ', $query ) && !eregi( '^create', trim( $query ) ) | |
| && !eregi( '^insert', trim( $query ) ) && !eregi( '^update', trim( $query ) ) | |
| && !eregi( '^delete', trim( $query ) ) ) | |
| { | |
| $queryArray = array(); | |
| $this->mResultCount = oci_fetch_all( $statement, $queryArray ); | |
| $statement = oci_parse( $this->mConnectionResource, $query ); | |
| @$result = oci_execute( $statement ); | |
| } | |
| return $statement; | |
| } | |
| catch( Exception $exception ) | |
| { | |
| // show an error and halt execution. This is OK in Oracle - if we are in a transaction, | |
| // a die before the END or COMMIT block causes an implicit ROLLBACK | |
| throw new Exception( $exception->getMessage() ); | |
| } | |
| } // query() | |
| /** | |
| * Escape a string in a manner that the chosen DBMS can handle | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $string The string to escape | |
| */ | |
| public function escapeString( $string ) | |
| { | |
| return ereg_replace( "'", "''", stripslashes( $string ) ); | |
| } // escapeString | |
| /** | |
| * Returns any part of a query before a semi-colon | |
| * | |
| * If there is an attempt of an injection attack, or if your string input legitimately has semicolons, | |
| * this is likely to cause truncated input. | |
| * | |
| * If your input may contain semicolons, be sure that you trust the source before allowing it to perfom | |
| * a query. | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $string The string to de-inject | |
| */ | |
| public function blockInjection( $string ) | |
| { | |
| $split = preg_split( "/;/", $string ); | |
| return $split[0]; | |
| } | |
| /** | |
| * Fetch a row form the database | |
| * | |
| * @return array | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| */ | |
| public function fetchRow( $result ) | |
| { | |
| return oci_fetch_row( $result ); | |
| } // fetchRow() | |
| /** | |
| * Fetch a row form the database and return it as an indexed array and an associative array with field names as indexes. | |
| * | |
| * For consistency, returned keys are cast to lower case | |
| * | |
| * @return array | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| */ | |
| public function fetchArray( $result ) | |
| { | |
| $returnValue = oci_fetch_array( $result ); | |
| // The final attempt at fetch_array returns false when all records are retrieved in a loop, | |
| // but array_change_key_case doesn't like that | |
| if( is_array( $returnValue ) ) | |
| { | |
| // convert all keys to lower case | |
| $returnValue = array_change_key_case( $returnValue ); | |
| } | |
| return $returnValue; | |
| } // fetchArray() | |
| /** | |
| * Get the name of a field at the given offset | |
| * | |
| * To be consistent with the "normal" abstractions, field offset is converted to 0-based, whereas oci_field_name is oddly 1-based | |
| * Results are also cast to lower case | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| * @param int $columnOffset The offset of the requested column (0 is the first column) | |
| */ | |
| public function fieldName( $result, $columnOffset ) | |
| { | |
| return strtolower( oci_field_name( $result, ($columnOffset + 1) ) ); | |
| } // fieldName() | |
| /** | |
| * The number of rows returned by a query | |
| * | |
| * @return int | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| */ | |
| public function numRows( $result ) | |
| { | |
| return $this->mResultCount; | |
| } // numRows() | |
| /** | |
| * The number of fields returned by a query | |
| * | |
| * @return int | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| */ | |
| public function numFields( $result ) | |
| { | |
| return oci_num_fields( $result ); | |
| } // numFields() | |
| /** | |
| * The type of field at the given offset | |
| * | |
| * To be consistent with the "normal" abstractions, field offset is converted to 0-based, whereas oci_field_name is oddly 1-based | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| * @param int $columnOffset The offset of the requested column (0 is the first column) | |
| */ | |
| public function fieldType( $result, $columnOffset ) | |
| { | |
| return strtolower( oci_field_type( $result, ($columnOffset + 1) ) ); | |
| } // fieldType() | |
| /** | |
| * A uniform type of field at the given offset. | |
| * | |
| * Returns a string that represents a simplified field type for consistency across DBMS' | |
| * | |
| * We don't check every field type. This has been written for the field types that APP(ideas) uses commonly. It will probably need modification to suit the needs of others. | |
| * | |
| * This globs a lot of different types together. You may need to make them more fine-grained | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param resource $result The conenction resource returned by a query | |
| * @param int $columnOffset The offset of the requested column (0 is the first column) | |
| */ | |
| public function simpleFieldType( $result, $columnOffset ) | |
| { | |
| $rawType = strtolower( oci_field_type( $result, ($columnOffset + 1) ) ); | |
| switch( $rawType ) | |
| { | |
| case "number": | |
| case "numeric": | |
| case "dec": | |
| case "decimal": | |
| case "double": | |
| case "float": | |
| case "int": | |
| case "integer": | |
| case "long": | |
| case "smallint": | |
| return "numeric"; | |
| break; | |
| case "date": | |
| case "timestamp": | |
| return "date"; | |
| break; | |
| case "varchar2": | |
| case "varchar": | |
| case "char": | |
| return "string"; | |
| break; | |
| case "clob": | |
| case "blob": | |
| return "binary"; | |
| break; | |
| default: | |
| return "unknown"; | |
| break; | |
| // No native boolean type for Oracle/oci8 | |
| } | |
| } // simpleFieldType | |
| /** | |
| * Convert a database-formatted boolean value into something consistent with PHP | |
| * | |
| * For Oracle, this assumes that the boolean field is an int that will contain either 1 or 0 | |
| * | |
| * @return bool | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $booleanValue A boolean value retrieved from a database query | |
| */ | |
| public function fixBoolean( $booleanValue ) | |
| { | |
| if( 1 === (int) strtolower( $booleanValue ) ) | |
| { | |
| return TRUE; | |
| } | |
| else | |
| { | |
| return FALSE; | |
| } | |
| } // fixBoolean() | |
| /** | |
| * Fixes a boolean input value of any kind to one understood by the DBMS | |
| * | |
| * For Oracle, this returns a 1 or 0 | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $booleanValue A boolean value retrieved from a database query | |
| */ | |
| public function fixDbBoolean( $booleanValue ) | |
| { | |
| $returnValue = 1; | |
| if( (strlen( $booleanValue ) < 1) || | |
| (0 === $booleanValue) || | |
| ('0' === $booleanValue) || | |
| (false === $booleanValue) || | |
| ("false" === strtolower( $booleanValue )) || | |
| ("f" === strtolower( $booleanValue )) || | |
| (NULL === $booleanValue) ) | |
| { | |
| $returnValue = 0; | |
| } | |
| return $returnValue; | |
| } // end fixDbBoolean | |
| /** | |
| * Inserts a blank record into the requested table and returns the value of the surrogate key of the new record. | |
| * | |
| * This allows us to use a "modify" method for saving new data rather than separate "add" and "modify" | |
| * | |
| * If one of your "requiredFields" is a string, you must enclose it in apostrophes on input | |
| * | |
| * @return int | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $tableName The name of the table into which we are inserting | |
| * @param string $fieldName The name of the field into which we are inserting | |
| * @param mixed $requiredFields An array of other fields and values that must not be null on a new record insert | |
| */ | |
| public function insertBlank( $tableName, $fieldName, $requiredFields = FALSE ) | |
| { | |
| if( !$requiredFields || !is_array( $requiredFields ) ) | |
| { | |
| $insertQuery = " | |
| INSERT | |
| INTO " . $tableName . " ( " . $fieldName . " ) | |
| VALUES ( DEFAULT ) | |
| "; | |
| } | |
| else | |
| { | |
| $fields = ""; | |
| $values = ""; | |
| foreach( $requiredFields as $field => $value ) | |
| { | |
| $fields .= ", " . $field; | |
| $values .= ", " . $value; | |
| } | |
| $insertQuery = " | |
| INSERT | |
| INTO " . $tableName . " ( " . $fieldName . $fields . " ) | |
| VALUES ( DEFAULT" . $values . " ) | |
| "; | |
| } | |
| // This is not an ideal way to get the last inserted ID, but there's no generic way around it as long as | |
| // there exists no standard naming convention (automatic sequence table creation by Oracle) for sequences | |
| // in Oracle. A better query would be: | |
| // SELECT <sequence name>.CURRVAL FROM DUAL; | |
| // If you have a solid naming scheme for sequence fields worked out, tak a look at base/AiPgSQL for an example implementation | |
| $returnQuery = "SELECT MAX( " . $fieldName . " ) FROM " . $tableName; | |
| $this->beginTransaction( __FILE__, __LINE__ ); | |
| $this->query( $insertQuery, __FILE__, __LINE__ ); | |
| $result = $this->query( $returnQuery, __FILE__, __LINE__ ); | |
| $this->endTransaction( __FILE__, __LINE__ ); | |
| $row = $this->fetchRow( $result ); | |
| $returnValue = $row[0]; | |
| return $returnValue; | |
| } // insertBlank() | |
| /** | |
| * Begins a database transaction if supported by the DBMS | |
| * | |
| * There is no explicit "transaction begin" in OCI8. Transactions are commited either automatically or through oci_commit(); | |
| * | |
| * @return void | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $file The file making the query. Leave empty to report the DB connector file name. | |
| * @param int $line The line number in the querying file. Leave empty to report the DB connector line number. | |
| */ | |
| public function beginTransaction( $file = false, $line = false ) | |
| { | |
| return; | |
| } // beginTransaction() | |
| /** | |
| * Ends (commits) a database transaction if supported by the DBMS | |
| * | |
| * @return void | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $file The file making the query. Leave empty to report the DB connector file name. | |
| * @param int $line The line number in the querying file. Leave empty to report the DB connector line number. | |
| */ | |
| public function endTransaction( $file = false, $line = false ) | |
| { | |
| oci_commit( $this->mConnectionResource ); | |
| } // endTransaction() | |
| /** | |
| * Retrieves a value that can be inserted into the database as a date or timestamp indicating the current date and/or time | |
| * | |
| * For oracle, with the exception of the default option, you will probably need to wrap the results in a TO_DATE function | |
| * | |
| * The default return form is "timestamp without timezone" | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $format The format of the string to return. One of 'database' 'epoch' or a PHP date() format. The default is 'database' | |
| */ | |
| public function getCurrentTimestamp( $format = 'database' ) | |
| { | |
| $returnString = "TO_DATE( '" . date( "Y-m-d H:i:s" ) . "', 'yyyy-mm-dd hh24:mi:ss' )"; | |
| if( "database" === $format ) | |
| { | |
| // Do nothing. This is the default | |
| } | |
| else if( "epoch" === $format ) | |
| { | |
| $returnString = time(); | |
| } | |
| else | |
| { | |
| $returnString = date( $format ); | |
| } | |
| return $returnString; | |
| } // end getCurrentTimestamp | |
| /** | |
| * Returns -1 if the versions table cannot be found, the current version number otherwise | |
| * | |
| * @return int | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| */ | |
| public function getCurrentVersion() | |
| { | |
| $returnValue = 0; | |
| $sql = "SELECT count(*) FROM user_tables WHERE LOWER( table_name ) = '" . strtolower( $this->mVersionsTable ) . "'"; | |
| $result = $this->query( $sql, __FILE__, __LINE__ ); | |
| $row = $this->fetchRow( $result ); | |
| if( $row[0] < 1 ) | |
| { | |
| $returnValue = -1; | |
| } | |
| else | |
| { | |
| $sql = "SELECT MAX( version_number ) FROM " . $this->mVersionsTable; | |
| $result = $this->query( $sql, __FILE__, __LINE__ ); | |
| $row = $this->fetchRow( $result ); | |
| $returnValue = (is_numeric( $row[0] )) ? $row[0] : 0; | |
| } | |
| return $returnValue; | |
| } | |
| /** | |
| * Retrieves a list of all available files for possible upgrade/downgrade of the database schema. | |
| * | |
| * Will echo an error and exit if file validation does not pass | |
| * | |
| * @return array | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $fromDir The input directory | |
| */ | |
| public function getUpgradeFiles( $fromDir ) | |
| { | |
| $returnValue = array(); | |
| if( !is_dir( $fromDir ) ) | |
| { | |
| return $returnValue; | |
| } | |
| $dh = opendir( $fromDir ); | |
| while( false !== ($file = readdir( $dh )) ) | |
| { | |
| if( 1 === preg_match( "/\.php$/", $file ) ) | |
| { | |
| $returnValue[] = $file; | |
| } | |
| } | |
| sort( $returnValue, SORT_NUMERIC ); | |
| // get out of here if the upgrade files are not in a valid sequence | |
| if( !$this->validateUpgradeFiles( $returnValue ) ) | |
| { | |
| exit(); | |
| } | |
| return $returnValue; | |
| } | |
| /** | |
| * Makes sure there are no skips in the version numbers | |
| * | |
| * @return bool | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param array $files The retrieved files | |
| */ | |
| public function validateUpgradeFiles( $files ) | |
| { | |
| $returnValue = true; | |
| $files = AiUtil::mustBeArray( $files ); | |
| $lastFileName = 0; | |
| foreach( $files as $index => $fileName ) | |
| { | |
| $split = preg_split( "/\./", $fileName ); | |
| if( (int) $split[0] !== $lastFileName + 1 ) | |
| { | |
| echo "File name: " . $fileName . " represents a skip from number " . $lastFileName . ". The upgrade cannot continue.\n"; | |
| $returnValue = false; | |
| $lastFileName = $split[0]; | |
| } | |
| else | |
| { | |
| $lastFileName++; | |
| } | |
| } | |
| return $returnValue; | |
| } | |
| /** | |
| * Performs an upgrade to the latest version or downgrade a single version | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param int $fromVersion The user's current schema version | |
| * @param string $direction "up" for an upgrade or "down" for a downgrade | |
| */ | |
| public function doUpgrade( $fromVersion, $direction ) | |
| { | |
| $files = $this->getUpgradeFiles( $_ENV["PWD"] . "/data/oracle" ); | |
| // make sure there are files and the input directory exists | |
| if( count( $files ) < 1 ) | |
| { | |
| return "Either there were no files in the updates/data/oracle directory or the directory was not found."; | |
| } | |
| $lastFile = end( $files ); | |
| $split = preg_split( "/\./", $lastFile ); | |
| $lastVersion = $split[0]; | |
| // exit if we can't or don't need to up/downgrade | |
| if( "up" === $direction && $fromVersion >= $lastVersion ) | |
| { | |
| return "Already up to date."; | |
| } | |
| else if( "down" === $direction && $fromVersion > $lastVersion ) | |
| { | |
| return "I do not have a file that is late enought to perform this downgrade. Your version is " . $fromVersion . " and the latest downgrade file is " . $lastFile; | |
| } | |
| if( "up" === $direction ) | |
| { | |
| echo "Upgrading from version " . $fromVersion . " to " . $lastVersion . "...\n"; | |
| // Run the updates | |
| foreach( $files as $index => $fileName ) | |
| { | |
| if( ($index + 1) > $fromVersion ) | |
| { | |
| $split = preg_split( "/\./", $fileName ); | |
| echo "Upgrading to version " . $split[0] . "...\n"; | |
| require_once( $_ENV["PWD"] . "/data/oracle/" . $fileName ); | |
| $className = DbUpdater . $split[0]; | |
| $update = new $className(); | |
| $sqlArray = $update->upgrade(); | |
| $this->beginTransaction( __FILE__, __LINE__ ); // cause an explicit rollback if any one of the intermediate queries fails | |
| foreach( $sqlArray as $index => $sql ) | |
| { | |
| echo $sql . "\n"; | |
| $this->query( $sql, __FILE__, __LINE__ ); | |
| } | |
| $this->recordSchemaChange( "add", $split[0] ); | |
| $this->endTransaction( __FILE__, __LINE__ ); | |
| } // if( ($index + 1) > $fromVersion ) ) | |
| } // foreach( $files as $index => $fileName ) | |
| } | |
| else if( "down" === $direction ) // run a downgrade | |
| { | |
| echo "Downgrading version " . $fromVersion . "...\n"; | |
| if( $fromVersion > 0 ) | |
| { | |
| require_once( $_ENV["PWD"] . "/data/oracle/" . $fromVersion . ".php" ); | |
| $className = DbUpdater . $fromVersion; | |
| $update = new $className(); | |
| $sqlArray = $update->downgrade(); | |
| $this->beginTransaction( __FILE__, __LINE__ ); // cause an explicit rollback if any one of the intermediate queries fails | |
| foreach( $sqlArray as $index => $sql ) | |
| { | |
| echo $sql . "\n"; | |
| $this->query( $sql, __FILE__, __LINE__ ); | |
| } | |
| $this->recordSchemaChange( "delete", $fromVersion ); | |
| $this->endTransaction( __FILE__, __LINE__ ); | |
| } | |
| } | |
| return "\n" . ucfirst( $direction ) . "grade complete."; | |
| } | |
| /** | |
| * Records a schema change in the database | |
| * | |
| * @return void | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $action The action to perform. Either "add" or "delete" | |
| * @param int $versionNumber The version number to record | |
| */ | |
| public function recordSchemaChange( $action, $versionNumber ) | |
| { | |
| // get out of here if the action isn't "add" or "delete" | |
| if( !in_array( strtolower( $action ), array( "add", "delete" ) ) ) | |
| { | |
| return; | |
| } | |
| // Make the query | |
| $sql = "INSERT INTO " . $this->mVersionsTable . " ( version_number ) VALUES( " . AiUtil::MustBeNumber( $versionNumber ) . " )"; | |
| // overwrite the query if this is a downgrade | |
| if( strtolower( "delete" ) === $action ) | |
| { | |
| $sql = "DELETE FROM " . $this->mVersionsTable . " WHERE version_number = " . AiUtil::MustBeNumber( $versionNumber ); | |
| } | |
| $this->query( $sql, __FILE__, __LINE__ ); | |
| } | |
| /** | |
| * Updates a BLOB or CLOB by primary key. | |
| * | |
| * @return void | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param string $tableName The name of the table | |
| * @param string $pkColumn Primary key column name | |
| * @param mixed $pkValue The value of the PK | |
| * @param string $lobColumn The column being updated | |
| * @param mixed $newValue The value to update | |
| * @param bool $isClob BLOB if FALSE, CLOB if true | |
| */ | |
| public function updateLob( $tableName, $pkColumn, $pkValue, $lobColumn, $newValue, $isClob = false ) | |
| { | |
| if( $isClob ) | |
| { | |
| $type = "CLOB"; | |
| $ociType = OCI_B_CLOB; | |
| } | |
| else | |
| { | |
| $type = "BLOB"; | |
| $ociType = OCI_B_BLOB; | |
| } | |
| $statement = oci_parse( $this->mConnectionResource, " | |
| UPDATE | |
| $tableName | |
| SET | |
| $lobColumn = EMPTY_{$type}() | |
| WHERE | |
| $pkColumn = :pk_value | |
| RETURNING | |
| $lobColumn INTO :lob | |
| "); | |
| oci_bind_by_name( $statement, ':pk_value', $pkValue ); | |
| $lob = oci_new_descriptor( $this->mConnectionResource, OCI_D_LOB ); | |
| oci_bind_by_name( $statement, ':lob', $lob, -1, $ociType ); | |
| if( !oci_execute( $statement, OCI_DEFAULT ) ) | |
| { | |
| throw new Exception( "Could not update LOB " . $lobColumn . ": " . oci_error( $statement ) ); | |
| } | |
| if( !$lob->save( $newValue ) ) | |
| { | |
| throw new Exception( "Error writing to LOB.") ; | |
| } | |
| $lob->free(); | |
| oci_free_statement( $statement ); | |
| } | |
| /** | |
| * Takes the data from an Oracle CLOB and returns a string | |
| * | |
| * @return string | |
| * @since Version 20120328 | |
| * @author Version 20120328, costmo | |
| * @param object $clobData | |
| */ | |
| public function clobToString( $clobData ) | |
| { | |
| $returnValue = ""; | |
| if( is_object( $clobData ) ) | |
| { | |
| $clobData->rewind(); | |
| if( $clobData->size() > 0 ) | |
| { | |
| $returnValue = $clobData->read( $clobData->size() ); | |
| } | |
| else | |
| { | |
| $returnValue = ''; | |
| } | |
| } | |
| else | |
| { | |
| $returnValue = $clobData; | |
| } | |
| return $returnValue; | |
| } | |