blob: 16f1811687ff03710a8ceda63b758cf5dd3cd45e [file] [log] [blame]
gobriendd79db82008-04-26 00:09:51 +00001<?php
2/*******************************************************************************
3 * Copyright (c) 2006-2007 Eclipse Foundation
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Bjorn Freeman-Benson - initial API and implementation
11 * Ward Cunningham - initial API and implementation
12 * Karl Mathias - initial API and implementation
13 *******************************************************************************/
14
15
16function ourTrim(&$item1, $key){$item1 = trim($item1);}
17
18abstract class AbstractSchemaChecker {
19 abstract public function check_and_modify( $context );
20
21 public function check_and_modify_table( $databasename, $tablename, $schemas, $context ) {
22 $dbh = $context->database( $databasename );
23 $tablenamesuffix = $context->initmode;
24
25 echo "working on table '$tablename$tablenamesuffix' \n";
26
27 $createfunction = "create_$tablename";
28 if( $context->testmode && $context->initmode === false ) {
29 echo "..creating in memory <br>\n";
30 $sql = "DROP TABLE IF EXISTS $tablename ";
31 mysql_remember_query( $sql, $dbh );
32 mysql_error_check();
33 $sql = $this->$createfunction('');
34 $translations = array(
35 '/param(\d+) TEXT/' => 'param\1 varchar(1024)',
36 '/\s[Tt][Ee][Xx][Tt]/' => ' varchar(1024)',
37 '/blob/' => 'varbinary(8192)',
38 '/ENGINE=([A-Za-z]+)/' => '',
39 '/mediumtext/' => 'varchar(256)',
40 '/tinytext/' => 'varchar(256)',
41 '/FULLTEXT/' => '',
42 );
43 $a1 = array();
44 $a2 = array();
45 foreach( $translations as $k => $v ) {
46 $a1[] = $k;
47 $a2[] = $v;
48 }
49 $sql = preg_replace( $a1, $a2, $sql );
50 $sql = $sql . " ENGINE=MEMORY SELECT * FROM $tablename" . '_prototype';
51 $this->create_db( $sql, $dbh );
52 return true;
53 } else {
54 $table_name = $tablename.$tablenamesuffix;
55 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
56 if( strlen(mysql_error()) > 0 ) {
57 if( $context->devmode
58 || $context->testmode
59 || $databasename == 'myfoundation' ) {
60 echo "..does not exist, creating <br>\n";
61 $this->create_db( $this->$createfunction($tablenamesuffix), $dbh );
62 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
63 $str = $this->table_has_schema($result, $tablename . $tablenamesuffix, $schemas[count($schemas)], $dbh);
64 if( $str === false )
65 return true;
66 else {
67 echo $str;
68 echo "..schema does not match after creation of table $tablename$tablenamesuffix, error <br>\n";
69 return false;
70 }
71 } else {
72// print mysql_error();
73 echo "..does not exist, error <br>\n";
74 $this->createTableFromSchema($table_name,$schemas[count($schemas)],$dbh,$context);
75 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
76 $str = $this->table_has_schema($result, $tablename . $tablenamesuffix, $schemas[count($schemas)], $dbh);
77 if( $str === false )
78 return true;
79 else {
80 echo $str;
81 echo "..schema does not match after creation, error <br>\n";
82 return false;
83 }
84 }
85 }
86 $lastmatch = 0;
87 $laststr = '';
88 for( $i = 1; $i <= count($schemas); $i++ ) {
89 if( $i > 1 )
90 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
91 $str = $this->table_has_schema($result, $tablename . $tablenamesuffix, $schemas[$i], $dbh);
92 if( $str === false )
93 $lastmatch = $i;
94 else
95 $laststr = $str;
96 }
97 if( $lastmatch == 0 ) {
98 if( $context->devmode
99 || $context->testmode ) {
100 echo "..no matching schema, deleting and recreating <br>\n";
101 mysql_remember_query( "DROP TABLE $tablename$tablenamesuffix", $dbh);
102 mysql_error_check();
103 $this->create_db( $this->$createfunction($tablenamesuffix), $dbh );
104 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
105 $str = $this->table_has_schema($result, $tablename . $tablenamesuffix, $schemas[count($schemas)], $dbh);
106 if( $str === false )
107 return true;
108 else {
109 echo $str;
110 echo "..schema does not match after creation, error <br>\n";
111 return false;
112 }
113 } else {
114 echo $laststr;
115 echo "..no matching schema, error <br>\n";
116 return false;
117 }
118 }
119
120 if( $lastmatch == count($schemas) ) {
121 echo "..correct <br>\n";
122 return true;
123 }
124 if( $context->devmode
125 || $context->testmode
126 || $databasename == 'myfoundation' ) {
127 echo "..old schema, updating <br>\n";
128 for( $i = $lastmatch; $i < count($schemas); $i++ ) {
129 $modifyfunction = 'modify_' . $tablename . '_' . $i . '_' . ($i+1);
130 $this->$modifyfunction( $tablenamesuffix, $dbh );
131 }
132 $result = mysql_remember_query( "DESCRIBE $tablename$tablenamesuffix", $dbh );
133 $str = $this->table_has_schema($result, $tablename . $tablenamesuffix, $schemas[count($schemas)], $dbh);
134 if( $str === false )
135 return true;
136 else {
137 echo $str;
138 echo "..schema does not match after modification, error <br>\n";
139 return false;
140 }
141 } else {
142 $this->updateTableFromSchema($table_name,$schemas[count($schemas)],$dbh,$context);
143
144 echo "..old schema, error <br>\n";
145 return false;
146
147 }
148 }
149 }
150
151 private function createTableFromSchema($table_name,$schema,$dbh,$context){
152 $fields = $this->splitSchema($schema);
153 $primary_keys = array();
154 foreach($fields as $field){
155 $query_guts .= $this->fieldToQuery($field,$primary_keys).",";
156 }
157 $query = "create table $table_name ( ".trim($query_guts,",\n");
158 if(!empty($primary_keys['PRI'])){
159 $query .= ", primary key(".implode($primary_keys['PRI'],",").")";
160 }
161 if(!empty($primary_keys['MUL'])){
162 foreach($primary_keys['MUL'] as $mul){
163 $query .= ", index($mul)";
164 }
165 }
166 $query .= ")";
167
168 mysql_query($query,$dbh);
169 }
170
171
172 //NEED TO HANDLE EDGES CASES
173 // - WHEN ALL FIELDS ARE DROPPED FROM TABLE.. TREAT LIKE DROP TABLE CREATE TABLE WITH NEW SCHEMA.
174
175 private function updateTableFromSchema($table_name,$schema,$dbh,$context){
176 $old_fields = $this->getTableDescription($table_name,$dbh);
177 $new_fields = $this->splitSchema($schema);
178 //run prerefactor quries
179
180print "\n";//gogo
181
182 //find all old fields that have changed
183 //find all fields to drop
184 foreach($old_fields as $k => $v){
185 if($diff = array_diff($new_fields[$k], $v)){
186 print "MODIFY - $k\n";
187 if($diff['Default'] or $diff['Field']){
188 $modq = "alter table $table_name modify $k ".$new_fields[$k]['Type']." ".$new_fields[$k]['Default']. " ".$diff['Extra'];
189 mysql_remember_query($modq,$dbh);
190 }
191 }else{
192 //drop new field
193 print "DROP - $k\n";
194 $query = "alter table $table_name drop $k ";
195 if($v['Key'] == "PRI"){
196 $query .= " DROP PRIMARY KEY ";
197 }
198 if($v['Key'] == "MUL"){
199 $query .= " DROP INDEX ";
200 }
201 mysql_query($query);
202 }
203 }
204
205 //find all to add
206 $endqueries[] = array();
207 foreach($new_fields as $k => $v){
208 if(!isset($old_fields[$k])){
209 print "add!!!! $k\n";
210
211 $keys = array();
212 $query = "alter table $table_name add ";
213 $query.= $this->fieldToquery($v,&$keys);
214
215 if(is_array($keys['PRI']) and in_array($k,$keys['PRI'])){
216 $query .= " PRIMARY KEY ";
217 }
218 if(is_array($keys['MUL']) and in_array($k,$keys['MUL'])){
219// $query .= " INDEX ";
220 $endqueries[] = "alter table $table_name add index ($k) ";
221 }
222
223 print $query."\n";
224 mysql_query($query);
225 foreach($endqueries as $q){
226 mysql_query($q);
227 }
228
229 }
230 }
231 //check schema
232
233 }
234
235 private function fieldToquery($fields,&$keys){
236//gogo
237// print_r($fields);
238 list($type,$size) = explode("(" , trim($fields['Type'],") ") );
239
240 foreach($fields as $k => $v){
241 if($k == 'Null'){
242 if($v == 'NO'){
243 $v = "not null";
244 }else{
245 $v = "null";
246 }
247 }
248 if($k == 'Default'){
249 if($v != "")
250 $v = "DEFAULT $v";
251 else
252 $v = "";
253
254 }
255 if($k == 'Key'){
256 switch($v){
257 case 'PRI':
258 $keys['PRI'][] = $fields['Field'];
259 break;
260 case 'MUL':
261 $keys['MUL'][] = $fields['Field'];
262 break;
263 default:
264 break;
265 }
266 $v = ""; //clear the value they keys will be appened to end of query
267 }
268
269 $line .= " $v ";
270 }
271 return $line;
272 }
273
274 private function getTableDescription($table_name,$dbh){
275 $query = "DESCRIBE $table_name";
276 print $query;
277 $result = mysql_query($query,$dbh);
278 $ret = array();
279 while( $row = mysql_fetch_assoc($result) ) {
280 $ret[$row['Field']] = $row;
281 }
282 return $ret;
283 }
284
285 private function splitSchema($schema){ //todo need to break out the same functionality from table_has_schema
286 $lines = explode("\n",trim($schema));
287
288 $breaker = array_shift($lines);
289 $fieldKeys = explode("|",trim(array_shift($lines),"|"));
290 array_walk($fieldKeys, 'ourTrim');
291
292 array_shift($lines); //shift off the next filler line +----+---
293
294 while($line = array_shift($lines)){
295 if($line == $breaker){
296 break;
297 }
298 $fieldValues = explode("|",trim(trim($line),"|"));
299 $aray = array_combine($fieldKeys,$fieldValues);
300
301 $aray['seen'] = false;
302 array_walk($aray, 'ourTrim');
303 $ret[$aray['Field']] = $aray;
304 }
305
306// print_r($ret);
307 return $ret;
308 }
309
310 private function create_db( $sql, $dbh ) {
311 mysql_remember_query( $sql, $dbh );
312 mysql_error_check();
313 }
314
315 /* returns error string if error, null if schema matches */
316 private function table_has_schema( $result, $tablename, $schemadescription, $dbh ) {
317 $schema = $this->splitSchema($schemadescription);
318
319 while( $row = mysql_fetch_assoc($result) ) {
320 if( !array_key_exists($row['Field'], $schema) ) {
321 $rtrn .= "..column " . $row['Field'] . " not in schema <br>\n";
322 continue;
323 }
324 $schema[$row['Field']]['seen'] = true;
325 $rec = $schema[$row['Field']];
326 if( $rec['Type'] != $row['Type'] ) {
327 $rtrn .= "..column " . $row['Field'] . " with different type (" . $rec['Type'] . " vs " . $row['Type'] . ") <br>\n";
328 continue;
329 }
330 $a = $rec['Null'];
331 $b = $row['Null'];
332 if( $a == '' ) $a = 'NO';
333 if( $b == '' ) $b = 'NO';
334 /* some versions of MySQL seem to ignore NOT NULL when applied to timestamps */
335 if( $row['Type'] == 'timestamp' ) $b = 'NO';
336 /* */
337 if( $a != $b ) {
338 $rtrn .= "..column " . $row['Field']. " with different NULL (" . $rec['Null'] . " vs " . $row['Null'] . ") <br>\n";
339 continue;
340 }
341 if( $rec['Key'] != $row['Key']) {
342 if( $rec['Key']) {
343 $rtrn .= "..column " . $row['Field']. " with different keys (" . $rec['Key'] . ") vs (" . $row['Key']. ") <br>\n";
344 continue;
345 // note that we allow the table to have more keys than the schema, but not fewer
346 }
347 }
348 if( $rec['Extra'] != $row['Extra']) {
349 $rtrn .= "..column " . $row['Field']. " with different extra infomration (" . $rec['Extra'] . " vs " . $row['Extra'] . ") <br>\n";
350 continue;
351 }
352 // not able to check 'default' at this time
353 }
354
355 foreach( $schema as $rec ) {
356 if( !$rec['seen']) {
357 $rtrn .= "..column " . $rec['Field'] . " not in table <br>\n";
358 continue;
359 }
360 }
361 if( $rtrn == '' ) return false;
362 return $rtrn;
363 }
364}
365
366
367?>