Merge lp:~quam-plures-core/quam-plures/qp-bug_526380 into lp:quam-plures
- qp-bug_526380
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tilman Blumenbach (community) | Approve | ||
EdB | Approve | ||
Review via email: mp+31049@code.launchpad.net |
Commit message
Description of the change
Implemented blueyeds fix for this bug
¥
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 ;)
¥
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 :)
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 ;)
¥
Tilman Blumenbach (tblue) wrote : | # |
I will try and merge blueyed's tests for that bug. That should make testing easier.
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?
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.
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
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.
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!
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.
Preview Diff
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' ) |
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.