From 47bc0ec9706778e4b9edf1eed58947f6a8013f9c Mon Sep 17 00:00:00 2001 From: mcfly Date: Sat, 20 Dec 2008 00:55:29 +0000 Subject: [PATCH] More work on forum upgrade --- e107_handlers/db_table_admin_class.php | 978 +++++++++++++------------ e107_plugins/forum/forum_update.php | 104 ++- 2 files changed, 606 insertions(+), 476 deletions(-) diff --git a/e107_handlers/db_table_admin_class.php b/e107_handlers/db_table_admin_class.php index e812a8a0e..14137c765 100644 --- a/e107_handlers/db_table_admin_class.php +++ b/e107_handlers/db_table_admin_class.php @@ -11,513 +11,559 @@ | GNU General Public License (http://gnu.org). | | $Source: /cvs_backup/e107_0.8/e107_handlers/db_table_admin_class.php,v $ -| $Revision: 1.6 $ -| $Date: 2008-11-02 14:54:44 $ -| $Author: e107steved $ +| $Revision: 1.7 $ +| $Date: 2008-12-20 00:55:29 $ +| $Author: mcfly_e107 $ +----------------------------------------------------------------------------+ */ /* Database utilities for admin tasks: - Get structure of a table from a database - Get structure of a table from a file - First level parse of table structure - Parse of field definitions part of table structure - Comparison of two structures, including generation of MySQL to make them the same - Some crude printing utilities - +Get structure of a table from a database +Get structure of a table from a file +First level parse of table structure +Parse of field definitions part of table structure +Comparison of two structures, including generation of MySQL to make them the same +Some crude printing utilities + Note: there are some uncommented 'echo' statements which are intentional to highlight that something's gone wrong! (not that it should, of course) */ class db_table_admin { - var $file_buffer = ''; // Contents of a file - var $last_file = ''; - -// Get list of fields and keys for a table - return FALSE if unsuccessful -// Return as for get_table_def - function get_current_table($table_name, $prefix = "") - { - global $sql; + var $file_buffer = ''; // Contents of a file + var $last_file = ''; - if (!$prefix) $prefix = MPREFIX; -// echo "Get table structure for: {$table_name}, prefix: {$prefix}
"; - $sql->db_Select_gen('SET SQL_QUOTE_SHOW_CREATE = 1'); - $qry = 'SHOW CREATE TABLE `'.$prefix.$table_name."`"; - if (!($z = $sql->db_Select_gen($qry))) return FALSE; - $row = $sql->db_Fetch(); - $tmp = str_replace("`", "", stripslashes($row[1])).';'; // Add semicolon to work with our parser - $count = preg_match_all("#CREATE\s+?TABLE\s+?`{0,1}({$prefix}{$table_name})`{0,1}\s+?\((.*?)\)\s+?(?:TYPE|ENGINE)\s*\=\s*(.*?);#is",$tmp,$matches,PREG_SET_ORDER); - if ($count === FALSE) return "Error occurred"; - if (!$count) return "No matches"; - return $matches; - } - - -// Routine to do first-level parse of table structure -//--------------------------------------------------- -// Given the name of a file, returns an array, with each element being a table creation definition. -// Tracks the last file read - only reads it once -// If the file name is an empty string, uses a previously read/set buffer -// If a table name is given, returns only that table's info; otherwise returns a list of all tables -// The table name must include a prefix where appropriate (although not required with standard E107 table definition files) -// Each element is itself an array: -// [0] - The complete string which creates a table (unless a prefix needs adding to the table name), including terminating ';' -// [1] - The table name. Any backticks are stripped -// [2] - Field definitions, with the surrounding (...) stripped -// [3] - The 'TYPE' field ('TYPE=' is stripped) and any AUTO-INCREMENT definition or other text. -// function get_table_def($table_name='',$file_name = e_ADMIN."sql/core_sql.php") - function get_table_def($table_name='',$file_name ="") - { - if ($file_name != '') - { // Read in and buffer a new file (if we've not already got one) - if ($this->last_file != $file_name) - { - if (!is_readable($file_name)) return "No file"; - $temp = file_get_contents($file_name); - // Strip any php header - $this->file_buffer = preg_replace("#\<\?php.*?\?\>#mis",'',$temp); - $this->last_file = $file_name; - } - } - if (!$table_name) $table_name = '\w+?'; - // Regex should be identical to that in get_current_table (apart from the source text variable name) - $count = preg_match_all("#CREATE\s+?TABLE\s+?`{0,1}({$table_name})`{0,1}\s+?\((.*?)\)\s+?(?:TYPE|ENGINE)\s*\=\s*(.*?);#is",$this->file_buffer,$matches,PREG_SET_ORDER); - if ($count === FALSE) return "Error occurred"; - if (!$count) return "No matches"; - return $matches; - } - - -// Parses the block of lines which make up the field and index definitions -// Returns an array where each entry is the definitions of a field or index - function parse_field_defs($text) - { - $ans = array(); - $text = str_replace("\r","\n",$text); - $field_lines = explode("\n",$text); - foreach ($field_lines as $fv) + // Get list of fields and keys for a table - return FALSE if unsuccessful + // Return as for get_table_def + function get_current_table($table_name, $prefix = "") { - unset($defs); - $fv = trim(str_replace(' ',' ',$fv)); - if (substr($fv,-1) == ',') $fv = trim(substr($fv,0,-1)); -// echo "Line: ".$fv."
"; - if ($fv) - { - $fd = explode(' ',$fv); - switch (strtoupper($fd[0])) - { - case 'PRIMARY' : - if (strtoupper($fd[1]) == 'KEY') - $defs['type'] = 'pkey'; - $defs['name'] = $fd[2]; - break; - case 'UNIQUE' : - if (count($fd) < 3) + global $sql; + + if (!$prefix) { $prefix = MPREFIX; } + // echo "Get table structure for: {$table_name}, prefix: {$prefix}
"; + $sql->db_Select_gen('SET SQL_QUOTE_SHOW_CREATE = 1'); + $qry = 'SHOW CREATE TABLE `'.$prefix.$table_name."`"; + if (!($z = $sql->db_Select_gen($qry))) { return FALSE; } + $row = $sql->db_Fetch(); + $tmp = str_replace("`", "", stripslashes($row[1])).';'; // Add semicolon to work with our parser + $count = preg_match_all("#CREATE\s+?TABLE\s+?`{0,1}({$prefix}{$table_name})`{0,1}\s+?\((.*?)\)\s+?(?:TYPE|ENGINE)\s*\=\s*(.*?);#is",$tmp,$matches,PREG_SET_ORDER); + if ($count === FALSE) { return "Error occurred";} + if (!$count) { return "No matches"; } + return $matches; + } + + + // Routine to do first-level parse of table structure + //--------------------------------------------------- + // Given the name of a file, returns an array, with each element being a table creation definition. + // Tracks the last file read - only reads it once + // If the file name is an empty string, uses a previously read/set buffer + // If a table name is given, returns only that table's info; otherwise returns a list of all tables + // The table name must include a prefix where appropriate (although not required with standard E107 table definition files) + // Each element is itself an array: + // [0] - The complete string which creates a table (unless a prefix needs adding to the table name), including terminating ';' + // [1] - The table name. Any backticks are stripped + // [2] - Field definitions, with the surrounding (...) stripped + // [3] - The 'TYPE' field ('TYPE=' is stripped) and any AUTO-INCREMENT definition or other text. + // function get_table_def($table_name='',$file_name = e_ADMIN."sql/core_sql.php") + function get_table_def($table_name='',$file_name ="") + { + if ($file_name != '') + { // Read in and buffer a new file (if we've not already got one) + if ($this->last_file != $file_name) { - echo "Truncated definition after UNIQUE {$i}: ".$fd[1]."
"; + if (!is_readable($file_name)) { return "No file"; } + $temp = file_get_contents($file_name); + // Strip any php header + $this->file_buffer = preg_replace("#\<\?php.*?\?\>#mis",'',$temp); + $this->last_file = $file_name; } - elseif (strtoupper($fd[1]) == 'KEY') + } + if (!$table_name) { $table_name = '\w+?'; } + // Regex should be identical to that in get_current_table (apart from the source text variable name) + $count = preg_match_all("#CREATE\s+?TABLE\s+?`{0,1}({$table_name})`{0,1}\s+?\((.*?)\)\s+?(?:TYPE|ENGINE)\s*\=\s*(.*?);#is",$this->file_buffer,$matches,PREG_SET_ORDER); + if ($count === false) { return "Error occurred"; } + if (!$count) { return "No matches"; } + return $matches; + } + + + // Parses the block of lines which make up the field and index definitions + // Returns an array where each entry is the definitions of a field or index + function parse_field_defs($text) + { + $ans = array(); + $text = str_replace("\r","\n",$text); + $field_lines = explode("\n",$text); + foreach ($field_lines as $fv) + { + unset($defs); + $fv = trim(str_replace(' ',' ',$fv)); + if (substr($fv,-1) == ',') { $fv = trim(substr($fv,0,-1)); } + // echo "Line: ".$fv."
"; + if ($fv) { - $defs['type'] = 'ukey'; - $defs['name'] = $fd[2]; - if (isset($fd[3])) $defs['keyfield'] = $fd[3]; else $defs['keyfield'] = '['.$fd[2].']'; + $fd = explode(' ',$fv); + switch (strtoupper($fd[0])) + { + case 'PRIMARY' : + if (strtoupper($fd[1]) == 'KEY') + $defs['type'] = 'pkey'; + $defs['name'] = $fd[2]; + break; + case 'UNIQUE' : + if (count($fd) < 3) + { + echo "Truncated definition after UNIQUE {$i}: ".$fd[1]."
"; + } + elseif (strtoupper($fd[1]) == 'KEY') + { + $defs['type'] = 'ukey'; + $defs['name'] = $fd[2]; + if (isset($fd[3])) $defs['keyfield'] = $fd[3]; else $defs['keyfield'] = '['.$fd[2].']'; + } + else + { + echo "Unrecognised word after UNIQUE in definition {$i}: ".$fd[1]."
"; + } + break; + case 'KEY' : + $defs['type'] = 'key'; + $defs['name'] = $fd[1]; + if (isset($fd[2])) { + $defs['keyfield'] = $fd[2]; + } + else + { + $defs['keyfield'] = '['.$fd[1].']'; + } + break; + default : // Must be a DB field name + $defs['type'] = 'field'; + $defs['name'] = $fd[0]; + $defs['fieldtype'] = $fd[1]; + $i = 2; // First unused field + if ((strpos($fd[1],'int') === 0) || (strpos($fd[1],'tinyint') === 0) || (strpos($fd[1],'smallint') === 0) || (strpos($fd[1],'bigint') === 0)) + { + if (isset($fd[2]) && (strtoupper($fd[2]) == 'UNSIGNED')) + { + $defs['vartype'] = $fd[2]; + $i++; + } + } + while ($i < count($fd)) + { + switch(strtoupper($fd[$i])) + { + case 'NOT' : + if (isset($fd[$i+1]) && strtoupper($fd[$i+1]) == 'NULL') + { + $i++; + $defs['nulltype'] = 'NOT NULL'; + } + else + { // Syntax error + echo "Unrecognised word in definition {$i} after 'NOT': ".$fd[$i+1]."
"; + } + break; + case 'DEFAULT' : + if (isset($fd[$i+1])) + { + $i++; + $defs['default'] = $fd[$i]; + } + break; + case 'COLLATE' : + $i++; // Just skip over - we ignore collation + break; + case 'AUTO_INCREMENT' : + $defs['autoinc'] = TRUE; + break; + default : + echo "Unknown definition {$i}: ".$fd[$i]."
"; + } + $i++; + } + } + if (count($defs) > 1) + { + $ans[] = $defs; + } + else + { + echo "Partial definition
"; + } + } + } + if (!count($ans)) { return FALSE; } + return $ans; + } + + + + // Utility routine - given our array-based definition, create a string MySQL field definition + function make_def($list) + { + switch ($list['type']) + { + case 'key' : + return 'KEY '.$list['name'].' ('.str_replace(array('(',')'),'',$list['keyfield']).')'; + case 'ukey' : + return 'UNIQUE KEY '.$list['name'].' ('.$list['name'].')'; + case 'pkey' : + return 'PRIMARY KEY ('.$list['name'].')'; + case 'field' : // Require a field - got a key. so add a field at the end + $def = $list['name']; + if (isset($list['fieldtype'])) { $def .= ' '.$list['fieldtype']; } + if (isset($list['vartype'])) { $def .= ' '.$list['vartype']; } + if (isset($list['nulltype'])) { $def .= ' '.$list['nulltype']; } + if (isset($list['default'])) { $def .= ' default '.$list['default']; } + if (varsettrue($list['autoinc'])) { $def .= ' auto_increment'; } + return $def; + } + return "Cannot generate definition for: ".$list['type'].' '.$list['name']; + } + + + // Compare two field/index lists as generated by parse_field_defs + // If $stop_on_error is TRUE, returns TRUE if the same, false if different + // Return a text list of differences, plus an array of MySQL queries to fix + // List1 is the reference, List 2 is the actual + // This version looks ahead on a failed match, and moves a field up in the table if already defined - should retain as much as possible + function compare_field_lists($list1, $list2, $stop_on_error=FALSE) + { + $i = 0; // Counts records in list1 (required format) + $j = 0; // Counts records in $created_list (our 'table so far' list) + $error_list = array(); // Free text list of differences + $change_list = array(); // MySQL statements to implement changes + $created_list = array(); // List of field defs that we build up (just names) + while ($i < count($list1)) + { + if (count($list2) == 0) + { // Missing field at end + if ($stop_on_error) { return FALSE; } + $error_list[] = 'Missing field at end: '.$list1[$i]['name']; + $change_list[] = 'ADD '.$this->make_def($list1[$i]); + $created_list[$j] = $list1[$i]['name']; + $j++; + } + elseif ($list1[$i]['type'] == $list2[0]['type']) + { // Worth doing a compare - fields are same type + // echo $i.': compare - '.$list1[$i]['name'].', '.$list2[0]['name'].'
'; + if (strcasecmp($list1[$i]['name'],$list2[0]['name']) != 0) + { // Names differ, so need to add or subtract a field. + // echo $i.': names differ - '.$list1[$i]['name'].', '.$list2[0]['name'].'
'; + if ($stop_on_error) { return FALSE; } + $found = FALSE; + for ($k = $i+1; $k < count($list1); $k++) + { + // echo "Compare ".$list1[$k]['name'].' with '.$list2[0]['name']; + if (strcasecmp($list1[$k]['name'],$list2[0]['name']) == 0) + { // Field in list2 found later in list1; do nothing + // echo " - match
"; + $found = TRUE; + break; + } + // echo " - no match
"; + } + + if (!$found) + { // Field in existing DB no longer required + $error_list[] = 'Obsolete field: '.$list2[0]['name']; + $change_list[] = 'DROP '.($list2[0]['type'] == 'field' ? '' : 'INDEX ').$list2[0]['name']; + array_shift($list2); + continue; + } + + $found = FALSE; + for ($k = 0; $k < count($list2); $k++) + { + // echo "Compare ".$list1[$i]['name'].' with '.$list2[$k]['name']; + if (strcasecmp($list1[$i]['name'],$list2[$k]['name']) == 0) + { // Field found; we need to move it up + // echo " - match
"; + $found = TRUE; + break; + } + // echo " - no match
"; + } + if ($found) + { + $error_list[] = 'Field out of position: '.$list2[$k]['name']; + $change_list[] = 'MODIFY '.$this->make_def($list1[$i]).(count($created_list) ? ' AFTER '.$created_list[count($created_list)-1] : ' FIRST'); + array_splice($list2,$k,1); // Finished with this element - delete it, and renumber the keys + $created_list[$j] = $list1[$i]['name']; + $j++; + // The above also amends any parameters as necessary + } + else + { // Need to insert a field + $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; + switch ($list1[$i]['type']) + { + case 'key' : + case 'ukey' : + case 'pkey' : // Require a key + $change_list[] = 'ADD '.$this->make_def($list1[$i]); + $error_list[] = 'Missing index: '.$list1[$i]['name']; + $created_list[$j] = $list1[$i]['name']; + $j++; + break; + + case 'field' : + $change_list[] = 'ADD '.$this->make_def($list1[$i]).(count($created_list) ? ' AFTER '.$created_list[count($created_list)-1] : ' FIRST'); + $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; + $created_list[$j] = $list1[$i]['name']; + $j++; + break; + } + } + } + else + { // Field/index is present as required; may be changes though + // Any difference and we need to update the table + // echo $i.': name match - '.$list1[$i]['name'].'
'; + foreach ($list1[$i] as $fi => $v) + { + $t = $list2[0][$fi]; + if (stripos($v,'varchar') !== FALSE) { $v = substr($v,3); } // Treat char, varchar the same + if (stripos($t,'varchar') !== FALSE) { $t = substr($t,3); } // Treat char, varchar the same + if (strcasecmp($t , $v) !== 0) + { + if ($stop_on_error) { return FALSE; } + $error_list[] = 'Incorrect definition: '.$fi.' = '.$v; + $change_list[] = 'MODIFY '.$this->make_def($list1[$i]); + break; + } + } + array_shift($list2); + $created_list[$j] = $list1[$i]['name']; + $j++; + } } else + { // Field type has changed. We know fields come before indexes. So something's missing + // echo $i.': types differ - '.$list1[$i]['type'].' '.$list1[$i]['name'].', '.$list2[$k]['type'].' '.$list2[$k]['name'].'
'; + if ($stop_on_error) { return FALSE; } + switch ($list1[$i]['type']) + { + case 'key' : + case 'ukey' : + case 'pkey' : // Require a key - got a field, or a key of a different type + while ((count($list2)>0) && ($list2[0]['type'] == 'field')) + { + $error_list[] = 'Extra field: '.$list2[0]['name']; + $change_list[] = 'DROP '.$list2[0]['name']; + array_shift($list2); + } + if ((count($list2) == 0) || ($list1[$i]['type'] != $list2[0]['type'])) + { // need to add a key + $change_list[] = 'ADD '.$this->make_def($list1[$i]); + $error_list[] = 'Missing index: '.$list1[$i]['name']; + $created_list[$j] = $list1[$i]['name']; + $j++; + } + break; + + case 'field' : // Require a field - got a key. so add a field at the end + $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; + $change_list[] = 'ADD '.$this->make_def($list1[$i]); + break; + + default : + $error_list[] = 'Unknown field type: '.$list1[$i]['type']; + $change_list[] = ''; // Null entry to keep them in step + } + } // End - missing or extra field + + $i++; // On to next field + } + if (count($list2)) + { // Surplus fields in actual table + // Echo count($list2)." fields at end to delete
"; + foreach ($list2 as $f) { - echo "Unrecognised word after UNIQUE in definition {$i}: ".$fd[1]."
"; - } - break; - case 'KEY' : - $defs['type'] = 'key'; - $defs['name'] = $fd[1]; - if (isset($fd[2])) $defs['keyfield'] = $fd[2]; else $defs['keyfield'] = '['.$fd[1].']'; - break; - default : // Must be a DB field name - $defs['type'] = 'field'; - $defs['name'] = $fd[0]; - $defs['fieldtype'] = $fd[1]; - $i = 2; // First unused field - if ((strpos($fd[1],'int') === 0) || (strpos($fd[1],'tinyint') === 0) || (strpos($fd[1],'smallint') === 0) || (strpos($fd[1],'bigint') === 0)) - { - if (isset($fd[2]) && (strtoupper($fd[2]) == 'UNSIGNED')) - { - $defs['vartype'] = $fd[2]; - $i++; - } - } - while ($i < count($fd)) - { - switch(strtoupper($fd[$i])) - { - case 'NOT' : - if (isset($fd[$i+1]) && strtoupper($fd[$i+1]) == 'NULL') - { - $i++; - $defs['nulltype'] = 'NOT NULL'; - } - else - { // Syntax error - echo "Unrecognised word in definition {$i} after 'NOT': ".$fd[$i+1]."
"; - } - break; - case 'DEFAULT' : - if (isset($fd[$i+1])) - { - $i++; - $defs['default'] = $fd[$i]; - } - break; - case 'COLLATE' : - $i++; // Just skip over - we ignore collation - break; - case 'AUTO_INCREMENT' : - $defs['autoinc'] = TRUE; - break; - default : - echo "Unknown definition {$i}: ".$fd[$i]."
"; - } - $i++; + switch ($f['type']) + { + case 'key' : + case 'ukey' : + case 'pkey' : // Require a key - got a field + $error_list[] = 'Extra index: '.$list2[0]['name']; + $change_list[] = 'DROP INDEX '.$list2[0]['name']; + break; + case 'field' : + $error_list[] = 'Extra field: '.$list2[0]['name']; + $change_list[] = 'DROP '.$list2[0]['name']; + break; + } } } - if (count($defs) > 1) $ans[] = $defs; else echo "Partial definition
"; - } + if ($stop_on_error) return TRUE; // If doing a simple comparison and we get to here, all matches + return array($error_list, $change_list); } - if (!count($ans)) return FALSE; - return $ans; - } - - - // Utility routine - given our array-based definition, create a string MySQL field definition - function make_def($list) - { - switch ($list['type']) + function make_changes_list($result) { - case 'key' : - return 'KEY '.$list['name'].' ('.str_replace(array('(',')'),'',$list['keyfield']).')'; - case 'ukey' : - return 'UNIQUE KEY '.$list['name'].' ('.$list['name'].')'; - case 'pkey' : - return 'PRIMARY KEY ('.$list['name'].')'; - case 'field' : // Require a field - got a key. so add a field at the end - $def = $list['name']; - if (isset($list['fieldtype'])) $def .= ' '.$list['fieldtype']; - if (isset($list['vartype'])) $def .= ' '.$list['vartype']; - if (isset($list['nulltype'])) $def .= ' '.$list['nulltype']; - if (isset($list['default'])) $def .= ' default '.$list['default']; - if (varsettrue($list['autoinc'])) $def .= ' auto_increment'; - return $def; + if (!is_array($result)) { return "Not an array
"; } + $text = ""; + for ($i = 0; $i < count($result[0]); $i++) + { + $text .= ""; + $text .= ""; + $text .= "\n"; + } + $text .= "
{$result[0][$i]}{$result[1][$i]}


"; + return $text; } - return "Cannot generate definition for: ".$list['type'].' '.$list['name']; - } - - // Compare two field/index lists as generated by parse_field_defs - // If $stop_on_error is TRUE, returns TRUE if the same, false if different - // Return a text list of differences, plus an array of MySQL queries to fix - // List1 is the reference, List 2 is the actual - // This version looks ahead on a failed match, and moves a field up in the table if already defined - should retain as much as possible - function compare_field_lists($list1, $list2, $stop_on_error=FALSE) - { - $i = 0; // Counts records in list1 (required format) - $j = 0; // Counts records in $created_list (our 'table so far' list) - $error_list = array(); // Free text list of differences - $change_list = array(); // MySQL statements to implement changes - $created_list = array(); // List of field defs that we build up (just names) - while ($i < count($list1)) + + // Return a table of info from the output of get_table_def + function make_table_list($result) { - if (count($list2) == 0) - { // Missing field at end - if ($stop_on_error) return FALSE; - $error_list[] = 'Missing field at end: '.$list1[$i]['name']; - $change_list[] = 'ADD '.$this->make_def($list1[$i]); - $created_list[$j] = $list1[$i]['name']; - $j++; - } - elseif ($list1[$i]['type'] == $list2[0]['type']) - { // Worth doing a compare - fields are same type -// echo $i.': compare - '.$list1[$i]['name'].', '.$list2[0]['name'].'
'; - if (strcasecmp($list1[$i]['name'],$list2[0]['name']) != 0) - { // Names differ, so need to add or subtract a field. -// echo $i.': names differ - '.$list1[$i]['name'].', '.$list2[0]['name'].'
'; - if ($stop_on_error) return FALSE; - $found = FALSE; - for ($k = $i+1; $k < count($list1); $k++) - { -// echo "Compare ".$list1[$k]['name'].' with '.$list2[0]['name']; - if (strcasecmp($list1[$k]['name'],$list2[0]['name']) == 0) - { // Field in list2 found later in list1; do nothing -// echo " - match
"; - $found = TRUE; - break; - } -// echo " - no match
"; - } + if (!is_array($result)) { return "Not an array
"; } + $text = ""; + for ($i = 0; $i < count($result); $i++) + { + $text .= ""; + $text .= ""; + $text .= ""; + $text .= "\n"; + } + $text .= "
{$result[$i][0]}{$result[$i][1]}{$result[$i][2]}{$result[$i][3]}


"; + return $text; + } - if (!$found) - { // Field in existing DB no longer required - $error_list[] = 'Obsolete field: '.$list2[0]['name']; - $change_list[] = 'DROP '.($list2[0]['type'] == 'field' ? '' : 'INDEX ').$list2[0]['name']; - array_shift($list2); - continue; - } - $found = FALSE; - for ($k = 0; $k < count($list2); $k++) - { -// echo "Compare ".$list1[$i]['name'].' with '.$list2[$k]['name']; - if (strcasecmp($list1[$i]['name'],$list2[$k]['name']) == 0) - { // Field found; we need to move it up -// echo " - match
"; - $found = TRUE; - break; - } -// echo " - no match
"; - } - if ($found) - { - $error_list[] = 'Field out of position: '.$list2[$k]['name']; - $change_list[] = 'MODIFY '.$this->make_def($list1[$i]).(count($created_list) ? ' AFTER '.$created_list[count($created_list)-1] : ' FIRST'); - array_splice($list2,$k,1); // Finished with this element - delete it, and renumber the keys - $created_list[$j] = $list1[$i]['name']; - $j++; - // The above also amends any parameters as necessary - } - else - { // Need to insert a field - $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; - switch ($list1[$i]['type']) + // Return a table of info from the output of parse_field_defs() + function make_field_list($fields) + { + $text = ""; + foreach ($fields as $f) + { + switch($f['type']) { - case 'key' : - case 'ukey' : - case 'pkey' : // Require a key - $change_list[] = 'ADD '.$this->make_def($list1[$i]); - $error_list[] = 'Missing index: '.$list1[$i]['name']; - $created_list[$j] = $list1[$i]['name']; - $j++; + case 'pkey' : + $text .= ""; break; - - case 'field' : - $change_list[] = 'ADD '.$this->make_def($list1[$i]).(count($created_list) ? ' AFTER '.$created_list[count($created_list)-1] : ' FIRST'); - $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; - $created_list[$j] = $list1[$i]['name']; - $j++; + case 'ukey' : + $text .= ""; break; + case 'key' : + $text .= ""; + break; + case 'field' : + $text .= ""; + if (isset($f['nulltype'])) + { + $text .= ""; + } + else + { + $text .= ""; + } + if (isset($f['default'])) + { + $text .= ""; + } + elseif (isset($f['autoinc'])) + { + $text .= ""; + } + else + { + $text .= ""; + } + $text .= ""; + break; + default : + $text .= ""; } - } + } + $text .= "
PRIMARY KEY{$f['name']} 
UNIQUE KEY{$f['name']}{$f['keyfield']}
KEY{$f['name']}{$f['keyfield']}
FIELD{$f['name']}{$f['fieldtype']}"; + if (isset($f['vartype'])) { $text .= " ".$f['vartype']; } + $text .= "{$f['nulltype']} default {$f['default']}AUTO_INCREMENT 
!!Unknown!!{$f['type']} 


--Ends--
"; + return $text; + } + + + + + //-------------------------------------------------- + // Update a table to required structure + //-------------------------------------------------- + + // $newStructure is an array element as returned from get_table_def() + // If $mlUpdate is TRUE, applies same query to all tables of same language + // Return TRUE on success. + // Return text string if $justCheck is TRUE and changes needed + // Return text string on most failures + // Return FALSE on certain failures (generally indicative of code/system problems) + function update_table_structure($newStructure, $justCheck=FALSE, $makeNewifNotExist = TRUE, $mlUpdate = FALSE) + { + global $sql; + // Pull out table name + $tableName = $newStructure[1]; + if (!$sql->db_Table_exists($tableName)) + { + if ($makeNewifNotExist === FALSE) { return 'Table doesn\'t exist'; } + if ($sql->db_Select_gen($newStructure[0])) { return TRUE; } + return 'Error creating new table: '.$tableName; + } + $reqFields = $this->parse_field_defs($newStructure[2]); // Required field definitions + if (E107_DBG_FILLIN8) { echo "Required table structure:
".$this->make_field_list($reqFields); } + + if ((($actualDefs = $this->get_current_table($tableName)) === FALSE) || !is_array($actualDefs)) // Get actual table definition (Adds current default prefix) + { + return "Couldn't get table structure: {$tableName}
"; } else - { // Field/index is present as required; may be changes though - // Any difference and we need to update the table -// echo $i.': name match - '.$list1[$i]['name'].'
'; - foreach ($list1[$i] as $fi => $v) - { - $t = $list2[0][$fi]; - if (stripos($v,'varchar') !== FALSE) $v = substr($v,3); // Treat char, varchar the same - if (stripos($t,'varchar') !== FALSE) $t = substr($t,3); // Treat char, varchar the same - if (strcasecmp($t , $v) !== 0) - { - if ($stop_on_error) return FALSE; - $error_list[] = 'Incorrect definition: '.$fi.' = '.$v; - $change_list[] = 'MODIFY '.$this->make_def($list1[$i]); - break; - } - } - array_shift($list2); - $created_list[$j] = $list1[$i]['name']; - $j++; - } - } - else - { // Field type has changed. We know fields come before indexes. So something's missing -// echo $i.': types differ - '.$list1[$i]['type'].' '.$list1[$i]['name'].', '.$list2[$k]['type'].' '.$list2[$k]['name'].'
'; - if ($stop_on_error) return FALSE; - switch ($list1[$i]['type']) { - case 'key' : - case 'ukey' : - case 'pkey' : // Require a key - got a field, or a key of a different type - while ((count($list2)>0) && ($list2[0]['type'] == 'field')) - { - $error_list[] = 'Extra field: '.$list2[0]['name']; - $change_list[] = 'DROP '.$list2[0]['name']; - array_shift($list2); - } - if ((count($list2) == 0) || ($list1[$i]['type'] != $list2[0]['type'])) - { // need to add a key - $change_list[] = 'ADD '.$this->make_def($list1[$i]); - $error_list[] = 'Missing index: '.$list1[$i]['name']; - $created_list[$j] = $list1[$i]['name']; - $j++; - } - break; + // echo $db_parser->make_table_list($actual_defs); + $actualFields = $this->parse_field_defs($actualDefs[0][2]); // Split into field definitions + if (E107_DBG_FILLIN8) { echo 'Actual table structure:
'.$this->make_field_list($actualFields); } - case 'field' : // Require a field - got a key. so add a field at the end - $error_list[] = 'Missing field: '.$list1[$i]['name'].' (found: '.$list2[0]['type'].' '.$list2[0]['name'].')'; - $change_list[] = 'ADD '.$this->make_def($list1[$i]); - break; - - default : - $error_list[] = 'Unknown field type: '.$list1[$i]['type']; - $change_list[] = ''; // Null entry to keep them in step - } - } // End - missing or extra field - - $i++; // On to next field + $diffs = $this->compare_field_lists($reqFields,$actualFields); // Work out any differences + if (count($diffs[0])) + { // Changes needed + if ($justCheck) { return 'Field changes rqd; table: '.$tableName.'
'; } + // Do the changes here + if (E107_DBG_FILLIN8) { echo "List of changes found:
".$this->make_changes_list($diffs); } + $qry = 'ALTER TABLE '.MPREFIX.$tableName.' '.implode(', ',$diffs[1]); + if (E107_DBG_FILLIN8) { echo 'Update Query used: '.$qry.'
'; } + if ($mlUpdate) + { + $ret = $sql->db_Query_all($qry); // Returns TRUE = success, FALSE = fail + } + else + { + $ret = $sql->db_Select_gen($qry); + } + if ($ret === FALSE) + { + return $sql->dbError() ; + } + } + return TRUE; // Success even if no changes required + } + return FALSE; } - if (count($list2)) - { // Surplus fields in actual table -// Echo count($list2)." fields at end to delete
"; - foreach ($list2 as $f) - { - switch ($f['type']) + + + function createTable($pathToSqlFile='', $tableName='', $addPrefix=true, $renameTable='') + { + $e107 = e107::getInstance(); + $tmp = $this->get_table_def($tableName, $pathToSqlFile); + $createText = $tmp[0][0]; + $newTableName = ($renameTable ? $renameTable : $tableName); + if($addPrefix) { $newTableName = MPREFIX.$newTableName; } + if($newTableName != $tableName) { - case 'key' : - case 'ukey' : - case 'pkey' : // Require a key - got a field - $error_list[] = 'Extra index: '.$list2[0]['name']; - $change_list[] = 'DROP INDEX '.$list2[0]['name']; - break; - case 'field' : - $error_list[] = 'Extra field: '.$list2[0]['name']; - $change_list[] = 'DROP '.$list2[0]['name']; - break; + $createText = preg_replace('#create +table +(\w*?) +#i', 'CREATE TABLE '.$newTableName.' ', $createText); } - } + return $e107->sql->db_Select_gen($createText); } - if ($stop_on_error) return TRUE; // If doing a simple comparison and we get to here, all matches - return array($error_list, $change_list); - } - function make_changes_list($result) - { - if (!is_array($result)) return "Not an array
"; - $text = ""; - for ($i = 0; $i < count($result[0]); $i++) - { - $text .= ""; - $text .= ""; - $text .= "\n"; - } - $text .= "
{$result[0][$i]}{$result[1][$i]}


"; - return $text; - } - - - // Return a table of info from the output of get_table_def - function make_table_list($result) - { - if (!is_array($result)) return "Not an array
"; - $text = ""; - for ($i = 0; $i < count($result); $i++) - { - $text .= ""; - $text .= ""; - $text .= ""; - $text .= "\n"; - } - $text .= "
{$result[$i][0]}{$result[$i][1]}{$result[$i][2]}{$result[$i][3]}


"; - return $text; - } - - - // Return a table of info from the output of parse_field_defs() - function make_field_list($fields) - { - $text = ""; - foreach ($fields as $f) - { - switch($f['type']) - { - case 'pkey' : - $text .= ""; - break; - case 'ukey' : - $text .= ""; - break; - case 'key' : - $text .= ""; - break; - case 'field' : - $text .= ""; - if (isset($f['nulltype'])) $text .= ""; else $text .= ""; - if (isset($f['default'])) $text .= ""; elseif - (isset($f['autoinc'])) $text .= ""; else $text .= ""; - $text .= ""; - break; - default : - $text .= ""; - } - } - $text .= "
PRIMARY KEY{$f['name']} 
UNIQUE KEY{$f['name']}{$f['keyfield']}
KEY{$f['name']}{$f['keyfield']}
FIELD{$f['name']}{$f['fieldtype']}"; - if (isset($f['vartype'])) $text .= " ".$f['vartype']; - $text .= "{$f['nulltype']} default {$f['default']}AUTO_INCREMENT 
!!Unknown!!{$f['type']} 


--Ends--
"; - return $text; - } - - - - - //-------------------------------------------------- - // Update a table to required structure - //-------------------------------------------------- - - // $newStructure is an array element as returned from get_table_def() - // If $mlUpdate is TRUE, applies same query to all tables of same language - // Return TRUE on success. - // Return text string if $justCheck is TRUE and changes needed - // Return text string on most failures - // Return FALSE on certain failures (generally indicative of code/system problems) - function update_table_structure($newStructure, $justCheck=FALSE, $makeNewifNotExist = TRUE, $mlUpdate = FALSE) - { - global $sql; - // Pull out table name - $tableName = $newStructure[1]; - if (!$sql->db_Table_exists($tableName)) - { - if ($makeNewifNotExist === FALSE) return 'Table doesn\'t exist'; - if ($sql->db_Select_gen($newStructure[0])) return TRUE; - return 'Error creating new table: '.$tableName; - } - $reqFields = $this->parse_field_defs($newStructure[2]); // Required field definitions - if (E107_DBG_FILLIN8) echo "Required table structure:
".$this->make_field_list($reqFields); - - if ((($actualDefs = $this->get_current_table($tableName)) === FALSE) || !is_array($actualDefs)) // Get actual table definition (Adds current default prefix) - { - return "Couldn't get table structure: {$tableName}
"; - } - else - { -// echo $db_parser->make_table_list($actual_defs); - $actualFields = $this->parse_field_defs($actualDefs[0][2]); // Split into field definitions - if (E107_DBG_FILLIN8) echo 'Actual table structure:
'.$this->make_field_list($actualFields); - - $diffs = $this->compare_field_lists($reqFields,$actualFields); // Work out any differences - if (count($diffs[0])) - { // Changes needed - if ($justCheck) return 'Field changes rqd; table: '.$tableName.'
'; - // Do the changes here - if (E107_DBG_FILLIN8) echo "List of changes found:
".$this->make_changes_list($diffs); - $qry = 'ALTER TABLE '.MPREFIX.$tableName.' '.implode(', ',$diffs[1]); - if (E107_DBG_FILLIN8) echo 'Update Query used: '.$qry.'
'; - if ($mlUpdate) - { - $ret = $sql->db_Query_all($qry); // Returns TRUE = success, FALSE = fail - } - else - { - $ret = $sql->db_Select_gen($qry); - } - if ($ret === FALSE) - { - return $sql->dbError() ; - } - } - return TRUE; // Success even if no changes required - } - return FALSE; - } } diff --git a/e107_plugins/forum/forum_update.php b/e107_plugins/forum/forum_update.php index 8cd0da74c..1581342eb 100644 --- a/e107_plugins/forum/forum_update.php +++ b/e107_plugins/forum/forum_update.php @@ -11,8 +11,8 @@ | GNU General Public License (http://gnu.org). | | $Source: /cvs_backup/e107_0.8/e107_plugins/forum/forum_update.php,v $ -| $Revision: 1.4 $ -| $Date: 2008-12-19 21:56:37 $ +| $Revision: 1.5 $ +| $Date: 2008-12-20 00:55:29 $ | $Author: mcfly_e107 $ +----------------------------------------------------------------------------+ */ @@ -67,10 +67,9 @@ $stepParms = (isset($stepParms) ? $stepParms : ''); if(function_exists('step'.$currentStep)) { - call_user_func('step'.$currentStep, $stepParms); + $result = call_user_func('step'.$currentStep, $stepParms); } - require(e_ADMIN.'footer.php'); exit; @@ -91,7 +90,7 @@ function step1() "; foreach($f->error['attach'] as $e) { - $errorText .= '** '.$e.'
'; + $text .= '** '.$e.'
'; } $text .= "
@@ -131,19 +130,105 @@ function step2() return; } - if($sql = file_get_contents(e_PLUGIN.'forum/forum_sql.php')) + require_once(e_HANDLER.'db_table_admin_class.php'); + $db = new db_table_admin; + + $tabList = array('forum' => 'forum_new', 'forum_thread' => '', 'forum_post' => '', 'forum_track' => ''); + $ret = ''; + $failed = false; + $text = ''; + foreach($tabList as $name => $rename) { - echo $sql; + $text .= 'Creating table '.($rename ? $rename : $name).' -> '; + $result = $db->createTable(e_PLUGIN.'forum/forum_sql.php', $name, true, $rename); + if($result) + { + $text .= 'Success
'; + } + else + { + $text .= 'Failed
'; + $failed = true; + } + } + if($failed) + { + $text .= " +

+ Creation of table(s) failed. You can not continue until these are create successfully! + "; } else { - echo 'failed'; + $text .= " +

+
+ +
+ "; } + $e107->ns->tablerender('Step 2: Forum table creation', $text); +} +function step3() +{ + $e107 = e107::getInstance(); + $stepCaption = 'Step 3: Extended user field creation'; + if(!isset($_POST['create_extended'])) + { + $text = " + This step will create the new extended user fields required for the new forum code:
+ * user_plugin_forum_posts (to track number of posts for each user)
+ * user_plugin_forum_viewed (to track threads viewed by each user
+

+
+ +
+ "; + $e107->ns->tablerender($stepCaption, $text); + return; + } + require_once(e_HANDLER.'user_extended_class.php'); + $ue = new e107_user_extended; + $fieldList = array( + 'plugin_forum_posts' => EUF_INTEGER, + 'plugin_forum_viewed' => EUF_TEXTAREA + ); + $failed = false; + foreach($fieldList as $fieldName => $fieldType) + { + $text .= 'Creating extended user field user_'.$fieldName.' -> '; + $result = $ue->user_extended_add_system($fieldName, $fieldType); + if($result) + { + $text .= 'Success
'; + } + else + { + $text .= 'Failed
'; + $failed = true; + } + } + if($failed) + { + $text .= ' +

+ Creation of extended field(s) failed. You can not continue until these are create successfully! + '; + } + else + { + $text .= " +

+
+ +
+ "; + } + $e107->ns->tablerender($stepCaption, $text); } -//print_a($f->error); class forumUpgrade { @@ -156,7 +241,6 @@ class forumUpgrade $this->getUpdateInfo(); } - function checkAttachmentDirs() { $dirs = array(