_app = JFactory::getApplication();
$this->_db = JFactory::getDbo();
// $this->_oldToNew = new stdClass();
$this->starttime = microtime(true);
$max_execution_time = VmConfig::getExecutionTime();
$jrmax_execution_time= vRequest::getInt('max_execution_time',900);
if(!empty($jrmax_execution_time)){
// vmdebug('$jrmax_execution_time',$jrmax_execution_time);
if($max_execution_time!==$jrmax_execution_time) @ini_set( 'max_execution_time', $jrmax_execution_time );
}
$this->maxScriptTime = VmConfig::getExecutionTime() * 0.90-1; //Lets use 10% of the execution time as reserve to store the progress
VmConfig::ensureMemoryLimit(256);
$this->maxMemoryLimit = (VmConfig::getMemoryLimit() * 0.99 * 1048576) - 6291456; //6 MB Reserve
$config = JFactory::getConfig();
$this->_prefix = $config->get('dbprefix');
$this->reCreaPri = VmConfig::get('reCreaPri',0);
$this->reCreaKey = VmConfig::get('reCreaKey',1);
$this->debug = false;
}
var $tables = array( 'products'=>'virtuemart_product_id',
'vendors'=>'virtuemart_vendor_id',
'categories'=>'virtuemart_category_id',
'manufacturers'=>'virtuemart_manufacturer_id',
'manufacturercategories'=>'virtuemart_manufacturercategories_id',
'paymentmethods'=>'virtuemart_paymentmethod_id',
'shipmentmethods'=>'virtuemart_shipmentmethod_id');
/**
*
*
* @author Max Milbers
* @param unknown_type $config
*/
public function createLanguageTables($langs=0){
if(empty($langs)){
$langs = VmConfig::get('active_languages',array(VmConfig::$jDefLangTag));
if(empty($langs)){
$langs = (array)VmConfig::$defaultLang;
}
}
vmdebug('Create/Update Language tables');
foreach($langs as $i => $lang){
$lang = strtolower(strtr($lang,'-','_'));
if(empty($lang))unset($langs[$i]);
}
$langTables = array();
//Todo add the mb_ stuff here
// vmTime('my langs
'.print_r($langs,1).'
');
$i = 0;
//$this->debug=true;
foreach($this->tables as $table=>$tblKey){
vmdebug('Updating language table '.$table);
// if($i>1) continue;
$className = 'Table'.ucfirst ($table);
if(!class_exists($className)) require(VMPATH_ADMIN.'/tables/'.$table.'.php');
$tableName = '#__virtuemart_'.$table;
$langTable = $this->getTable($table);
$translatableFields = $langTable->getTranslatableFields();
if(empty($translatableFields)) continue;
$fields = array();
$lines = array();
$linedefault = "NOT NULL DEFAULT ''";
//Text has no default
$linedefaulttext = "NOT NULL";
$fields[$tblKey] = 'int(1) UNSIGNED NOT NULL';
// vmdebug('createLanguageTables ',$translatableFields);
//set exceptions from normal shema here !
//Be aware that you can use this config settings, when declaring them in the virtuemart.cfg
if(VmConfig::get('dblayoutstrict',true)){
if($table=='products'){
$fields['product_s_desc'] = 'varchar('.VmConfig::get('dbpsdescsize',2000).') '.$linedefault;
$fields['product_desc'] = 'text ';
$key = array_search('product_desc', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('product_s_desc', $translatableFields);
unset($translatableFields[$key]);
} else if($table=='vendors'){
//This makes too much trouble with the vendor stuff, so we use simply text for it
// $fields['vendor_store_desc'] = 'varchar('.VmConfig::get('dbvdescsize',1800).') '.$linedefault;
// $fields['vendor_terms_of_service'] = 'varchar('.VmConfig::get('dbtossize',18100).') '.$linedefault;
// $fields['vendor_legal_info'] = 'varchar('.VmConfig::get('dblegalsize',1100).') '.$linedefault;
$fields['vendor_store_desc'] = 'text ';
$fields['vendor_terms_of_service'] = 'mediumtext ';
$fields['vendor_legal_info'] = 'text ';
$fields['vendor_letter_css'] = 'text ';
$fields['vendor_letter_header_html'] = "varchar(6500) NOT NULL DEFAULT '{vm:vendorname}
{vm:vendoraddress}
'";
$fields['vendor_letter_footer_html'] = "varchar(6500) NOT NULL DEFAULT '{vm:vendorlegalinfo}
Page {vm:pagenum}/{vm:pagecount}
'";
$key = array_search('vendor_store_desc', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('vendor_terms_of_service', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('vendor_legal_info', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('vendor_letter_css', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('vendor_letter_header_html', $translatableFields);
unset($translatableFields[$key]);
$key = array_search('vendor_letter_footer_html', $translatableFields);
unset($translatableFields[$key]);
}
} else {
vmdebug('dblayoutstrict false');
$fields['vendor_terms_of_service'] = 'mediumtext ';
$key = array_search('vendor_terms_of_service', $translatableFields);
unset($translatableFields[$key]);
$fields['vendor_legal_info'] = 'text ';
$key = array_search('vendor_legal_info', $translatableFields);
unset($translatableFields[$key]);
}
// vmdebug('createLanguageTables ',$translatableFields);
foreach($translatableFields as $k => $name){
if(strpos($name,'name') !==false ){
$fields[$name] = 'varchar('.VmConfig::get('dbnamesize',400).') '.$linedefault;
} else if(strpos($name,'metadesc')!==false ){
$fields[$name] = 'varchar('.VmConfig::get('dbmetasize',400).') '.$linedefault;
} else if(strpos($name,'metatitle')!==false ){
$fields[$name] = 'varchar('.VmConfig::get('dbmetasize',100).') '.$linedefault;
} else if(strpos($name,'metakey')!==false ){
$fields[$name] = 'varchar('.VmConfig::get('dbmetasize',400).') '.$linedefault;
} else if(strpos($name,'metaauthor')!==false ){
$fields[$name] = 'varchar(64) '.$linedefault;
} else if(strpos($name,'slug')!==false ){
$fields[$name] = 'varchar('.VmConfig::get('dbslugsize',191).') '.$linedefault;
$slug = true;
}else if(strpos($name,'phone')!==false) {
$fields[$name] = 'varchar(26) '.$linedefault;
}else if(strpos($name,'desc')!==false) {
/*if(VmConfig::get('dblayoutstrict',true)){
$fields[$name] = 'varchar('.VmConfig::get('dbdescsize',12500).') '.$linedefault;
} else {*/
$fields[$name] = 'text ';
//}
} else {
$fields[$name] = 'varchar(255) '.$linedefault;
}
}
$lines[0] = $fields;
if($slug){
$lines[1][$tblKey] = 'PRIMARY KEY (`'.$tblKey.'`)';
$lines[1]['slug'] = 'UNIQUE KEY `slug` (`slug`)';
//a slug must anyway be unique and so one index for both is faster
//testing revealed that it is slower
//$lines[1][$tblKey] = 'PRIMARY KEY (`'.$tblKey.'`,`slug`)';
} else {
$lines[1][$tblKey] = 'PRIMARY KEY (`'.$tblKey.'`)';
}
//$table[3] = '';
foreach($langs as $lang){
// $lang = strtr($lang,'-','_');
$lang = strtolower(strtr($lang,'-','_'));
$tbl_lang = $tableName.'_'.$lang;
$langTables[$tbl_lang] = $lines;
}
$i++;
}
$this->reCreaPri = 1;
$ret = $this->updateMyVmTables($langTables);
// vmTime('done creation of lang tables');
return $ret;
}
public function getTablesBySql($file){
if(!file_exists($file)){
vmError('Could not execute sql, could not find file '.$file);
return false;
}
$data = fopen($file, 'r');
$updateLines = array();
while ($line = fgets ($data)) {
$updateLines[] = $line;
}
fclose($data);
/*stAn, clear memory immidiately*/
$data = null;
$tables = self::prepareUpdateFieldsBySql($updateLines, $file);
return $tables;
}
static public function prepareUpdateFieldsBySql($updateLines, $name){
$tables = array();
$tableDefStarted = false;
foreach($updateLines as $line){
$line = trim($line);
if (empty($line)) continue; // Empty line
if (strpos($line, '#') === 0) continue; // Commentline
if (strpos($line, '--') === 0) continue; // Commentline
if(strpos($line,'CREATE TABLE IF NOT EXISTS')!==false){
$tableDefStarted = true;
$fieldLines = array();
$tableKeys = array();
$start = strpos($line,'`');
$tablename = trim(substr($line,$start+1,-3));
// vmdebug('my $tablename ',$start,$end,$line);
} else if($tableDefStarted && (strpos($line,'KEY')!==false or strpos($line,'UNIQUE')!==false)){
$start = strpos($line,"`");
$temp = substr($line,$start+1);
$end = strpos($temp,"`");
$keyName = substr($temp,0,$end);
if(strrpos($line,',')==strlen($line)-1){
$line = substr($line,0,-1);
}
$tableKeys[$keyName] = $line;
} else if(strpos($line,'ENGINE')!==false){
$tableDefStarted = false;
$tl = strtolower($line);
if(strpos($tl,'myisam')!==false){
$engine = 'InnoDB';//'MyISAM'; banned
} else if(strpos($tl,'innodb')!==false){
$engine = 'InnoDB';
} else if(strpos($tl,'memory')!==false){
$engine = 'Memory';
} else {
$engine = '';
}
$start = strpos($line,"COMMENT='");
$temp = substr($line,$start+9);
$end = strpos($temp,"'");
$comment = substr($temp,0,$end);
$tables[$tablename] = array($fieldLines, $tableKeys,$comment,$engine);
} else if($tableDefStarted){
$start = strpos($line,"`");
$temp = substr($line,$start+1);
$end = strpos($temp,"`");
$keyName = substr($temp,0,$end);
if(empty($keyName)){
$m = 'getTablesBySql empty $keyName line: '.$line .' file: '. $name;
//vmError($m,$m);
//$tableDefStarted = false;
} else {
$line = trim(substr($line,$end+2));
if(strrpos($line,',')==strlen($line)-1){
$line = substr($line,0,-1);
}
$fieldLines[$keyName] = $line;
}
}
}
return $tables;
}
public function updateMyVmTables($file = 0, $like ='_virtuemart_'){
if(empty($file)){
$file = VMPATH_ADMIN.'/install/install.sql';
}
if(is_array($file)){
$tables = $file;
} else {
$tables = $this->getTablesBySql($file);
}
//vmdebug('Parsed tables',$tables); //return;
$this->_db->setQuery('SHOW TABLES LIKE "%'.$like.'%"');
try {
$existingtables = $this->_db->loadColumn();
} catch (Exception $e) {
vmError('updateMyVmTables '.$e->getMessage());
return false;
}
$i = 0;
$demandedTables = array();
//TODO ignore admin menu table
foreach ($tables as $tablename => $table){
// if($i>2) continue;
$tablename = str_replace('#__',$this->_prefix,$tablename);
$demandedTables[] = $tablename;
if(in_array($tablename,$existingtables)){
/*$q = 'LOCK TABLES `'.$tablename.'` WRITE';
$this->_db->setQuery($q);
$this->_db->execute();*/
if(!isset($table[3])) $table[3] = 'InnoDB';
if(empty($this->reCreaKey)) $table[1] = false;
$this->alterColumns($tablename,$table);
usleep(10);
$this->optimizeTable($tablename);
usleep(10);
/*$q = 'UNLOCK TABLES';
$this->_db->setQuery($q);
$this->_db->execute();*/
} else {
$this->createTable($tablename,$table);
}
$i++;
}
}
public function optimizeTable($tablename){
//There is a bug, which can make your table unaccessable
/*$q ='OPTIMIZE TABLE '.$tablename;
$this->_db->setQuery($q);
$res1 = $this->_db->execute();*/
$q = 'Show Index FROM '.$tablename;
$this->_db->setQuery($q);
$res2 = $this->_db->loadAssocList();
//vmdebug('Optimised table '.$tablename,$res1,$res2);
/*foreach($res2 as $m){
vmdebug($tablename.': '.$m['Key_name'].' '.$m['Cardinality']);
}*/
}
public function createTable($tablename,$table){
$q = 'CREATE TABLE IF NOT EXISTS `'.$tablename.'` (
';
foreach($table[0] as $fieldname => $alterCommand){
$q .= '`'.$fieldname.'` '.$alterCommand.',
';
}
foreach($table[1] as $name => $value){
$q .= $value.',
';
}
$q = substr(trim($q),0,-1);
$comment = '';
if(!empty($table[3])){
$comment = " COMMENT='".$table[3]."'";
}
$q .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci ".$comment." AUTO_INCREMENT=1 ;";
$this->_db->setQuery($q);
try {
if($this->_db->execute()){
vmInfo('created table '.$tablename);
}
}
catch(Exception $e) {
vmError('FAILED: createTable '.$tablename.' ERROR :'.$e->getMessage() .' query: '.$q);
}
// $this->_app->enqueueMessage($q);
}
public function dropTables($todelete){
if(empty($todelete)) return;
$q = 'DROP ';// .implode(',',$todelete);
foreach($todelete as $tablename){
$tablename = str_replace('#__',$this->_prefix,$tablename);
$q .= $tablename.', ';
}
$q = substr($q,0,-1);
// $this->_db->setQuery($q);
// if(!$this->_db->query()){
// $this->_app->enqueueMessage('dropTables ERROR :'.$this->_db->getErrorMsg() );
// }
$this->_app->enqueueMessage($q);
}
private function alterKey($tablename,$keys){
if((microtime(true)-$this->starttime) >= ($this->maxScriptTime)){
vmWarn('compareUpdateTable alterKey not finished, please rise execution time and update tables again');
return false;
}
$demandedFieldNames = array();
foreach($keys as $i=>$line){
$demandedFieldNames[] = $i;
}
$query = "SHOW INDEXES FROM `".$tablename."` "; //SHOW {INDEX | INDEXES | KEYS}
$this->_db->setQuery($query);
$eKeysData = $this->_db->loadObjectList();
$eKeys = array();
foreach($eKeysData as $index) {
$indexName = $index->Key_name;
if (!isset($eKeys[$indexName])) {
$eKeys[$indexName] = $index;
$eKeys[$indexName]->columns = array();
$eKeys[$indexName]->columns[$index->Column_name] = $index->Column_name;
}
else {
$eKeys[$indexName]->columns[$index->Column_name] = $index->Column_name;
}
}
$tkeys=array();
$keyT = $keys;
//Lets check if something changed
foreach($keyT as $keyIndex =>$value) {
$matches = array();
$k = new stdClass();
$k->line = $value;
if($p = strpos( $value, 'PRIMARY' ) !== false) {
if(strpos( $value, '`' ) !== false) {
/* stAn - not reliable
$spl = explode('`', $value);
$k->Key_name = 'PRIMARY';
$k->Column_name = $spl[1];
$k->Non_unique=0;
$tkeys[$k->Key_name] = $k;
*/
$cols = preg_match_all('/`(.*?)`/', $value, $matches);
if (isset($matches[1]) && (count($matches[1]) >= 1)) {
$key_name = $matches[1][0];
$matches[1] = array_unique ($matches[1]); //if possible to name primary with something else
$cols = implode('|', $matches[1]);
$k->Primary = 1;
$k->Key_name = 'PRIMARY';
$k->Column_name = $cols;
$k->keyIndex = $keyIndex;
foreach ($matches[1] as $col) {
$k->columns[$col] = $col;
}
$tkeys['PRIMARY'] = $k;
}
continue;
}
}
if($p = strpos( $value, 'UNIQUE' ) !== false) {
$k->Non_unique=0;
$value = trim(substr($value,$p));
} else {
$k->Non_unique=1;
}
if(strpos( $value, '`' ) !== false) {
/* stAn old code up to vm3.6.11 - not reliable for multi columns:
$spl = explode('`', $value);
//We dont prevent drop and add of double keys
if(!isset($spl[5])) {
$k->Key_name = $spl[1];
$k->Column_name = $spl[3];
$tkeys[$k->Column_name] = $k;
}
*/
$cols = preg_match_all('/`(.*?)`/', $value, $matches);
if (isset($matches[1]) && (count($matches[1]) > 1)) {
$key_name = $matches[1][0];
unset($matches[1][0]);
$cols = implode('|', $matches[1]);
$k->Key_name = $key_name;
$k->Column_name = $cols;
$k->keyIndex = $keyIndex;
foreach ($matches[1] as $col) {
$k->columns[$col] = $col;
}
$tkeys[$key_name] = $k;
}
}
}
$primaryFound = false;
foreach($eKeys as $i => $eKey) {
if (isset($tkeys[$i])) {
/*stAn - check if array values are exactly same regardless of order */
$diff = array_diff($eKey->columns, $tkeys[$i]->columns);
$diff2 = array_diff($tkeys[$i]->columns, $eKey->columns);
if ((empty($diff)) && (empty($diff2))) {
if ($i === 'PRIMARY') {
$primaryFound = true;
}
$keyIndex = $tkeys[$i]->keyIndex;
unset($keys[$keyIndex]);
unset($eKeys[$i]);
continue;
}
else {
//stAn - vmdebug can come here to display which indexes are going to be updated from $tkey[$i]->line
}
}
}
$ok=true;
foreach($eKeys as $i => $eKey) {
if(strpos( $eKey->Key_name, 'PRIMARY' ) !== false) {
$primaryFound = true;
continue;
}
if(empty($eKey->Key_name)) continue;
$query = "SHOW INDEXES FROM `".$tablename."` ";
$this->_db->setQuery($query);
$eKeyNamesNOW = $this->_db->loadColumn(2);
if(!in_array($eKey->Key_name,$eKeyNamesNOW)) continue;
if ((!empty($tkeys[$eKey->Key_name])) || (!empty($keys[$eKey->Key_name]))) {
$is_core_field = true;
}
else {
$is_core_field = false;
}
/* stAn: if the field is named same as within new schema, it is considered to be a core field, if a core field is changed it is dropped and recreated
if the field is not core field, it will not get dropped with hidden config:
ignore either specific custom index or ignore drop index per all non core fields
usage in virtuemart.cfg
ignore.index.virtuemart_product_customfields.customfield_value=1
OR per any custom index:
ignore.index=1
*/
$config_key = 'ignore.index.'.substr($tablename, strlen($this->_prefix)).'.'.$eKey->Key_name;
$ignoreChange = VmConfig::get($config_key, 0);
if (((!$is_core_field) && (VmConfig::get('ignore.index', 0))) || ($ignoreChange)) {
vmInfo('IGNORE: alterTable DROP INDEX '.$tablename.'.'.$eKey->Key_name);
continue;
}
$query = 'ALTER TABLE `'.$tablename.'` DROP INDEX `'.$eKey->Key_name.'` ';
$this->_db->setQuery($query);
try {
if($this->_db->execute()){
vmInfo('alterTable DROP INDEX '.$tablename.'.'.$eKey->Key_name );
}
} catch(Exception $e) {
vmError('FAILED: alterTable DROP INDEX '.$tablename.'.'.$eKey->Key_name.' : '.$e->getMessage() );
}
}
foreach($keys as $name =>$value){
if($primaryFound and strpos($value,'PRIMARY')!==false){
if(strpos($value,'PRIMARY')!==false){
continue;
}
}
$query = "ALTER TABLE `".$tablename."` ADD ".$value ;
$action = 'ADD';
$this->_db->setQuery($query);
if(!empty($query)){
try {
if($this->_db->execute()){
$this->_app = JFactory::getApplication();
vmInfo('alterKey '.$action.' INDEX '.$name );
}
}
catch(Exception $e) {
vmError('FAILED: alterKey '.$action.' INDEX '.$name.': '.$e->getMessage().' '.$query );
}
}
}
}
function reCreateKeyByTableAttributes($keyAttribs){
$oldkey ='';
if(!empty($keyAttribs->Key_name) && !empty($keyAttribs->Column_name) ){
if(!$keyAttribs->Non_unique){
$oldkey = 'UNIQUE ';
//$oldkey = 'PRIMARY KEY (`'.$keyAttribs->Column_name.'`)';
}
//else {
$oldkey .= 'KEY `'.$keyAttribs->Key_name.'` (`'.$keyAttribs->Column_name.'`)';
//}
} else {
vmdebug('reCreateKeyByTableAttributes $keyAttribs empty?',$keyAttribs);
}
// if(empty($keyAttribs->Cardinality)){
// vmdebug('Cardinality : '.$keyAttribs->Cardinality.' '.$oldkey);
// }
return $oldkey;
}
/**
* @author Max Milbers
* @param unknown_type $tablename
* @param unknown_type $fields
* @param unknown_type $command
*/
public function alterColumns($tablename,$tableDef){
$after =' FIRST';
$dropped = 0;
$altered = 0;
$added = 0;
$toRepeat = false;
$this->_app = JFactory::getApplication();
$fields = isset($tableDef[0]) ? $tableDef[0]:false;
$keys = isset($tableDef[1]) ? $tableDef[1]:false;
$engine = isset($tableDef[3]) ? $tableDef[3]:false;
$demandFieldNames = array();
foreach($fields as $i=>$line){
$demandFieldNames[] = $i;
}
$q = 'SHOW FULL COLUMNS FROM `'.$tablename.'` ';
$this->_db->setQuery($q);
$fullColumns = $this->_db->loadObjectList();
$columns = $this->_db->loadColumn(0);
//vmdebug('alterColumns',$fullColumns);
//Attention user_infos is not in here, because it an contain customised fields. #__virtuemart_order_userinfos #__virtuemart_userinfos
//This is currently not working as intended, because the config is not deleted before, it is better to create an extra command for this, when we need it later
$lastDebug = '';
foreach($fields as $fieldname => $alterCommand){
if((microtime(true)-$this->starttime) >= ($this->maxScriptTime)){
vmWarn('alterColumns alterKey not finished, please rise execution time and update tables again');
return false;
}
$query='';
$action = '';
if(empty($alterCommand)){
vmdebug('empty alter command '.$fieldname);
continue;
}
if(in_array($fieldname,$columns)){
$key=array_search($fieldname, $columns);
$oldColumn = $this->reCreateColumnByTableAttributes($fullColumns[$key]);
// while (strpos($oldColumn,' ')){
// str_replace(' ', ' ', $oldColumn);
// }
while (strpos($alterCommand,' ')){
$alterCommand = str_replace(' ', ' ', trim($alterCommand));
}
$oldColumn = strtoupper($oldColumn);
$alterCommand = strtoupper(trim($alterCommand));
//mysql8 text field default is "text not null', strict match:
/*if (($oldColumn === 'TEXT NOT NULL') && ($alterCommand === 'TEXT')) {
$alterCommand = 'TEXT NOT NULL';
}
if (($oldColumn === 'TEXT') && ($alterCommand === 'TEXT NOT NULL')) {
$alterCommand = 'TEXT';
}
if (($oldColumn === 'MEDIUMTEXT NOT NULL') && ($alterCommand === 'MEDIUMTEXT')) {
$alterCommand = 'MEDIUMTEXT NOT NULL';
}
if (($oldColumn === 'MEDIUMTEXT') && ($alterCommand === 'MEDIUMTEXT NOT NULL')) {
$alterCommand = 'MEDIUMTEXT NOT NULL';
}*/
//stAn = on mysql8 there is no support for int(1), smallint(1), bigint(1), etc...
if ((strpos($oldColumn, 'INT(') === false) && (strpos($alterCommand, 'INT(') !== false)) {
$rep = preg_replace("/INT\([^\)]+\)/", "INT", $alterCommand);
$alterCommand = $rep;
}
//if mysql8 syntax is detected:
if ((strpos($oldColumn, 'DEFAULT CURRENT_TIMESTAMP DEFAULT_GENERATED ON') !== false) && (strpos($alterCommand, 'DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()') !== false)) {
//change it from original alter:
$alterCommand = str_replace('DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()','DEFAULT CURRENT_TIMESTAMP DEFAULT_GENERATED ON UPDATE CURRENT_TIMESTAMP', $alterCommand);
}
if (strpos($oldColumn, ' CURRENT_TIMESTAMP ')!==false) {
//if mysql5.7 or mysql8 uses constant instead of function adjust the alter query comparison:
$alterCommand = str_replace('CURRENT_TIMESTAMP()','CURRENT_TIMESTAMP', $alterCommand);
}
//stAn - INT UNSIGNED DEFAULT NULL becomes INT UNSIGNED on mysql8
if ((strpos($oldColumn, ' DEFAULT') === false) && (strpos(strrev($alterCommand), strrev('DEFAULT NULL'))===0)) {
$alterCommand = substr($alterCommand, 0, strpos($alterCommand, ' DEFAULT NULL'));
}
if ((strpos($oldColumn, ' NULL') === false) && (strpos(strrev($alterCommand), strrev(' NULL'))===0)) {
$test = substr($alterCommand, 0, strpos($alterCommand, ' NULL'));
if ($test === $oldColumn) {
//ignore default NULL in mysql8: DECIMAL(15,5) vs DECIMAL(15,5) NULL
$alterCommand = $test;
}
}
if($this->debug) vmdebug('alterColumns '.$tablename.' column '.$fieldname,$oldColumn,$alterCommand);
if ($oldColumn != $alterCommand ) {
$pr = '';
vmdebug('alterColumns columns different '.$fieldname,$oldColumn,$alterCommand);
//vmdebug('alterColumns columns different ! '.$fieldname);
//If the field is an auto_increment, we add to the sql the creation of the primary key
if( (strpos($alterCommand,'AUTO_INCREMENT')!==false xor strpos($oldColumn,'AUTO_INCREMENT')!==false)){
$pr = ', ADD PRIMARY KEY (`'.$fieldname.'`)';
//This function drops the key only if existing
$this->dropPrimaryKey($tablename);
}
/*stAn - ignore custom colums from config
ignore.change.virtuemart_product_customfields.customfield_value=1
*/
$config_key = 'ignore.change.'.substr($tablename, strlen($this->_prefix)).'.'.$fieldname;
$ignoreChange = VmConfig::get($config_key, 0);
if (!empty($ignoreChange)) {
$notice = 'IGNORE: alterColumns '.$tablename.' from '.$oldColumn.' to '.$fieldname.' '.$alterCommand;
vmInfo($notice);
$query = '';
continue;
}
else {
$query = 'ALTER TABLE `'.$tablename.'` CHANGE COLUMN `'.$fieldname.'` `'.$fieldname.'` '.$alterCommand.' '.$after.$pr;
$action = 'CHANGE';
$altered++;
$lastdebug = 'alterColumns '.$tablename.' from '.$oldColumn.' to '.$fieldname.' '.$alterCommand.' '.$after.$pr;
vmInfo($lastdebug);
}
}
}
else {
$pr = '';
if(strpos($alterCommand,'AUTO_INCREMENT')!==false ){
$pr = ', ADD PRIMARY KEY (`'.$fieldname.'`)';
$this->dropPrimaryKey($tablename);
}
$query = 'ALTER TABLE `'.$tablename.'` ADD `'.$fieldname.'` '.$alterCommand.' '.$after.$pr;
$action = 'ADD';
$added++;
$lastdebug = 'alterColumns ADD '.$query;
vmdebug($lastdebug);
}
if (!empty($query)) {
$this->_db->setQuery($query);
$msg = 'alterTable '.$action.' '.$tablename.'.'.$fieldname.' query '.$query;
try {
if(!$this->_db->execute() ){
vmError( $msg, $msg.$query );
} else {
vmInfo( $msg );
}
}
catch(Exception $e) {
//stAn, there is no need to fail the script due to alter
vmInfo( 'FAILED: '.$e->getMessage().' '.$query );
}
}
$after = ' AFTER `'.$fieldname.'` ';
}
if($keys){
$this->alterKey($tablename,$keys,false);
if($toRepeat){
vmdebug('Created keys, writing now field with autoincrement',$tablename,$toRepeat);
$this->alterColumns($tablename,$toRepeat);
}
}
if(VmConfig::get('updEngine',true)){
$q = 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_NAME = "'.$tablename.'" ';
$this->_db->setQuery( $q );
$exEngine = $this->_db->loadResult();
if(!empty($engine) and strtoupper( $exEngine ) != strtoupper( $engine )) {
$q = 'ALTER TABLE '.$tablename.' ENGINE='.$engine;
$this->_db->setQuery( $q );
try {
$this->_db->execute();
vmInfo( 'Changed engine '.$exEngine.' of table '.$tablename.' to '.$engine, $exEngine );
}
catch(Exception $e) {
vmInfo( 'FAILED: Changed engine '.$exEngine.' of table '.$tablename.' to '.$engine, $exEngine );
}
}
}
if($dropped != 0 or $altered !=0 or $added!=0){
$this->_app->enqueueMessage('Table updated: Tablename '.$tablename.' dropped: '.$dropped.' altered: '.$altered.' added: '.$added);
}
return true;
}
/**
* This function drops the key only if existing and removes before the auto_increment attribute from the column
* @author Max Milbers
* @param $tablename
* @return bool
*/
public function dropPrimaryKey($tablename){
$q = 'SHOW INDEXES FROM `'.$tablename.'` WHERE Key_name = "PRIMARY";';
$this->_db->setQuery($q);
$this->_db->execute();
$res = $this->_db->loadAssoc();
if($res){
//We check if there is an auto_increment field and disable it
$q = 'SHOW FULL COLUMNS FROM `'.$tablename.'` WHERE Extra = "auto_increment";'; //$q = 'SHOW CREATE TABLE '.$this->_tbl;
$this->_db->setQuery($q);
$column = $this->_db->loadObject();
if($column){
$old = $this->reCreateColumnByTableAttributes($column);
$old = trim(str_replace('AUTO_INCREMENT', '',$old));
$q = 'ALTER TABLE `'.$tablename.'` CHANGE COLUMN `'.$column->Field.'` `'.$column->Field.'` '.$old;
$this->_db->setQuery($q);
try {
if(!$this->_db->execute() ){
vmError( 'Could not alter auto_increment column dropping primary '.$q );
}
}
catch(Exception $e) {
vmInfo( 'FAILED: Could not alter auto_increment column dropping primary '.$q );
}
}
$q = 'ALTER TABLE `'.$tablename.'` DROP PRIMARY KEY;';
$this->_db->setQuery($q);
try {
if(!$this->_db->execute() ){
vmError( 'Could not drop Primary for CHANGE '.$q );
} else {
vmdebug('dropPrimaryKey '.$tablename);
}
}
catch(Exception $e) {
vmInfo( 'FAILED: Could not drop Primary for CHANGE '.$q );
}
}
return true;
}
public function deleteColumns($tablename,$fields){
$dropped = 0;
$q = 'SHOW FULL COLUMNS FROM `'.$tablename.'` '; //$q = 'SHOW CREATE TABLE '.$this->_tbl;
$this->_db->setQuery($q);
//$fullColumns = $this->_db->loadObjectList();
$columns = $this->_db->loadColumn(0);
$demandFieldNames = array();
foreach($fields as $i=>$line){
$demandFieldNames[] = $i;
}
$upDelCols = (int) VmConfig::get('updelcols',0);
if($upDelCols==1 and !($tablename==$this->_prefix.'virtuemart_userfields' or $tablename==$this->_prefix.'virtuemart_userinfos' or $tablename==$this->_prefix.'virtuemart_order_userinfos')){
foreach($columns as $fieldname){
if(!in_array($fieldname, $demandFieldNames)){
$query = 'ALTER TABLE `'.$tablename.'` DROP COLUMN `'.$fieldname.'` ';
$action = 'DROP';
$dropped++;
$this->_db->setQuery($query);
try {
if($this->_db->execute()){
vmInfo('alterTable '.$action.' '.$tablename.'.'.$fieldname );
}
}
catch(Exception $e) {
vmError('FAILED: alterTable '.$action.' '.$tablename.'.'.$fieldname.' :'.$e->getMessage() );
}
}
}
}
return $dropped;
}
public function reCreateColumnByTableAttributes($fullColumn){
$oldColumn = $fullColumn->Type;
if(!empty($fullColumn->Null)){
$oldColumn .= $this->notnull($fullColumn->Null).$this->getdefault($fullColumn->Default);
}
$oldColumn .= $this->formatExtra($fullColumn->Extra).$this->formatComment($fullColumn->Comment);
return trim($oldColumn);
}
private function formatComment($comment){
if(!empty($comment)){
return ' COMMENT \''.$comment.'\'';
} else {
return '';
}
}
private function notnull($string){
if ($string=='NO') {
return ' NOT NULL';
} else {
return '';
}
}
private function formatExtra($extra){
if (!empty($extra)) {
return ' '.strtoupper(trim($extra));
} else {
return '';
}
}
private function primarykey($string){
if ($string=='PRI') {
return ' AUTO_INCREMENT';
} else {
return '';
}
}
private function getdefault($string){
if (isset($string)) {
if(strpos($string,'\'')!==FALSE or strpos(strtolower($string),'current_timestamp')!==FALSE){
return " DEFAULT ".trim($string);
} else {
return " DEFAULT '".trim($string)."'";
}
} else {
return '';
}
}
function loadCountListContinue($q,$startLimit,$maxItems,$msg){
$continue = true;
$this->_db->setQuery($q);
try{
if($this->_db->execute()){
$entries = $this->_db->loadAssocList();
$count = count($entries);
vmInfo($msg. ' found '.$count.' vm1 entries for migration ');
$startLimit += $maxItems;
if($count<$maxItems){
$continue = false;
}
} else {
$entries = array();
$continue = false;
}
} catch (Exception $e){
vmError($msg.' db error '. $e->getMessage().' '. $this->_db->getQuery());
$entries = array();
$continue = false;
}
return array($entries,$startLimit,$continue);
}
}