Merge lp:~quam-plures-core/quam-plures/qp-bug_526380 into lp:quam-plures

Proposed by Yabs
Status: Merged
Merged at revision: 7517
Proposed branch: lp:~quam-plures-core/quam-plures/qp-bug_526380
Merge into: lp:quam-plures
Diff against target: 282 lines (+76/-34)
1 file modified
qp_inc/_core/model/db/_upgrade.funcs.php (+76/-34)
To merge this branch: bzr merge lp:~quam-plures-core/quam-plures/qp-bug_526380
Reviewer Review Type Date Requested Status
Tilman Blumenbach (community) Approve
EdB Approve
Review via email: mp+31049@code.launchpad.net

Description of the change

Implemented blueyeds fix for this bug

¥

To post a comment you must log in.
Revision history for this message
EdB (edb) wrote :

Downloaded and will test, but does anyone have a plugin that triggers the bug ... or will the snippet in the bug report work as a pretend plugin? I recall the nature of this bug but I haven't migrated any plugins that make a database table.

Revision history for this message
Yabs (yabs) wrote :

IIRC the snippet in a pretend plugin should trigger it (if fix fails) ... been a smidge since I played with the plugin that "discovered" that bug though ... not sure if the plugin even exists since I tidied server :-S

Note : 100% untested, I just merged diffs, must go through a test install, but I don't expect problems from blueyed code ;)

¥

Revision history for this message
EdB (edb) wrote :

Yeah no problem with blueyed code but the idea and all that ya know? Anyway tested by modificating the 'test' plugin and identifying the problem in core-of-the-moment then seeing that the problem does not exist in this branch. To verificate the problem one simply has to install a plugin that might trigger the but, then disable the plugin, then enable it again.

So should I add the altered test plugin to this branch? I also removed the bit that stops the user settings from showing up, and fixed a comment in the code about translation stuff.

Will merge after knowing if test should be here or have it's own branch :)

review: Approve
Revision history for this message
Yabs (yabs) wrote :

Yeah, even blueyed's code needs review ... especially since it was diff'd in by a blonde :D

I'd need to see your test plugin code before I could answer that ;)

¥

Revision history for this message
Tilman Blumenbach (tblue) wrote :

I will try and merge blueyed's tests for that bug. That should make testing easier.

Revision history for this message
EdB (edb) wrote :

The test plugin will get it's own branch. Much cleaner that way :)

Tblue are you saying I should hold off on doing this merge?

Revision history for this message
Tilman Blumenbach (tblue) wrote :

> Tblue are you saying I should hold off on doing this merge?

Yes, please. But feel free to create a branch for your plugin.

Revision history for this message
Bush League Critic (bushleaguecritic) wrote :

Pulled this test branch and jury-rigged a plugin that creates a table
(GetDbLayout) based on the code snippet yabs provided in the original
bug report. I had no issues/errors installing the plugin and the table
was created exactly as requested.

Should that have been enough to trigger the original bug?

BLC

Revision history for this message
EdB (edb) wrote :

Hi BLC. The bug is triggered when you disable then enable a plugin that created a database that includes specific field types. If your DB has a float and/or a decimal field, then disable and enable will ... not show the bug in this branch. Apply the same plugin to the current core and you'll see the bug in all it's glory.

There is also now a test plugin branch that adds a table that triggers the bug if you want it.

Revision history for this message
Tilman Blumenbach (tblue) wrote :

OTOH, looks like the relevant part is exactly the same as blueyed's code, so perhaps adding tests is not necessary right now, but we should really start using tests at some later point!

Revision history for this message
Tilman Blumenbach (tblue) wrote :

Did the same as dmassay. Could reproduce behaviour described in bug report before merging in this branch, could not reproduce it afterwards.

So, looks fine.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'qp_inc/_core/model/db/_upgrade.funcs.php'
--- qp_inc/_core/model/db/_upgrade.funcs.php 2009-12-21 17:27:20 +0000
+++ qp_inc/_core/model/db/_upgrade.funcs.php 2010-07-27 14:17:42 +0000
@@ -142,7 +142,7 @@
142142
143 if( preg_match( '|^(\s*CREATE TABLE\s+)(IF NOT EXISTS\s+)?([^\s(]+)(.*)$|is', $qry, $match) )143 if( preg_match( '|^(\s*CREATE TABLE\s+)(IF NOT EXISTS\s+)?([^\s(]+)(.*)$|is', $qry, $match) )
144 {144 {
145 $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[3] ));145 $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[3] ));
146 $qry = $match[1].( empty($match[2]) ? '' : $match[2] ).$tablename.$match[4];146 $qry = $match[1].( empty($match[2]) ? '' : $match[2] ).$tablename.$match[4];
147147
148 $items[strtolower($tablename)][] = array(148 $items[strtolower($tablename)][] = array(
@@ -159,7 +159,7 @@
159 }159 }
160 elseif( preg_match( '|^(\s*INSERT INTO\s+)([\S]+)(.*)$|is', $qry, $match) )160 elseif( preg_match( '|^(\s*INSERT INTO\s+)([\S]+)(.*)$|is', $qry, $match) )
161 {161 {
162 $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));162 $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
163 $items[strtolower($tablename)][] = array(163 $items[strtolower($tablename)][] = array(
164 'queries' => array($match[1].$tablename.$match[3]),164 'queries' => array($match[1].$tablename.$match[3]),
165 'note' => '',165 'note' => '',
@@ -167,7 +167,7 @@
167 }167 }
168 elseif( preg_match( '|^(\s*UPDATE\s+)([\S]+)(.*)$|is', $qry, $match) )168 elseif( preg_match( '|^(\s*UPDATE\s+)([\S]+)(.*)$|is', $qry, $match) )
169 {169 {
170 $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));170 $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
171 $items[strtolower($tablename)][] = array(171 $items[strtolower($tablename)][] = array(
172 'queries' => array($match[1].$tablename.$match[3]),172 'queries' => array($match[1].$tablename.$match[3]),
173 'note' => '',173 'note' => '',
@@ -308,7 +308,7 @@
308 { // For every field line specified in the query308 { // For every field line specified in the query
309 // Extract the field name309 // Extract the field name
310 preg_match( '|^([^\s(]+)|', trim($create_definition), $match );310 preg_match( '|^([^\s(]+)|', trim($create_definition), $match );
311 $fieldname = db_delta_remove_backticks($match[1]);311 $fieldname = db_delta_remove_quotes($match[1]);
312 $fieldname_lowered = strtolower($fieldname);312 $fieldname_lowered = strtolower($fieldname);
313313
314 $create_definition = trim($create_definition, ", \r\n\t");314 $create_definition = trim($create_definition, ", \r\n\t");
@@ -319,17 +319,18 @@
319 'create_definition' => $create_definition,319 'create_definition' => $create_definition,
320 );320 );
321321
322 if( ! preg_match( '~^(PRIMARY(?:\s+KEY)|UNIQUE(?:\s+(?:INDEX|KEY))?|KEY|INDEX) (?:\s+(\w+))? (\s+USING \w+)? \s* \((.*)\)$~ix', $create_definition, $match ) )322 if( ! preg_match( '~^(PRIMARY(?:\s+KEY)|(?:FULLTEXT|UNIQUE)(?:\s+(?:INDEX|KEY))?|KEY|INDEX) (?:\s+() (\w+) )? (\s+USING\s+\w+)? \s* \((.*)\)$~ix', $create_definition, $match )
323 && ! preg_match( '~^(PRIMARY(?:\s+KEY)|(?:FULLTEXT|UNIQUE)(?:\s+(?:INDEX|KEY))?|KEY|INDEX) (?:\s+([`"])([\w\s]+)\\2)? (\s+USING\s+\w+)? \s* \((.*)\)$~ix', $create_definition, $match ) )
323 { // invalid type, should not happen324 { // invalid type, should not happen
324 debug_die( 'Invalid type in $indices: '.$create_definition );325 debug_die( 'Invalid type in $indices: '.$create_definition );
325 }326 }
326 $add_index['keyword'] = $match[1];327 $add_index['keyword'] = $match[1];
327 $add_index['name'] = strtoupper($match[2]);328 $add_index['name'] = strtoupper($match[3]);
328 $add_index['type'] = $match[3]; // "USING [type_name]"329 $add_index['type'] = $match[4]; // "USING [type_name]"
329 $add_index['col_names'] = explode( ',', $match[4] );330 $add_index['col_names'] = explode( ',', $match[5] );
330 foreach( $add_index['col_names'] as $k => $v )331 foreach( $add_index['col_names'] as $k => $v )
331 {332 {
332 $add_index['col_names'][$k] = strtolower(db_delta_remove_backticks(trim($v)));333 $add_index['col_names'][$k] = strtolower(db_delta_remove_quotes(trim($v)));
333 }334 }
334335
335 if( $fieldname_lowered == 'primary' )336 if( $fieldname_lowered == 'primary' )
@@ -438,7 +439,7 @@
438 {439 {
439 $index_pattern .= 'PRIMARY(\s+KEY)?';440 $index_pattern .= 'PRIMARY(\s+KEY)?';
440 // optional primary key name:441 // optional primary key name:
441 $index_pattern .= '(\s+\w+)?';442 $index_pattern .= '(\s+[`"]?\w+[`"]?)?';
442 }443 }
443 elseif( $index_data['unique'] )444 elseif( $index_data['unique'] )
444 {445 {
@@ -446,11 +447,11 @@
446 }447 }
447 else448 else
448 {449 {
449 $index_pattern .= '(INDEX|KEY)';450 $index_pattern .= '(INDEX|(?:FULLTEXT\s+)?KEY)';
450 }451 }
451 if( $index_name != 'PRIMARY' )452 if( $index_name != 'PRIMARY' )
452 {453 {
453 $index_pattern .= '(\s+`?'.$index_name.'`?)?'; // optionally in backticks (and index name is optionally itself)454 $index_pattern .= '(\s+[`"]?'.$index_name.'[`"]?)?'; // optionally in backticks (and index name is optionally itself)
454 }455 }
455456
456 $index_columns = '';457 $index_columns = '';
@@ -462,7 +463,7 @@
462 $index_columns .= '\s*,\s*';463 $index_columns .= '\s*,\s*';
463 }464 }
464 // Add the field to the column list string465 // Add the field to the column list string
465 $index_columns .= '`?'.$column_data['fieldname'].'`?'; // optionally in backticks466 $index_columns .= '[`"]?'.$column_data['fieldname'].'[`"]?'; // optionally in backticks
466 if( ! empty($column_data['subpart']) )467 if( ! empty($column_data['subpart']) )
467 {468 {
468 $index_columns .= '\s*\(\s*'.$column_data['subpart'].'\s*\)\s*';469 $index_columns .= '\s*\(\s*'.$column_data['subpart'].'\s*\)\s*';
@@ -486,7 +487,7 @@
486 if( ! preg_match( '~^\w+\s+[^(]~', $index['create_definition'], $match ) )487 if( ! preg_match( '~^\w+\s+[^(]~', $index['create_definition'], $match ) )
487 { // no key name given, make the name part optional, if it's the default one:488 { // no key name given, make the name part optional, if it's the default one:
488 // (Default key name seems to be the first column, eventually with "_\d+"-suffix)489 // (Default key name seems to be the first column, eventually with "_\d+"-suffix)
489 $auto_key = db_delta_remove_backticks(strtoupper($index['col_names'][0]));490 $auto_key = db_delta_remove_quotes(strtoupper($index['col_names'][0]));
490 if( isset($used_auto_keys[$auto_key]) )491 if( isset($used_auto_keys[$auto_key]) )
491 {492 {
492 $used_auto_keys[$auto_key]++;493 $used_auto_keys[$auto_key]++;
@@ -606,7 +607,7 @@
606 unset($type_matches); // have we detected the type as matching (for optional length param)607 unset($type_matches); // have we detected the type as matching (for optional length param)
607 $fieldtype = '';608 $fieldtype = '';
608609
609 $pattern_field = '`?'.$tablefield->Field.'`?'; // optionally in backticks610 $pattern_field = '[`"]?'.$tablefield->Field.'[`"]?'; // optionally in backticks
610611
611 // Get the field type from the query612 // Get the field type from the query
612 if( preg_match( '~^'.$pattern_field.'\s+ (TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC) ( \s* \([\d\s,]+\) )? (\s+ UNSIGNED)? (\s+ ZEROFILL)? (.*)$~ix', $column_definition, $match ) )613 if( preg_match( '~^'.$pattern_field.'\s+ (TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC) ( \s* \([\d\s,]+\) )? (\s+ UNSIGNED)? (\s+ ZEROFILL)? (.*)$~ix', $column_definition, $match ) )
@@ -617,9 +618,18 @@
617 { // synonym618 { // synonym
618 $fieldtype = 'INT';619 $fieldtype = 'INT';
619 }620 }
620 elseif( $fieldtype == 'DECIMAL' )621 elseif( $fieldtype == 'DEC' )
621 { // synonym622 { // synonym
622 $fieldtype = 'DEC';623 $fieldtype = 'DECIMAL';
624 }
625 // Build pattern to match type: Length is optional (for int types) or precision/scale (must match exactly):
626 if( substr($fieldtype, -3) == 'INT' )
627 { // length is optional/not relevant
628 $matches_pattern = '~^'.preg_replace( '~\((\d+)\)~', '(\(\d+\))?', $tablefield->Type ).'$~i';
629 }
630 else
631 {
632 $matches_pattern = '~^'.preg_quote($tablefield->Type, '~').'$~i';
623 }633 }
624634
625 if( isset($match[2]) )635 if( isset($match[2]) )
@@ -637,8 +647,6 @@
637647
638 $field_to_parse = $match[5];648 $field_to_parse = $match[5];
639649
640 // The length param is optional:
641 $matches_pattern = '~^'.preg_replace( '~\((\d+)\)~', '(\(\d+\))?', $tablefield->Type ).'$~i';
642 $type_matches = preg_match( $matches_pattern, $fieldtype );650 $type_matches = preg_match( $matches_pattern, $fieldtype );
643 }651 }
644 elseif( preg_match( '~^'.$pattern_field.'\s+(DATETIME|DATE|TIMESTAMP|TIME|YEAR|TINYBLOB|BLOB|MEDIUMBLOB|LONGBLOB|TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT) ( \s+ BINARY )? (.*)$~ix', $column_definition, $match ) )652 elseif( preg_match( '~^'.$pattern_field.'\s+(DATETIME|DATE|TIMESTAMP|TIME|YEAR|TINYBLOB|BLOB|MEDIUMBLOB|LONGBLOB|TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT) ( \s+ BINARY )? (.*)$~ix', $column_definition, $match ) )
@@ -838,6 +846,7 @@
838 }846 }
839 }847 }
840848
849 // TODO: "COLLATE" and other attribute handling should happen here, based on $field_to_parse
841850
842 if( ! isset($type_matches) )851 if( ! isset($type_matches) )
843 { // not tried to match before852 { // not tried to match before
@@ -859,7 +868,7 @@
859 }868 }
860 else869 else
861 { // implicit default, see http://dev.mysql.com/doc/refman/4.1/en/data-type-defaults.html870 { // implicit default, see http://dev.mysql.com/doc/refman/4.1/en/data-type-defaults.html
862 if( preg_match( '~^(TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC)$~', $fieldtype ) )871 if( preg_match( '~^(TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC)~', $fieldtype ) )
863 { // numeric872 { // numeric
864 $update_default = '0';873 $update_default = '0';
865 $update_default_set = '0';874 $update_default_set = '0';
@@ -1052,7 +1061,8 @@
1052 array_shift( $items[$table_lowered] );1061 array_shift( $items[$table_lowered] );
10531062
1054 // Add the remaining indices (which are not "inline" with a column definition and therefor already handled):1063 // Add the remaining indices (which are not "inline" with a column definition and therefor already handled):
1055 foreach( $indices as $index )1064 $add_index_queries = array();
1065 foreach( $indices as $k => $index )
1056 {1066 {
1057 if( empty($index['create_definition']) )1067 if( empty($index['create_definition']) )
1058 { // skip "inline"1068 { // skip "inline"
@@ -1064,14 +1074,35 @@
1064 $query .= ' DROP PRIMARY KEY,';1074 $query .= ' DROP PRIMARY KEY,';
1065 unset( $obsolete_indices['PRIMARY'] );1075 unset( $obsolete_indices['PRIMARY'] );
1066 }1076 }
1067 // Push a query line into $items that adds the index to that table1077
1068 $items[$table_lowered][] = array(1078 // Create a query that adds the index to the table
1079 $query = array(
1069 'queries' => array($query.' ADD '.$index['create_definition']),1080 'queries' => array($query.' ADD '.$index['create_definition']),
1070 'note' => 'Added index <strong>'.$index['create_definition'].'</strong>',1081 'note' => 'Added index <strong>'.$index['create_definition'].'</strong>',
1071 'type' => 'add_index' );1082 'type' => 'add_index' );
1083
1084 // Check if the index creation has to get appended after any DROPs (required for indices with the same name)
1085 $append_after_drops = false;
1086 foreach( $obsolete_indices as $obsolete_index )
1087 {
1088 if( strtolower($obsolete_index['name']) == strtolower($index['name']) )
1089 {
1090 $append_after_drops = true;
1091 break;
1092 }
1093 }
1094 if( $append_after_drops )
1095 { // do this after any DROPs (i.e. KEY name changes)
1096 $add_index_queries[] = $query;
1097 }
1098 else
1099 { // this needs to get done before any other DROPs
1100 // to prevent e.g. "Incorrect table definition; there can be only one auto column and it must be defined as a key(Errno=1075)"
1101 $items[$table_lowered][] = $query;
1102 }
1072 }1103 }
10731104
10741105 // Now add queries to drop any (maybe changed!) indices
1075 foreach( $obsolete_indices as $index_info )1106 foreach( $obsolete_indices as $index_info )
1076 {1107 {
1077 // Push a query line into $items that drops the index from the table1108 // Push a query line into $items that drops the index from the table
@@ -1080,6 +1111,9 @@
1080 'note' => 'Dropped index <strong>'.$index_info['name'].'</strong>',1111 'note' => 'Dropped index <strong>'.$index_info['name'].'</strong>',
1081 'type' => 'drop_index' );1112 'type' => 'drop_index' );
1082 }1113 }
1114
1115 // Add queries to (re)create (maybe changed indices) to the end
1116 $items[$table_lowered] = array_merge($items[$table_lowered], $add_index_queries);
1083 }1117 }
10841118
10851119
@@ -1134,18 +1168,24 @@
11341168
11351169
1136/**1170/**
1137 * Remove backticks around a field/table name.1171 * Remove quotes/backticks around a field/table name.
1138 *
1139 * "backtick" means "single backquote" (`)
1140 *1172 *
1141 * @param string Field name1173 * @param string Field name
1174 * @param string List of quote chars to remove
1142 * @return string1175 * @return string
1143 */1176 */
1144function db_delta_remove_backticks($fieldname)1177function db_delta_remove_quotes($fieldname, $quotes = '`"')
1145{1178{
1146 if( substr($fieldname, 0, 1) == '`' && substr($fieldname, -1) == '`' )1179 $quotes_len = strlen( $quotes );
1147 { // backticks:1180
1148 $fieldname = substr($fieldname, 1, -1);1181 for( $i = 0; $i < $quotes_len; $i++ )
1182 {
1183 $char = $quotes[$i];
1184 if( substr($fieldname, 0, 1) == $char && substr($fieldname, -1) == $char )
1185 { // found quotes:
1186 $fieldname = substr($fieldname, 1, -1);
1187 return $fieldname;
1188 }
1149 }1189 }
1150 return $fieldname;1190 return $fieldname;
1151}1191}
@@ -1154,7 +1194,7 @@
1154/**1194/**
1155 * Alter the DB schema to match the current expected one ({@link $schema_queries}).1195 * Alter the DB schema to match the current expected one ({@link $schema_queries}).
1156 *1196 *
1157 * @todo if used by install only, then put it into the install folde!!!1197 * @todo if used by install only, then put it into the install folder!!!
1158 *1198 *
1159 * @param boolean Display what we've done?1199 * @param boolean Display what we've done?
1160 */1200 */
@@ -1162,9 +1202,11 @@
1162{1202{
1163 global $schema_queries, $DB, $debug;1203 global $schema_queries, $DB, $debug;
11641204
1205 // Go through all tables:
1165 foreach( $schema_queries as $table => $query_info )1206 foreach( $schema_queries as $table => $query_info )
1166 {1207 {
1167 $items_need_update = db_delta( $query_info[1], array('drop_column', 'drop_index') );1208 // Look for differences between terrain & map:
1209 $items_need_update = db_delta( $query_info[1], array('drop_column', 'drop_index'), false );
11681210
1169 if( empty($items_need_update) )1211 if( empty($items_need_update) )
1170 {1212 {
@@ -1185,7 +1227,7 @@
1185 }1227 }
1186 }1228 }
1187 else1229 else
1188 { // the same, but with output1230 { // execute & output
1189 foreach( $items_need_update as $table => $itemlist )1231 foreach( $items_need_update as $table => $itemlist )
1190 {1232 {
1191 if( count($itemlist) == 1 && $itemlist[0]['type'] == 'create_table' )1233 if( count($itemlist) == 1 && $itemlist[0]['type'] == 'create_table' )

Subscribers

People subscribed via source and target branches