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
1=== modified file 'qp_inc/_core/model/db/_upgrade.funcs.php'
2--- qp_inc/_core/model/db/_upgrade.funcs.php 2009-12-21 17:27:20 +0000
3+++ qp_inc/_core/model/db/_upgrade.funcs.php 2010-07-27 14:17:42 +0000
4@@ -142,7 +142,7 @@
5
6 if( preg_match( '|^(\s*CREATE TABLE\s+)(IF NOT EXISTS\s+)?([^\s(]+)(.*)$|is', $qry, $match) )
7 {
8- $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[3] ));
9+ $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[3] ));
10 $qry = $match[1].( empty($match[2]) ? '' : $match[2] ).$tablename.$match[4];
11
12 $items[strtolower($tablename)][] = array(
13@@ -159,7 +159,7 @@
14 }
15 elseif( preg_match( '|^(\s*INSERT INTO\s+)([\S]+)(.*)$|is', $qry, $match) )
16 {
17- $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
18+ $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
19 $items[strtolower($tablename)][] = array(
20 'queries' => array($match[1].$tablename.$match[3]),
21 'note' => '',
22@@ -167,7 +167,7 @@
23 }
24 elseif( preg_match( '|^(\s*UPDATE\s+)([\S]+)(.*)$|is', $qry, $match) )
25 {
26- $tablename = db_delta_remove_backticks(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
27+ $tablename = db_delta_remove_quotes(preg_replace( $DB->dbaliases, $DB->dbreplaces, $match[2] ));
28 $items[strtolower($tablename)][] = array(
29 'queries' => array($match[1].$tablename.$match[3]),
30 'note' => '',
31@@ -308,7 +308,7 @@
32 { // For every field line specified in the query
33 // Extract the field name
34 preg_match( '|^([^\s(]+)|', trim($create_definition), $match );
35- $fieldname = db_delta_remove_backticks($match[1]);
36+ $fieldname = db_delta_remove_quotes($match[1]);
37 $fieldname_lowered = strtolower($fieldname);
38
39 $create_definition = trim($create_definition, ", \r\n\t");
40@@ -319,17 +319,18 @@
41 'create_definition' => $create_definition,
42 );
43
44- if( ! preg_match( '~^(PRIMARY(?:\s+KEY)|UNIQUE(?:\s+(?:INDEX|KEY))?|KEY|INDEX) (?:\s+(\w+))? (\s+USING \w+)? \s* \((.*)\)$~ix', $create_definition, $match ) )
45+ if( ! preg_match( '~^(PRIMARY(?:\s+KEY)|(?:FULLTEXT|UNIQUE)(?:\s+(?:INDEX|KEY))?|KEY|INDEX) (?:\s+() (\w+) )? (\s+USING\s+\w+)? \s* \((.*)\)$~ix', $create_definition, $match )
46+ && ! 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 ) )
47 { // invalid type, should not happen
48 debug_die( 'Invalid type in $indices: '.$create_definition );
49 }
50 $add_index['keyword'] = $match[1];
51- $add_index['name'] = strtoupper($match[2]);
52- $add_index['type'] = $match[3]; // "USING [type_name]"
53- $add_index['col_names'] = explode( ',', $match[4] );
54+ $add_index['name'] = strtoupper($match[3]);
55+ $add_index['type'] = $match[4]; // "USING [type_name]"
56+ $add_index['col_names'] = explode( ',', $match[5] );
57 foreach( $add_index['col_names'] as $k => $v )
58 {
59- $add_index['col_names'][$k] = strtolower(db_delta_remove_backticks(trim($v)));
60+ $add_index['col_names'][$k] = strtolower(db_delta_remove_quotes(trim($v)));
61 }
62
63 if( $fieldname_lowered == 'primary' )
64@@ -438,7 +439,7 @@
65 {
66 $index_pattern .= 'PRIMARY(\s+KEY)?';
67 // optional primary key name:
68- $index_pattern .= '(\s+\w+)?';
69+ $index_pattern .= '(\s+[`"]?\w+[`"]?)?';
70 }
71 elseif( $index_data['unique'] )
72 {
73@@ -446,11 +447,11 @@
74 }
75 else
76 {
77- $index_pattern .= '(INDEX|KEY)';
78+ $index_pattern .= '(INDEX|(?:FULLTEXT\s+)?KEY)';
79 }
80 if( $index_name != 'PRIMARY' )
81 {
82- $index_pattern .= '(\s+`?'.$index_name.'`?)?'; // optionally in backticks (and index name is optionally itself)
83+ $index_pattern .= '(\s+[`"]?'.$index_name.'[`"]?)?'; // optionally in backticks (and index name is optionally itself)
84 }
85
86 $index_columns = '';
87@@ -462,7 +463,7 @@
88 $index_columns .= '\s*,\s*';
89 }
90 // Add the field to the column list string
91- $index_columns .= '`?'.$column_data['fieldname'].'`?'; // optionally in backticks
92+ $index_columns .= '[`"]?'.$column_data['fieldname'].'[`"]?'; // optionally in backticks
93 if( ! empty($column_data['subpart']) )
94 {
95 $index_columns .= '\s*\(\s*'.$column_data['subpart'].'\s*\)\s*';
96@@ -486,7 +487,7 @@
97 if( ! preg_match( '~^\w+\s+[^(]~', $index['create_definition'], $match ) )
98 { // no key name given, make the name part optional, if it's the default one:
99 // (Default key name seems to be the first column, eventually with "_\d+"-suffix)
100- $auto_key = db_delta_remove_backticks(strtoupper($index['col_names'][0]));
101+ $auto_key = db_delta_remove_quotes(strtoupper($index['col_names'][0]));
102 if( isset($used_auto_keys[$auto_key]) )
103 {
104 $used_auto_keys[$auto_key]++;
105@@ -606,7 +607,7 @@
106 unset($type_matches); // have we detected the type as matching (for optional length param)
107 $fieldtype = '';
108
109- $pattern_field = '`?'.$tablefield->Field.'`?'; // optionally in backticks
110+ $pattern_field = '[`"]?'.$tablefield->Field.'[`"]?'; // optionally in backticks
111
112 // Get the field type from the query
113 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 ) )
114@@ -617,9 +618,18 @@
115 { // synonym
116 $fieldtype = 'INT';
117 }
118- elseif( $fieldtype == 'DECIMAL' )
119+ elseif( $fieldtype == 'DEC' )
120 { // synonym
121- $fieldtype = 'DEC';
122+ $fieldtype = 'DECIMAL';
123+ }
124+ // Build pattern to match type: Length is optional (for int types) or precision/scale (must match exactly):
125+ if( substr($fieldtype, -3) == 'INT' )
126+ { // length is optional/not relevant
127+ $matches_pattern = '~^'.preg_replace( '~\((\d+)\)~', '(\(\d+\))?', $tablefield->Type ).'$~i';
128+ }
129+ else
130+ {
131+ $matches_pattern = '~^'.preg_quote($tablefield->Type, '~').'$~i';
132 }
133
134 if( isset($match[2]) )
135@@ -637,8 +647,6 @@
136
137 $field_to_parse = $match[5];
138
139- // The length param is optional:
140- $matches_pattern = '~^'.preg_replace( '~\((\d+)\)~', '(\(\d+\))?', $tablefield->Type ).'$~i';
141 $type_matches = preg_match( $matches_pattern, $fieldtype );
142 }
143 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 ) )
144@@ -838,6 +846,7 @@
145 }
146 }
147
148+ // TODO: "COLLATE" and other attribute handling should happen here, based on $field_to_parse
149
150 if( ! isset($type_matches) )
151 { // not tried to match before
152@@ -859,7 +868,7 @@
153 }
154 else
155 { // implicit default, see http://dev.mysql.com/doc/refman/4.1/en/data-type-defaults.html
156- if( preg_match( '~^(TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC)$~', $fieldtype ) )
157+ if( preg_match( '~^(TINYINT|SMALLINT|MEDIUMINT|INTEGER|INT|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|DEC|NUMERIC)~', $fieldtype ) )
158 { // numeric
159 $update_default = '0';
160 $update_default_set = '0';
161@@ -1052,7 +1061,8 @@
162 array_shift( $items[$table_lowered] );
163
164 // Add the remaining indices (which are not "inline" with a column definition and therefor already handled):
165- foreach( $indices as $index )
166+ $add_index_queries = array();
167+ foreach( $indices as $k => $index )
168 {
169 if( empty($index['create_definition']) )
170 { // skip "inline"
171@@ -1064,14 +1074,35 @@
172 $query .= ' DROP PRIMARY KEY,';
173 unset( $obsolete_indices['PRIMARY'] );
174 }
175- // Push a query line into $items that adds the index to that table
176- $items[$table_lowered][] = array(
177+
178+ // Create a query that adds the index to the table
179+ $query = array(
180 'queries' => array($query.' ADD '.$index['create_definition']),
181 'note' => 'Added index <strong>'.$index['create_definition'].'</strong>',
182 'type' => 'add_index' );
183+
184+ // Check if the index creation has to get appended after any DROPs (required for indices with the same name)
185+ $append_after_drops = false;
186+ foreach( $obsolete_indices as $obsolete_index )
187+ {
188+ if( strtolower($obsolete_index['name']) == strtolower($index['name']) )
189+ {
190+ $append_after_drops = true;
191+ break;
192+ }
193+ }
194+ if( $append_after_drops )
195+ { // do this after any DROPs (i.e. KEY name changes)
196+ $add_index_queries[] = $query;
197+ }
198+ else
199+ { // this needs to get done before any other DROPs
200+ // to prevent e.g. "Incorrect table definition; there can be only one auto column and it must be defined as a key(Errno=1075)"
201+ $items[$table_lowered][] = $query;
202+ }
203 }
204
205-
206+ // Now add queries to drop any (maybe changed!) indices
207 foreach( $obsolete_indices as $index_info )
208 {
209 // Push a query line into $items that drops the index from the table
210@@ -1080,6 +1111,9 @@
211 'note' => 'Dropped index <strong>'.$index_info['name'].'</strong>',
212 'type' => 'drop_index' );
213 }
214+
215+ // Add queries to (re)create (maybe changed indices) to the end
216+ $items[$table_lowered] = array_merge($items[$table_lowered], $add_index_queries);
217 }
218
219
220@@ -1134,18 +1168,24 @@
221
222
223 /**
224- * Remove backticks around a field/table name.
225- *
226- * "backtick" means "single backquote" (`)
227+ * Remove quotes/backticks around a field/table name.
228 *
229 * @param string Field name
230+ * @param string List of quote chars to remove
231 * @return string
232 */
233-function db_delta_remove_backticks($fieldname)
234+function db_delta_remove_quotes($fieldname, $quotes = '`"')
235 {
236- if( substr($fieldname, 0, 1) == '`' && substr($fieldname, -1) == '`' )
237- { // backticks:
238- $fieldname = substr($fieldname, 1, -1);
239+ $quotes_len = strlen( $quotes );
240+
241+ for( $i = 0; $i < $quotes_len; $i++ )
242+ {
243+ $char = $quotes[$i];
244+ if( substr($fieldname, 0, 1) == $char && substr($fieldname, -1) == $char )
245+ { // found quotes:
246+ $fieldname = substr($fieldname, 1, -1);
247+ return $fieldname;
248+ }
249 }
250 return $fieldname;
251 }
252@@ -1154,7 +1194,7 @@
253 /**
254 * Alter the DB schema to match the current expected one ({@link $schema_queries}).
255 *
256- * @todo if used by install only, then put it into the install folde!!!
257+ * @todo if used by install only, then put it into the install folder!!!
258 *
259 * @param boolean Display what we've done?
260 */
261@@ -1162,9 +1202,11 @@
262 {
263 global $schema_queries, $DB, $debug;
264
265+ // Go through all tables:
266 foreach( $schema_queries as $table => $query_info )
267 {
268- $items_need_update = db_delta( $query_info[1], array('drop_column', 'drop_index') );
269+ // Look for differences between terrain & map:
270+ $items_need_update = db_delta( $query_info[1], array('drop_column', 'drop_index'), false );
271
272 if( empty($items_need_update) )
273 {
274@@ -1185,7 +1227,7 @@
275 }
276 }
277 else
278- { // the same, but with output
279+ { // execute & output
280 foreach( $items_need_update as $table => $itemlist )
281 {
282 if( count($itemlist) == 1 && $itemlist[0]['type'] == 'create_table' )

Subscribers

People subscribed via source and target branches