Example #1
0
	function check_mysql_table_structure()
	{
		global $DB;
		$strError = '';

		if ($this->arTestVars['table_charset_fail'])
			return $this->Result(null, GetMessage('SC_TABLE_COLLATION_NA'));

		$module = '';
		$cnt = $iCurrent = 0;
		if ($dir = opendir($path = $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules'))
		{
			while(false !== ($item = readdir($dir)))
			{
//				if ($item == '.' || $item == '..')
				if (strpos($item, '.') !== false) // skipping all external modules
					continue;

				$cnt++;

				if ($this->arTestVars['last_value'])
				{
					$iCurrent++;
					if ($this->arTestVars['last_value'] == $item)
						unset($this->arTestVars['last_value']);
				}
				elseif (!$module)
					$module = $item;
			}
			closedir($dir);
		}
		else
			return false;
				
		$file = $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/'.$module.'/install/db/mysql/install.sql';
		if (!file_exists($file))
			$file = $_SERVER['DOCUMENT_ROOT'].'/bitrix/modules/'.$module.'/install/mysql/install.sql';
		if (file_exists($file)) // uses database...
		{
			$arTableColumns = array();
			$rs = $DB->Query('SELECT * FROM b_module WHERE id="'.$DB->ForSQL($module).'"');
			if ($rs->Fetch()) // ... and is installed
			{
				if (false === ($query = file_get_contents($file)))
					return false;

				$arTables = array();
				$arQuery = $DB->ParseSQLBatch(str_replace("\r", "", $query));
				foreach($arQuery as $sql)
				{
					if (preg_match('#^(CREATE TABLE )(IF NOT EXISTS)? *`?([a-z0-9_]+)`?(.*);?$#mis',$sql,$regs))
					{
						$table = $regs[3];
						if (preg_match('#^site_checker_#', $table))
							continue;
						$rs = $DB->Query('SHOW TABLES LIKE "'.$table.'"');
						if (!$rs->Fetch())
						{
							if ($this->fix_mode)
							{
								if (!$DB->Query($sql, true))
									return $this->Result(false, 'Mysql Query Error: '.$sql.' ['.$DB->db_Error.']');
							}
							else
							{
								$strError .= GetMessage('SC_ERR_NO_TABLE', array('#TABLE#' => $table))."<br>";
								$_SESSION['FixQueryList'][] = $sql;
								$this->arTestVars['iError']++;
								$this->arTestVars['iErrorAutoFix']++;
								$this->arTestVars['cntNoTables']++;
								continue;
							}
						}

						$arTables[$table] = $sql;
						$tmp_table = 'site_checker_'.$table;
						$DB->Query('DROP TABLE IF EXISTS `'.$tmp_table.'`');
						$DB->Query($regs[1].' `'.$tmp_table.'`'.$regs[4]);
					}
					elseif (preg_match('#^(ALTER TABLE)( )?`?([a-z0-9_]+)`?(.*);?$#mis',$sql,$regs))
					{
						$table = $regs[3];
						if (!$arTables[$table])
							continue;
						$tmp_table = 'site_checker_'.$table;
						$DB->Query($regs[1].' `'.$tmp_table.'`'.$regs[4]);
					}
					elseif (preg_match('#^INSERT INTO *`?([a-z0-9_]+)`?[^\(]*\(?([^)]*)\)?[^V]*VALUES[^\(]*\((.+)\);?$#mis',$sql,$regs))
					{
						$table = $regs[1];
						if (!$arTables[$table])
							continue;
						$tmp_table = 'site_checker_'.$table;

						if ($regs[2])
							$arColumns = explode(',', $regs[2]);
						else
						{
							if (!$arTableColumns[$tmp_table])
							{
								$rs = $DB->Query('SHOW COLUMNS FROM `'.$tmp_table.'`');
								while($f = $rs->Fetch())
									$arTableColumns[$tmp_table][] = $f['Field'];
							}
							$arColumns = $arTableColumns[$tmp_table];
						}
						
						$strValues = $regs[3];
						$ar = explode(",",$strValues);
						$arValues = array();
						$i = 0;
						$str = '';
						foreach($ar as $v)
						{
							$str .= ($str ? ',' : '').$v;
							if (preg_match('#^ *(-?[0-9]+|\'.*\'|".*"|null|now\(\)) *$#i',$str)) 
							{
								$arValues[$i] = $str;
								$str = '';
								$i++;
							}
						}
						
						if (!$str)
						{
							$sqlSelect = 'SELECT * FROM `'.$table.'` WHERE 1=1 ';
							foreach($arColumns as $k => $c)
							{
								$v = $arValues[$k];
								if (!preg_match('#null|now\(\)#i',$v))
									$sqlSelect .= ' AND '.$c.'='.$v;
							}
							$rs = $DB->Query($sqlSelect);
							if (!$rs->Fetch())
							{
								if ($this->fix_mode)
								{
									if (!$DB->Query($sql, true))
										return $this->Result(false, 'Mysql Query Error: '.$sql.' ['.$DB->db_Error.']');
								}
								else
								{
									$strError .= GetMessage('SC_ERR_NO_VALUE', array('#TABLE#' => $table, '#SQL#' => $sql))."<br>";
									$_SESSION['FixQueryList'][] = $sql;
									$this->arTestVars['iError']++;
									$this->arTestVars['iErrorAutoFix']++;
									$this->arTestVars['cntNoValues']++;
								}
							}
						}
						else
							echo "Error parsing SQL:\n".$sql."\n";
					}
				}

				foreach($arTables as $table => $sql)
				{
					$tmp_table = 'site_checker_'.$table;
					$arColumns = array();
					$rs = $DB->Query('SHOW COLUMNS FROM `'.$table.'`');
					while($f = $rs->Fetch())
						$arColumns[strtolower($f['Field'])] = $f;

					$rs = $DB->Query('SHOW COLUMNS FROM `'.$tmp_table.'`');
					while($f_tmp = $rs->Fetch())
					{
						$tmp = TableFieldConstruct($f_tmp);
						if ($f = $arColumns[strtolower($f_tmp['Field'])])
						{
							if (($cur = TableFieldConstruct($f)) != $tmp)
							{
								$sql = 'ALTER TABLE `'.$table.'` MODIFY `'.$f_tmp['Field'].'` '.$tmp;
								if ($this->fix_mode)
								{
									if (TableFieldCanBeAltered($f, $f_tmp))
									{
										if (!$DB->Query($sql, true))
											return $this->Result(false, 'Mysql Query Error: '.$sql.' ['.$DB->db_Error.']');
									}
									else
										$this->arTestVars['iErrorFix']++;
								}
								else
								{
									$_SESSION['FixQueryList'][] = $sql;
									$strError .= GetMessage('SC_ERR_FIELD_DIFFERS', array('#TABLE#' => $table, '#FIELD#' => $f['Field'], '#CUR#' => $cur, '#NEW#' => $tmp))."<br>";
									$this->arTestVars['iError']++;
									if (TableFieldCanBeAltered($f, $f_tmp))
										$this->arTestVars['iErrorAutoFix']++;
									$this->arTestVars['cntDiffFields']++;
								}
							}
						}
						else
						{
							$sql = 'ALTER TABLE `'.$table.'` ADD `'.$f_tmp['Field'].'` '.str_replace('auto_increment', '' , strtolower($tmp)); // if only Primary Key is missing we will have to pass the test twice
							if ($this->fix_mode)
							{
								if (!$DB->Query($sql, true))
									return $this->Result(false, 'Mysql Query Error: '.$sql.' ['.$DB->db_Error.']');
							}
							else
							{
								$_SESSION['FixQueryList'][] = $sql;
								$strError .= GetMessage('SC_ERR_NO_FIELD', array('#TABLE#' => $table, '#FIELD#' => $f_tmp['Field']))."<br>";
								$this->arTestVars['iError']++;
								$this->arTestVars['iErrorAutoFix']++;
								$this->arTestVars['cntNoFields']++;
							}
						}
					}

					$arIndexes = array();
					$rs = $DB->Query('SHOW INDEXES FROM `'.$table.'`');
					while($f = $rs->Fetch())
					{
						$ix =& $arIndexes[$f['Key_name']];
						$column = strtolower($f['Column_name'].($f['Sub_part'] ? '('.$f['Sub_part'].')' : ''));
						if ($ix)
							$ix .= ','.$column;
						else
							$ix = $column;
					}

					$arIndexes_tmp = array();
					$rs = $DB->Query('SHOW INDEXES FROM `'.$tmp_table.'`');
					while($f = $rs->Fetch())
					{
						$ix =& $arIndexes_tmp[$f['Key_name']];
						$column = strtolower($f['Column_name'].($f['Sub_part'] ? '('.$f['Sub_part'].')' : ''));
						if ($ix)
							$ix .= ','.$column;
						else
							$ix = $column;
					}
					unset($ix); // unlink the reference
					foreach($arIndexes_tmp as $name => $ix)
					{
						if (!in_array($ix,$arIndexes))
						{
							while($arIndexes[$name])
								$name .= '_sc';
							$sql = $name == 'PRIMARY' ? 'ALTER TABLE `'.$table.'` ADD PRIMARY KEY ('.$ix.')' : 'CREATE INDEX `'.$name.'` ON `'.$table.'` ('.$ix.')';
							if ($this->fix_mode)
							{
								if (!$DB->Query($sql, true))
									return $this->Result(false, 'Mysql Query Error: '.$sql.' ['.$DB->db_Error.']');
							}
							else
							{
								$_SESSION['FixQueryList'][] = $sql;
								$strError .= GetMessage('SC_ERR_NO_INDEX', array('#TABLE#' => $table, '#INDEX#' => $name.' ('.$ix.')'))."<br>";
								$this->arTestVars['iError']++;
								$this->arTestVars['iErrorAutoFix']++;
								$this->arTestVars['cntNoIndexes']++;
							}
						}
					}

					$DB->Query('DROP TABLE `'.$tmp_table.'`');
				}
				echo $strError; // to log
			}
		}

		if ($iCurrent < $cnt) // partial
		{
			$this->arTestVars['last_value'] = $module;
			$this->test_percent = floor($iCurrent / $cnt * 100);
			return true;
		}

		if ($this->fix_mode)
		{
			if ($this->arTestVars['iErrorFix'] > 0)
				return $this->Result(null, GetMessage('SC_CHECK_TABLES_STRUCT_ERRORS_FIX', 
					array(
						'#VAL#' => intval($this->arTestVars['iErrorFix']),
					)));
			return true;
		}
		else
		{
			if ($this->arTestVars['iError'] > 0)
			{
				echo implode(";\n", $_SESSION['FixQueryList']).';';
				$_SESSION['FixQueryList'] = array();
				return $this->Result(false, GetMessage('SC_CHECK_TABLES_STRUCT_ERRORS', 
					array(
						'#VAL#' => intval($this->arTestVars['iError']),
						'#VAL1#' => intval($this->arTestVars['iErrorAutoFix']),
						'#NO_TABLES#' => intval($this->arTestVars['cntNoTables']),
						'#NO_FIELDS#' => intval($this->arTestVars['cntNoFields']),
						'#DIFF_FIELDS#' => intval($this->arTestVars['cntDiffFields']),
						'#NO_INDEXES#' => intval($this->arTestVars['cntNoIndexes']),
						'#NO_VALUES#' => intval($this->arTestVars['cntNoValues']),
					)).($this->arTestVars['iErrorAutoFix'] > 0 ? fix_link(3) : ''));
			}
			return true;
		}
	}
 function check_mysql_table_structure()
 {
     global $DB;
     $strError = '';
     if ($this->arTestVars['table_charset_fail']) {
         return $this->Result(null, GetMessage('SC_TABLE_COLLATION_NA'));
     }
     $module = '';
     $cnt = $i = 0;
     if ($dir = opendir($path = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules')) {
         while (false !== ($item = readdir($dir))) {
             if ($item == '.' || $item == '..') {
                 continue;
             }
             $cnt++;
             if ($this->arTestVars['last_value']) {
                 $i++;
                 if ($this->arTestVars['last_value'] == $item) {
                     unset($this->arTestVars['last_value']);
                 }
             } elseif (!$module) {
                 $module = $item;
             }
         }
         closedir($dir);
     } else {
         return false;
     }
     $file = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/' . $module . '/install/db/mysql/install.sql';
     if (!file_exists($file)) {
         $file = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/' . $module . '/install/mysql/install.sql';
     }
     if (file_exists($file)) {
         $rs = $DB->Query('SELECT * FROM b_module WHERE id="' . $DB->ForSQL($module) . '"');
         if ($rs->Fetch()) {
             if (!($query = file_get_contents($file))) {
                 return false;
             }
             $arTables = array();
             $arQuery = $DB->ParseSQLBatch(str_replace("\r", "", $query));
             foreach ($arQuery as $sql) {
                 if (preg_match('#^(CREATE TABLE )(IF NOT EXISTS)? ?`?([a-z0-9_]+)`?(.*);?$#mis', $sql, $regs)) {
                     $table = $regs[3];
                     if (preg_match('#^site_checker_#', $table)) {
                         continue;
                     }
                     $arTables[$table] = $sql;
                     $tmp_table = 'site_checker_' . $table;
                     $DB->Query('DROP TABLE IF EXISTS `' . $tmp_table . '`');
                     $DB->Query($regs[1] . ' `' . $tmp_table . '`' . $regs[4]);
                 } elseif (preg_match('#^(ALTER TABLE)( )?`?([a-z0-9_]+)`?(.*);?$#mis', $sql, $regs)) {
                     $table = $regs[3];
                     $tmp_table = 'site_checker_' . $table;
                     $DB->Query($regs[1] . ' `' . $tmp_table . '`' . $regs[4]);
                 }
             }
             foreach ($arTables as $table => $sql) {
                 $rs = $DB->Query('SHOW TABLES LIKE "' . $table . '"');
                 if (!$rs->Fetch()) {
                     if ($this->fix_mode) {
                         if (!$DB->Query($sql)) {
                             return $this->Result(false, 'Mysql Query Error: ' . $sql . ' [' . $DB->db_Error . ']');
                         }
                     } else {
                         $strError .= GetMessage('SC_ERR_NO_TABLE', array('#TABLE#' => $table)) . "<br>\n";
                         $this->arTestVars['iError']++;
                         $this->arTestVars['iErrorAutoFix']++;
                         $this->arTestVars['cntNoTables']++;
                     }
                     continue;
                 }
                 $tmp_table = 'site_checker_' . $table;
                 $arColumns = array();
                 $rs = $DB->Query('SHOW COLUMNS FROM `' . $table . '`');
                 while ($f = $rs->Fetch()) {
                     $arColumns[$f['Field']] = $f;
                 }
                 $rs = $DB->Query('SHOW COLUMNS FROM `' . $tmp_table . '`');
                 while ($f_tmp = $rs->Fetch()) {
                     $tmp = TableFieldConstruct($f_tmp);
                     if ($f = $arColumns[$f_tmp['Field']]) {
                         if (($cur = TableFieldConstruct($f)) != $tmp) {
                             if ($this->fix_mode) {
                                 if (TableFieldCanBeAltered($f, $f_tmp)) {
                                     if (!$DB->Query($sql = 'ALTER TABLE `' . $table . '` MODIFY `' . $f_tmp['Field'] . '` ' . $tmp, true)) {
                                         return $this->Result(false, 'Mysql Query Error: ' . $sql . ' [' . $DB->db_Error . ']');
                                     }
                                 } else {
                                     $this->arTestVars['iErrorFix']++;
                                 }
                             } else {
                                 $strError .= GetMessage('SC_ERR_FIELD_DIFFERS', array('#TABLE#' => $table, '#FIELD#' => $f['Field'], '#CUR#' => $cur, '#NEW#' => $tmp)) . "<br>\n";
                                 $this->arTestVars['iError']++;
                                 if (TableFieldCanBeAltered($f, $f_tmp)) {
                                     $this->arTestVars['iErrorAutoFix']++;
                                 }
                                 $this->arTestVars['cntDiffFields']++;
                             }
                         }
                     } else {
                         if ($this->fix_mode) {
                             if (!$DB->Query($sql = 'ALTER TABLE `' . $table . '` ADD `' . $f_tmp['Field'] . '` ' . $tmp, true)) {
                                 return $this->Result(false, 'Mysql Query Error: ' . $sql . ' [' . $DB->db_Error . ']');
                             }
                         } else {
                             $strError .= GetMessage('SC_ERR_NO_FIELD', array('#TABLE#' => $table, '#FIELD#' => $f_tmp['Field'])) . "<br>\n";
                             $this->arTestVars['iError']++;
                             $this->arTestVars['iErrorAutoFix']++;
                             $this->arTestVars['cntNoFields']++;
                         }
                     }
                 }
                 $arIndexes = array();
                 $rs = $DB->Query('SHOW INDEXES FROM `' . $table . '`');
                 while ($f = $rs->Fetch()) {
                     $ix =& $arIndexes[$f['Key_name']];
                     $column = $f['Column_name'] . ($f['Sub_part'] ? '(' . $f['Sub_part'] . ')' : '');
                     if ($ix) {
                         $ix .= ',' . $column;
                     } else {
                         $ix = $column;
                     }
                 }
                 $arIndexes_tmp = array();
                 $rs = $DB->Query('SHOW INDEXES FROM `' . $tmp_table . '`');
                 while ($f = $rs->Fetch()) {
                     $ix =& $arIndexes_tmp[$f['Key_name']];
                     $column = $f['Column_name'] . ($f['Sub_part'] ? '(' . $f['Sub_part'] . ')' : '');
                     if ($ix) {
                         $ix .= ',' . $column;
                     } else {
                         $ix = $column;
                     }
                 }
                 unset($ix);
                 // unlink the reference
                 foreach ($arIndexes_tmp as $name => $ix) {
                     if (!in_array($ix, $arIndexes)) {
                         if ($this->fix_mode) {
                             while ($arIndexes[$name]) {
                                 $name .= '_sc';
                             }
                             if (!$DB->Query($sql = 'CREATE INDEX `' . $name . '` ON `' . $table . '` (' . $ix . ')', true)) {
                                 return $this->Result(false, 'Mysql Query Error: ' . $sql . ' [' . $DB->db_Error . ']');
                             }
                         } else {
                             $strError .= GetMessage('SC_ERR_NO_INDEX', array('#TABLE#' => $table, '#INDEX#' => $name . ' (' . $ix . ')')) . "<br>\n";
                             $this->arTestVars['iError']++;
                             $this->arTestVars['iErrorAutoFix']++;
                             $this->arTestVars['cntNoIndexes']++;
                         }
                     }
                 }
                 $DB->Query('DROP TABLE `' . $tmp_table . '`');
             }
             echo $strError;
             // to log
         }
     }
     if ($i < $cnt) {
         $this->arTestVars['last_value'] = $module;
         $this->test_percent = floor($i / $cnt * 100);
         return true;
     }
     if ($this->fix_mode) {
         if ($this->arTestVars['iErrorFix'] > 0) {
             return $this->Result(null, GetMessage('SC_CHECK_TABLES_STRUCT_ERRORS_FIX', array('#VAL#' => intval($this->arTestVars['iErrorFix']))));
         }
         return true;
     } else {
         if ($this->arTestVars['iError'] > 0) {
             return $this->Result(false, GetMessage('SC_CHECK_TABLES_STRUCT_ERRORS', array('#VAL#' => intval($this->arTestVars['iError']), '#VAL1#' => intval($this->arTestVars['iErrorAutoFix']), '#NO_TABLES#' => intval($this->arTestVars['cntNoTables']), '#NO_FIELDS#' => intval($this->arTestVars['cntNoFields']), '#DIFF_FIELDS#' => intval($this->arTestVars['cntDiffFields']), '#NO_INDEXES#' => intval($this->arTestVars['cntNoIndexes']))) . ($this->arTestVars['iErrorAutoFix'] > 0 ? fix_link(3) : ''));
         }
         return true;
     }
 }