app = $app; } public function emptyTableCache(){ $this->CheckColumnTableCache = []; $this->allTables = []; $this->indexe = []; } /** * @var bool $force * * @return array */ public function getAllTables($force = false) { if($force || empty($this->allTables)) { $this->allTables = $this->app->DB->SelectFirstCols('SHOW TABLES'); } return $this->allTables; } /** * @param string $table * @param string $pk */ public function createTable($table, $pk = 'id') { $sql = "CREATE TABLE `$table` (`".$pk."` INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (`".$pk."`)) ENGINE = InnoDB DEFAULT CHARSET=utf8"; $this->app->DB->Query($sql); $this->addPrimary($table, $pk); } /** * @param string $table * @param string $pk */ public function addPrimary($table, $pk = 'id') { $this->CheckAlterTable( "ALTER TABLE `$table` ADD PRIMARY KEY (`".$pk."`)", true ); $this->CheckAlterTable( "ALTER TABLE `$table` MODIFY `".$pk."` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1", true ); } /** * @param string $table * @param bool $force * * @return array */ public function getIndexeCached($table, $force = false) { if($force || !isset($this->indexe[$table])){ $this->indexe[$table] = $this->app->DB->SelectArr(sprintf('SHOW INDEX FROM `%s`', $table)); if($this->indexe[$table] === null) { $this->indexe[$table] = []; } } return $this->indexe[$table]; } /** * @param string $table */ public function clearIndexCached($table) { if(!isset($this->indexe[$table])) { return; } unset($this->indexe[$table]); } /** * @param string $table * @param string $pk */ public function hasPrimaryKey($table, $pk = 'id') { $indexe = $this->getIndexeCached($table); if(empty($indexe)) { return false; } foreach($indexe as $index) { if($index['Column_name'] === $pk && $index['Key_name'] === 'PRIMARY' && (int)$index['Non_unique'] === 0 ) { return true; } } return false; } /** * @param string $table * @param string $pk * * @return void */ function CheckTable($table, $pk = 'id') { if($pk === 'id') { $tables = $this->getAllTables(); if(!empty($tables)){ if(!in_array($table, $tables)){ $this->createTable($table, $pk); return; } if(!$this->hasPrimaryKey($table, $pk)) { $this->addPrimary($table, $pk); } return; } } $found = false; $tables = $this->getAllTables(true); if($tables) { $found = in_array($table, $tables); } else{ $check = $this->app->DB->Select("SELECT $pk FROM `$table` LIMIT 1"); if($check) { $found = true; } } if($found==false) { $sql = "CREATE TABLE `$table` (`".$pk."` INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (`".$pk."`)) ENGINE = InnoDB DEFAULT CHARSET=utf8"; $this->app->DB->Update($sql); $this->CheckAlterTable("ALTER TABLE `$table` ADD PRIMARY KEY (`".$pk."`)"); $this->CheckAlterTable("ALTER TABLE `$table` MODIFY `".$pk."` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1"); } if($pk !== 'id') { $this->CheckColumn('created_at','timestamp',$table,"DEFAULT CURRENT_TIMESTAMP NOT NULL"); } } /** * @param string $column * @param string $type * @param string $table * @param string $default * * @return void */ function UpdateColumn($column,$type,$table,$default="NOT NULL") { $fields = $this->app->DB->SelectArr("show columns from `".$table."`"); if($fields) { foreach($fields as $val) { $field_array[] = $val['Field']; } } if (in_array($column, $field_array)) { $this->app->DB->Query('ALTER TABLE `'.$table.'` CHANGE `'.$column.'` `'.$column.'` '.$type.' '.$default.';'); } } /** * @param string $column * @param string $table * * @return void */ public function DeleteColumn($column,$table) { $this->app->DB->Query('ALTER TABLE `'.$table.'` DROP `'.$column.'`;'); } /** * @param string $column * @param string $type * @param string $table * @param string $default * * @return void */ public function CheckColumn($column,$type,$table,$default="") { if($table === 'firmendaten') { if($this->app->DB->Select("SELECT `id` FROM `firmendaten_werte` WHERE `name` = '$column' LIMIT 1"))return; } if(!isset($this->CheckColumnTableCache[$table])) { $tmp=$this->app->DB->SelectArr("show columns from `".$table."`"); if($tmp) { foreach($tmp as $val) { $this->CheckColumnTableCache[$table][] = $val['Field']; //$types[$val['Field']] = strtolower($val['Type']); } } } if (isset($this->CheckColumnTableCache[$table]) && !in_array($column, $this->CheckColumnTableCache[$table])) { if($this->check_column_missing_run) { //$result = mysqli_query($this->app->DB->connection,'ALTER TABLE `'.$table.'` ADD `'.$column.'` '.$type.' '.$default.';'); $this->check_column_missing[$table][]=$column; } else { $result = $this->app->DB->Query('ALTER TABLE `'.$table.'` ADD `'.$column.'` '.$type.' '.$default.';'); if($table === 'firmendaten' && $this->app->DB->error()) { if((method_exists($this->app->DB, 'errno2') && $this->app->DB->errno() == '1118') || strpos($this->app->DB->error(),'Row size too large') !== false ) { $this->ChangeFirmendatenToMyIsam(); $this->app->DB->Query('ALTER TABLE `'.$table.'` ADD `'.$column.'` '.$type.' '.$default.';'); } } } } } /** * @param array $indexe * * @return array */ protected function getGroupedIndexe($indexe) { if(empty($indexe)) { return $indexe; } $return = []; foreach($indexe as $index) { $keyName = $index['Key_name']; $isUnique = $index['Non_unique'] == '0'; $seq = $index['Seq_in_index']; $columnName = $index['Column_name']; $return[$isUnique?'unique':'index'][$keyName][(int)$seq - 1] = $columnName; } return $return; } /** * @param array $indexe * * @return array */ protected function getDoubleIndexeFromGroupedIndexe($indexe) { if(empty($indexe)) { return []; } $ret = []; foreach($indexe as $type => $indexArrs) { $columnStrings = []; foreach($indexArrs as $indexKey => $columns) { $columnString = implode('|', $columns); if(in_array($columnString, $columnStrings)) { $ret[$type][] = $indexKey; continue; } $columnStrings[] = $columnString; } } return $ret; } /** * @param string $table * @param array $indexe * @param bool $noCache * * @return array|null */ public function CheckDoubleIndex($table, $indexe, $noCache = false) { $query = $noCache?null:$this->CheckAlterTable("SHOW INDEX FROM `$table`"); if(!$query) { $indexeGrouped = $this->getGroupedIndexe($indexe); $doubleIndexe = $this->getDoubleIndexeFromGroupedIndexe($indexeGrouped); if(!empty($doubleIndexe)) { $indexe = $this->getIndexeCached($table, true); $indexeGrouped = $this->getGroupedIndexe($indexe); $doubleIndexe = $this->getDoubleIndexeFromGroupedIndexe($indexeGrouped); if(empty($doubleIndexe)) { return $indexe; } foreach($doubleIndexe as $type => $doubleIndex) { foreach($doubleIndex as $indexName) { $this->app->DB->Query("ALTER TABLE `".$table."` DROP INDEX `".$indexName."`"); } } } elseif($noCache) { return $indexe; } $this->CheckAlterTable("SHOW INDEX FROM `$table`", true); return $this->getIndexeCached($table, true); } if(empty($indexe) || count($indexe) == 1){ return $indexe; } $uniquearr = array(); $indexarr = array(); foreach($indexe as $index) { if($index['Key_name'] !== 'PRIMARY' && !empty($index['Column_name'])) { if($index['Non_unique']) { $indexarr[$index['Key_name']][] = $index['Column_name']; }else{ $uniquearr[$index['Key_name']][] = $index['Column_name']; } } } $cindex = count($indexarr); $cuniqe = count($uniquearr); $changed = false; if($cindex > 1) { $check = array(); foreach($indexarr as $key => $value) { if(empty($value)) { continue; } if(count($value) > 1){ sort($value); } $vstr = implode(',', $value); if(in_array($vstr, $check)) { $this->app->DB->Query("DROP INDEX `".$key."` ON `".$table."`"); $changed = true; }else{ $check[] = $vstr; } } } if($cuniqe > 1) { $check = array(); foreach($uniquearr as $key => $value) { if(empty($value)) { continue; } if(count($value) > 1){ sort($value); } $vstr = implode(',', $value); if(in_array($vstr, $check)) { $this->app->DB->Query("DROP UNIQUE `".$key."` ON `".$table."`"); $changed = true; }else{ $check[] = $vstr; } } } if($changed) { return $this->getIndexeCached($table, true); } return $indexe; } /** * @param string $table * @param string|array $column * * @return bool */ public function CheckFulltextIndex($table,$column) { if(empty($table) || empty($column)) { return false; } if(!is_array($column)) { $column = [$column]; } $columnmasked = []; foreach($column as $keyColumn => $valueColumn) { if(!empty($valueColumn)) { $columnmasked[] = "`$valueColumn`"; }else{ unset($column[$keyColumn]); } } if(empty($column)) { return false; } $columnsFound = []; $indexe = $this->getIndexeCached($table, true); $indexeFound = []; if(!empty($indexe)) { foreach($indexe as $index) { if($index['Index_type'] === 'FULLTEXT') { $indexeFound[] = $index['Column_name']; if(!in_array($index['Column_name'], $columnsFound)) { $columnsFound[] = $index['Column_name']; } } } $cindexeFound = count($indexeFound); $column = count($column); if(($column === $cindexeFound) && (count($columnsFound) === $column)) { return true; } if($cindexeFound > 0) { return false; } } $this->app->DB->Query( "ALTER TABLE `$table` ADD FULLTEXT INDEX `FullText` (".implode(',',$columnmasked).");" ); $error = $this->app->DB->error(); return empty($error); } /** * @param string $table * @param string $column * @param bool $unique * * @return void */ function CheckIndex($table, $column, $unique = false) { $indexex = null; $indexexother = null; $indexe = $this->getIndexeCached($table); if($indexe) { $indexe = $this->CheckDoubleIndex($table, $indexe, true); foreach($indexe as $index) { if(is_array($column) && $index['Key_name'] !== 'PRIMARY') { if($unique && !$index['Non_unique']) { if(in_array($index['Column_name'], $column)) { $indexex[$index['Key_name']][$index['Column_name']] = true; }else{ $indexexother[$index['Key_name']][$index['Column_name']] = true; } } elseif(!$unique){ if(in_array($index['Column_name'], $column)) { $indexex[$index['Key_name']][$index['Column_name']] = true; } } } elseif(!is_array($column)){ if($index['Column_name'] == $column) { return; } } } if($this->check_column_missing_run) { $this->check_index_missing[$table][] = $column; } if(!$unique) { if(is_array($column)) { if($indexex) { foreach($indexex as $k => $v) { if(count($v) === 1 && count($column) > 1) { $this->app->DB->Query("DROP INDEX `".$k."` ON `".$table."`"); $this->clearIndexCached($table); unset($indexex[$k]); } } foreach($indexex as $k => $v) { if(count($v) == count($column)){ return; } } foreach($indexex as $k => $v) { if(!isset($indexexother[$k])) { $this->app->DB->Query("DROP INDEX `".$k."` ON `".$table."`"); $cols = null; foreach($column as $c) { $cols[] = "`$c`"; } $this->CheckAlterTable("ALTER TABLE `$table` ADD INDEX(".implode(', ',$cols)."); ",true); $this->clearIndexCached($table); return; } } } $cols = null; foreach($column as $c) { $cols[] = "`$c`"; } $this->CheckAlterTable("ALTER TABLE `$table` ADD INDEX(".implode(', ',$cols)."); ", true); $this->clearIndexCached($table); } else{ $this->CheckAlterTable("ALTER TABLE `$table` ADD INDEX(`$column`); ", true); $this->clearIndexCached($table); } } else{ if(is_array($column)) { if($indexex) { foreach($indexex as $k => $v) { if(count($v) == count($column)) { return; } } foreach($indexex as $k => $v) { if(!isset($indexexother[$k])) { $this->app->DB->Query("DROP INDEX `".$k."` ON `".$table."`"); $cols = null; foreach($column as $c) { $cols[] = "`$c`"; } $this->CheckAlterTable("ALTER TABLE `$table` ADD UNIQUE(".implode(', ',$cols)."); ", true); $this->clearIndexCached($table); return; } } } $cols = null; foreach($column as $c) { $cols[] = "`$c`"; } $this->CheckAlterTable("ALTER TABLE `$table` ADD UNIQUE(".implode(', ',$cols)."); ", true); $this->clearIndexCached($table); }else{ $this->CheckAlterTable("ALTER TABLE `$table` ADD UNIQUE(`$column`); ", true); $this->clearIndexCached($table); } } } elseif(!is_array($column)) { if(!$unique) { $this->CheckAlterTable("ALTER TABLE `$table` ADD INDEX(`$column`); "); }else{ $this->CheckAlterTable("ALTER TABLE `$table` ADD UNIQUE(`$column`); "); } $this->clearIndexCached($table); } elseif(is_array($column)) { $cols = null; foreach($column as $c) { $cols[] = "`$c`"; } $this->CheckAlterTable("ALTER TABLE `$table` ADD UNIQUE(".implode(', ',$cols)."); "); $this->clearIndexCached($table); } } /** * @param string $sql * @param bool $force * * @return mysqli_result|bool */ function CheckAlterTable($sql, $force = false) { $sqlmd5 = md5($sql); $check = $this->app->DB->Select("SELECT id FROM checkaltertable WHERE checksum='$sqlmd5' LIMIT 1"); if($check > 0 && !$force) return; $query = $this->app->DB->Query($sql); if($query && empty($check) && !$this->app->DB->error()){ $this->app->DB->Insert("INSERT INTO checkaltertable (id,checksum) VALUES ('','$sqlmd5')"); } return $query; } /** * @return void */ public function ChangeFirmendatenToMyIsam() { $this->app->DB->Query("ALTER TABLE firmendaten ENGINE = MyISAM;"); } /** * @param string $table * * @return array */ public function getSortedIndexColumnsByIndexName($table): array { $indexesByName = []; $indexes = $this->app->DB->SelectArr(sprintf('SHOW INDEX FROM `%s`', $table)); if(empty($indexes)) { return $indexesByName; } foreach($indexes as $index) { $indexesByName[$index['Key_name']][] = $index['Column_name']; } foreach($indexesByName as $indexName => $columns) { $columns = array_unique($columns); sort($columns); $indexesByName[$indexName] = $columns; } return $indexesByName; } /** * @deprecated will be removed in 21.4 * * @param string $table * @param array $columns */ public function dropIndex($table, $columns): void { if(empty($table) || empty($columns)) { return; } $columns = array_unique($columns); sort($columns); $countColumns = count($columns); $indexes = $this->getSortedIndexColumnsByIndexName($table); if(empty($indexes)) { return; } foreach($indexes as $indexName => $indexColumns) { if(count($indexColumns) !== $countColumns) { continue; } if(count(array_intersect($indexColumns, $columns)) === $countColumns) { $this->app->DB->Query(sprintf('ALTER TABLE `%s` DROP INDEX `%s`', $table, $indexName)); } } } }