<?php

defined('WikyBlog') or die("Not an entry point...");


/*

TO DO
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	* need table prefixes!
	
USEFULL QUERIES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
http://dev/manuals/mysql/manual_Reference.html#ALTER_TABLE

	ALTER [IGNORE] TABLE tbl_name alter_specification [, alter_specification ...]
	
	ALTER TABLE --tbl_name-- ...
	
		----COLUMNS----
		... ADD COLUMN --create_definition-- [FIRST | AFTER column_name ]
		... MODIFY COLUMN --col_name--  --create_definition-- [FIRST | AFTER column_name ]
		... DROP COLUMN --col_name--
	
		
		----INDEXES----
		... ADD INDEX --index_name-- (--index_col_name--,...)
		... ADD PRIMARY KEY (--index_col_name--,...)
		... ADD UNIQUE --index_name-- (--index_col_name--,...)
		... DROP PRIMARY KEY
		... DROP INDEX --index_name--

		----COLLATION---
		... COLLATE --collation--
		
*/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//		Update Database
//	

class dbAlter{
	var $tablePrefix;
	var $done = true;
	var $MySQL41 = false;
	var $charSets = array();
	
	function dbAlter(){
		$query = 'SHOW VARIABLES LIKE "VERSION"';
		$result = wbDB::runQuery($query);
		$row = mysql_fetch_row($result);
		if( version_compare($row[1],'4.1','>=') ){
			$this->MySQL41 = true;
			
			$query = 'SHOW CHARACTER SET';
			$result = wbDB::runQuery($query);
			while($row = mysql_fetch_assoc($result)){
				$this->charSets[$row['Charset']] = true;
			}
		}
	}
	
	////////////////////////////////////////////////////////////
	function go($tablePrefix='',$dbData=false){
		$dropTables = false; //This will probably never be needed
		$this->tablePrefix = $tablePrefix;
		$unchanged = array();
		
		if( $this->MySQL41 ){
			message('MySQL 4.1 or greater was found.');
		}
		if( !$dbData ){
			require('installSQL.php');
		}
		
		if( is_string($dbData) ){
			$newDB = unserialize($dbData);
		}elseif( is_array($dbData) ){
			$newDB =& $dbData;
		}
		if(empty($newDB)){
			trigger_error('New Database value are empty');
			return;
		}
		
		
		//get current database config
		$currDB = getDatabaseArray($tablePrefix);
		if( $currDB === $newDB ){
			message('Database has not changed (1).');
			return $this->done;
			return returnMessages(false);
		}
	
		// include_once('tool/error.php');
		// message('curr: '.showArray($currDB));
		// message('new: '.showArray($newDB));	
		
		//Go Through newDB
		//checkPrefix
		foreach($newDB as $tableName => $newTable){
			if( isset($currDB[$tableName]) ){
				$this->table($currDB[$tableName],$newTable,$tableName);
				unset($currDB[$tableName]);
				
			}else{
				$this->createTable($tableName,$newTable);
			}
		}
		
		//Any tables to DROP
		foreach($currDB as $tableName => $currTable){
			if($dropTables){
				$query = 'DROP TABLE `'.$this->tablePrefix.$tableName.'`';
				$this->query($query);
			}else{
				$unchanged[] = $tableName;
				//message('Found a table that is not part of this software: '.$tableName);
			}
		}
		if( count($unchanged) > 0){
			message('The following tables were not changed: '.implode($unchanged,', ').'.');
		}
		return $this->done;
	}
	
	function createTable(&$tableName,&$newTable){
		
		$query = 'CREATE TABLE `'.$this->tablePrefix.$tableName.'` ';
			$query .= '(';
			$query .= implode(', ',$newTable['columns']);
			if( isset($newTable['keys']) && is_array($newTable['keys']) ){
				$query .= ', ';
				$query .= implode(', ',$newTable['keys']);
			}
			$query .= ')';
		
		if( $this->MySQL41 ){
			$query .= ' ENGINE='.$newTable['Engine'];
			if( isset($this->charSets[$newTable['Default_charset']]) ){
				$query .= ' DEFAULT CHARSET='.$newTable['Default_charset'];
			}
		}else{
			$query .= ' TYPE='.$newTable['Engine'];
		}
		
		$this->query($query);
	}
	
	////////////////////////////////////////////////////////////
	function table(&$currTable,&$newTable,$tableName){
		if($currTable === $newTable){
			//message('Table has not changed: '.$tableName);
			return;
		}
		
		//In case keys or columns are not set for one of the arrays
		$doKeys = false;
		$empty=array();
		$empty['columns'] = array();
		$empty['keys'] = array();
		$queries = array();
		
		$currTable = $currTable + $empty;
		$newTable = $newTable + $empty;
		
		//drop keys first
		if( $currTable['keys'] !== $newTable['keys']){
			$doKeys = true;
			$this->dropKeys($currTable['keys'],$newTable['keys'],$queries);
		}
		
		//check each column
		if( $currTable['columns'] === $newTable['columns']){
			message($tableName.' :: Columns have not changed');
		}else{
			$this->columns($currTable['columns'],$newTable['columns'],$queries);
		}
		
		//add columns last
		if( $doKeys ){
			$this->addKeys($currTable['keys'],$newTable['keys'],$queries);
		}
		
		// character set and collation
		$temp = '';
		if( $this->MySQL41 && isset($this->charSets[$newTable['Default_charset']]) ){
			
			if( $currTable['Default_charset'] !== $newTable['Default_charset']){
				$temp .= 'DEFAULT CHARACTER SET '.$newTable['Default_charset'];
			}
			if( $currTable['Collation'] !== $newTable['Collation']){
				$temp .= ' COLLATE '.$newTable['Collation'];
			}
			
		}
		
		if( !empty($temp) ){
			$queries[] = $temp;
		}
		
		// message(showArray($queries));
		// message(implode(' , ',$queries));
		//$query = implode(' , ',$queries);
		if( count($queries) < 1){
			message('<b>Notice:</b> No differences found for table <tt>'.$tableName.'</tt>');
			// message('Curr: '. showArray($currTable));
			// message('new: '. showArray($newTable));
			return;
		}
		
		$this->alterQuery($queries,$tableName);
		return;
		
	}
	
	
	////////////////////////////////////////////////////////////
	// 	ALTER TABLE --tbl_name-- ...
	// 	... ADD INDEX --index_name-- (--index_col_name--,...)
	// 	... ADD PRIMARY KEY (--index_col_name--,...)
	// 	... ADD UNIQUE --index_name-- (--index_col_name--,...)
	//
	function addKeys(&$currKeys,&$newKeys,&$queries){
		$diff = array_diff_assoc($newKeys,$currKeys);
		//message('add keys: '.showArray($diff));
		foreach($diff as $sql){
			$queries[] = ' ADD '.$sql;
		}
	}
		
	////////////////////////////////////////////////////////////
	// 	ALTER TABLE --tbl_name-- ...
	// 	... DROP PRIMARY KEY
	// 	... DROP INDEX --index_name--
	//
	function dropKeys(&$currKeys,&$newKeys,&$queries){
		$diff = array_diff_assoc($currKeys,$newKeys);
		//message('drop keys: '.showArray($test));
		
		foreach($diff as $keyName => $sql){
			if( $keyName === 'PRIMARY'){
				$queries[] = 'DROP PRIMARY KEY ';
			}else{
				$queries[] = 'DROP INDEX `'.$keyName.'`';
			}
		}
	}
	
	////////////////////////////////////////////////////////////
	// 	ALTER TABLE --tbl_name-- ...
	//
	// 	... ADD COLUMN --create_definition-- [FIRST | AFTER column_name ]
	// 	... MODIFY COLUMN --create_definition-- [FIRST | AFTER column_name ]	//MODIFY is an Oracle extension
	//	... CHANGE COLUMN --col_name-- --create_definition-- [FIRST | AFTER column_name ] 				//MySQL extension to SQL-92.
	// 	... DROP COLUMN --col_name--
	//
	function columns(&$currColumns,&$newColumns,&$queries){
		//$unChanged = array();
		$where = ' FIRST';
		
		//go through newColumns
		foreach($newColumns as $columnName => $sql){
			$query = '';
			if( isset($currColumns[$columnName])  ){
				if( $sql === $currColumns[$columnName]){
					//$unChanged[] = $columnName;
				}else{
					//$queries[] = 'CHANGE COLUMN `'.$columnName.'` '.$sql;
					$queries[] = 'MODIFY '.$sql.$where;
				}
				
				unset($currColumns[$columnName]);
			}else{
				$queries[] = 'ADD COLUMN '.$sql.$where;
			}
			$where = ' AFTER `'.$columnName.'` ';
		}
		// if( count($unChanged) > 0 ){
		// 	message('Unchanged Columns: '.implode(', ',$unChanged));
		// }
		
		//Add Any Columns that weren't in $currColumns
		foreach($currColumns as $columnName => $sql){
			$query = 'DROP COLUMN `'.$columnName.'`';
			$queries[] = $query;
		}
	}
	
	//Could even send 
	//
	function alterQuery($query,&$tableName){
		$alter = 'ALTER TABLE `'.$this->tablePrefix.$tableName.'` ';
		if( is_array($query) ){
			$alter .= implode(' , ',$query);
		}else{
			$alter .= $query;
		}
		
		profile('numQueries',1);
		$bool = mysql_query($alter);
		if( !$bool ){
			$this->done = false;
			$mess = '<br/> &nbsp; -- <b>Update Warning: </b>Query did not execute correctly: '.mysql_error();
		}else{
			$mess = '<br/> &nbsp; -- query executed successfully.';
		}
		//message('Table has changed: '.$tableName.' <br/> &nbsp; '.$alter.$mess);
		message($alter.$mess);
	}
	
	////////////////////////////////////////////////////////////
	function query(&$query){
		profile('numQueries',1);
		
		//mysql_query will return true/false for 
		$bool = mysql_query($query);
		if( !$bool ){
			$this->done = false;
			message('<b>Update Warning: </b>Query did not execute correctly: '.$query.'<p>'.mysql_error().'</p>');
		}else{
			message($query . '<br/> &nbsp; -- query executed successfully.');
		}
	}
}

//
//		Update Database
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//		getDatabaseArray()
//
//		Get an Array representing the database
//		$array[--tableName--] = array()
//
//		$array[--tableName--]['columns'] = array()
//		$array[--tableName--]['keys'] = array()
//
//		$array[--tableName--]['columns'][--columnName--] = sql
//		$array[--tableName--]['keys'][--keyName--] = sql
//
//		--- needs to work with collations
//
//
//		
//

function getDatabaseArray($tablePrefix='',$skipTables=false,$useTables=false){
	global $dbname;
	$dataArray = array();
	
	////////////////////////////////////////////////////////////////////////////////
	//
	//		Tables
	//		echo '<h2>SHOW TABLES</h2>';
	
	//$collation = getDbCollation($dbname);
	//message($collation);
	
	if( is_array($skipTables) ){
		$skipTables = array_flip($skipTables);
	}elseif(is_array($useTables) ){
		$useTables = array_flip($useTables);
	}
	
	$query = 'SHOW TABLE STATUS ';
	if( !empty($tablePrefix) ){
		$query .= ' LIKE "'.wbDB::like($tablePrefix).'%"';
	}
	$result = wbDB::runQuery($query);
	//showRows($result);
	while($row = mysql_fetch_assoc($result)){
		$table = $row['Name'];
		$table = substr($table,strlen($tablePrefix));
		
		if( is_array($skipTables) ){
			if( isset($skipTables[$table]) ){
				//message('skip: '.$table);
				continue;
			}
		}elseif( is_array($useTables) ){
			if( !isset($useTables[$table]) ){
				continue;
			}
		}
		
		//message('add: '.$table);
		$dataArray[$table] = array();
		if( isset($row['Engine']) ){
			$dataArray[$table]['Engine'] = $row['Engine'];
		}
		if( isset($row['Collation']) ){
			$dataArray[$table]['Collation'] = $row['Collation'];
			$pos = strpos($row['Collation'],'_');
			$dataArray[$table]['Default_charset'] = substr($row['Collation'],0,$pos);
		}
	}
	
	////////////////////////////////////////////////////////////////////////////////
	//
	//		Columns
	//		echo '<h2>SHOW COLUMNS/ DESCRIBE</h2>';
	//
	
	foreach($dataArray as $table => $null){
		//	SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE wild]
		//  $query = 'DESCRIBE `'.$tablePrefix.$table.'`';
		$query = 'SHOW FULL COLUMNS FROM `'.$tablePrefix.$table.'`';
		$result = wbDB::runQuery($query);
		$prev=null;
		
		$textTypes = array(	'blob'=>true,
						'tinyblob'=>true,
						'mediumblob'=>true,
						'longblob'=>true,
						'text'=>true,
						'tinytext'=>true,
						'mediumtext'=>true,
						'longtext'=>true);
							
		$numTypes = array(	'tinyint'=>true,
						'smallint'=>true,
						'mediumint'=>true,
						'int'=>true,
						'integer'=>true,
						'bigint'=>true,
						'float'=>true,
						'double'=>true,
						'real'=>true,
						'decimal'=>true,
						'numeric'=>true,
						'bit'=>true,);
		
							
		while($row = mysql_fetch_assoc($result)){
			$sql = '`'.$row['Field'].'` '.$row['Type'];
			
			$pos = strpos($row['Type'],'(');
			if( $pos > 0 ){
				$strippedType = trim(substr($row['Type'],0,$pos));
			}else{
				$strippedType = $row['Type'];
			}
			$strippedType = strtolower($strippedType);
			
			// only needed if a column is going to have a different collation than the table
			//
			// if( isset($row['Collation']) && $row['Collation'] != 'NULL' ){
			// 	$sql .= ' COLLATE '.$row['Collation'];
			// }
			
				
			
			if( $row['Null'] === 'YES' ){
				
				if($row['Type'] == 'timestamp' ){
					if( $row['Default'] == 'CURRENT_TIMESTAMP'){
						//$sql .= ' DEFAULT CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP';//doesn't work for MySQL 4.0
						//$sql .= ' NOT NULL DEFAULT CURRENT_TIMESTAMP ';//doesn't work for < 4.1.2
						$sql .= ' NOT NULL '; //FOR MySQL 4.0
					}elseif( !empty($row['Default']) ){
						$sql .= ' NOT NULL DEFAULT \''.wbDB::escape($row['Default']).'\'';
					}
				}elseif( isset($textTypes[$strippedType]) ){
					
				}else{
					$sql .= ' DEFAULT NULL ';
				}
				
			}else{
				
				if($row['Type'] == 'timestamp' ){
					if( $row['Default'] == 'CURRENT_TIMESTAMP'){
						$sql .= ' NOT NULL '; //FOR MySQL 4.0
					}else{
						$sql .= ' NOT NULL DEFAULT \''.wbDB::escape($row['Default']).'\'';
					}
					
				}else{
					
					//checked with datetime, strings..
					$sql .= ' NOT NULL';
					if( $row['Extra'] == 'auto_increment' ){
						//nothing
						
					}elseif( !empty($row['Default']) ){
						$sql .= ' DEFAULT \''.wbDB::escape($row['Default']).'\'';
						
					}elseif( isset($textTypes[$strippedType]) ){
						//nothing
						
					}elseif( isset($numTypes[$strippedType]) ){
						$sql .= ' DEFAULT \'0\'';
					}
				}
			}
			
			$sql .= ' '.$row['Extra'];
			
			$dataArray[$table]['columns'][$row['Field']] = $sql;
		}
		//showRows($result);
	}
	
	////////////////////////////////////////////////////////////////////////////////
	//
	//		INDEXES
	//		echo '<h2>INDEX</h2>';
	//
	foreach($dataArray as $table => $null){
		$query = 'SHOW INDEX FROM `'.$tablePrefix.$table.'`';
		$result = wbDB::runQuery($query);
		//showRows($result);
		$tempA = array();
		while($row = mysql_fetch_assoc($result)){
			$tempA[$row['Key_name']]['Non_unique'] = $row['Non_unique'];
			$tempA[$row['Key_name']][(int)$row['Seq_in_index']] = '`'.$row['Column_name'].'`';
			if( !empty($row['Sub_part']) ){
				$tempA[$row['Key_name']][(int)$row['Seq_in_index']] .= '('.$row['Sub_part'].')';
			}
			$tempA[$row['Key_name']]['Index_type'] = $row['Index_type'];
		}
		
		// echo '<h3>Hmm</h3>';
		// echo showArray($tempA);
		foreach($tempA as $key => $info){
			$temp = '';
			
			if( $key == 'PRIMARY' ){
				$temp .= 'PRIMARY KEY (';
				
			}elseif($info['Index_type'] == 'FULLTEXT'){
				$temp = 'FULLTEXT KEY '.$key.' (';
			}elseif((int)$info['Non_unique']===1){
				$temp = 'INDEX `'.$key.'` (';
			}else{
				$temp = 'UNIQUE `'.$key.'` (';
			}
			unset($info['Non_unique']);
			unset($info['Index_type']);
			$temp .= implode(', ',$info);
			$temp .= ')';
			$dataArray[$table]['keys'][$key] = $temp;
		}
	}
	//echo showArray($dataArray);
	return $dataArray;
}

