fix to prior commit
[openemr.git] / library / sql_upgrade_fx.php
blob065994fb6fe94a75cddd5f60531160db59f9aa0b
1 <?php
2 /**
3 * Upgrading and patching functions of database.
5 * Functions to allow safe database modifications
6 * during upgrading and patches.
8 * Copyright (C) 2008-2012 Rod Roark <rod@sunsetsystems.com>
10 * LICENSE: This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
21 * @package OpenEMR
22 * @author Rod Roark <rod@sunsetsystems.com>
23 * @author Brady Miller <brady@sparmy.com>
24 * @author Teny <teny@zhservices.com>
25 * @link http://www.open-emr.org
28 /**
29 * Check if a Sql table exists.
31 * @param string $tblname Sql Table Name
32 * @return boolean returns true if the sql table exists
34 function tableExists($tblname) {
35 $row = sqlQuery("SHOW TABLES LIKE '$tblname'");
36 if (empty($row)) return false;
37 return true;
40 /**
41 * Check if a Sql column exists in a selected table.
43 * @param string $tblname Sql Table Name
44 * @param string $colname Sql Column Name
45 * @return boolean returns true if the sql column exists
47 function columnExists($tblname, $colname) {
48 $row = sqlQuery("SHOW COLUMNS FROM $tblname LIKE '$colname'");
49 if (empty($row)) return false;
50 return true;
53 /**
54 * Check if a Sql column has a certain type.
56 * @param string $tblname Sql Table Name
57 * @param string $colname Sql Column Name
58 * @param string $coltype Sql Column Type
59 * @return boolean returns true if the sql column is of the specified type
61 function columnHasType($tblname, $colname, $coltype) {
62 $row = sqlQuery("SHOW COLUMNS FROM $tblname LIKE '$colname'");
63 if (empty($row)) return true;
64 return (strcasecmp($row['Type'], $coltype) == 0);
67 /**
68 * Check if a Sql row exists. (with one value)
70 * @param string $tblname Sql Table Name
71 * @param string $colname Sql Column Name
72 * @param string $value Sql value
73 * @return boolean returns true if the sql row does exist
75 function tableHasRow($tblname, $colname, $value) {
76 $row = sqlQuery("SELECT COUNT(*) AS count FROM $tblname WHERE " .
77 "$colname LIKE '$value'");
78 return $row['count'] ? true : false;
81 /**
82 * Check if a Sql row exists. (with two values)
84 * @param string $tblname Sql Table Name
85 * @param string $colname Sql Column Name 1
86 * @param string $value Sql value 1
87 * @param string $colname2 Sql Column Name 2
88 * @param string $value2 Sql value 2
89 * @return boolean returns true if the sql row does exist
91 function tableHasRow2D($tblname, $colname, $value, $colname2, $value2) {
92 $row = sqlQuery("SELECT COUNT(*) AS count FROM $tblname WHERE " .
93 "$colname LIKE '$value' AND $colname2 LIKE '$value2'");
94 return $row['count'] ? true : false;
97 /**
98 * Check if a Sql row exists. (with three values)
100 * @param string $tblname Sql Table Name
101 * @param string $colname Sql Column Name 1
102 * @param string $value Sql value 1
103 * @param string $colname2 Sql Column Name 2
104 * @param string $value2 Sql value 2
105 * @param string $colname3 Sql Column Name 3
106 * @param string $value3 Sql value 3
107 * @return boolean returns true if the sql row does exist
109 function tableHasRow3D($tblname, $colname, $value, $colname2, $value2, $colname3, $value3) {
110 $row = sqlQuery("SELECT COUNT(*) AS count FROM $tblname WHERE " .
111 "$colname LIKE '$value' AND $colname2 LIKE '$value2' AND $colname3 LIKE '$value3'");
112 return $row['count'] ? true : false;
116 * Check if a Sql row exists. (with four values)
118 * @param string $tblname Sql Table Name
119 * @param string $colname Sql Column Name 1
120 * @param string $value Sql value 1
121 * @param string $colname2 Sql Column Name 2
122 * @param string $value2 Sql value 2
123 * @param string $colname3 Sql Column Name 3
124 * @param string $value3 Sql value 3
125 * @param string $colname4 Sql Column Name 4
126 * @param string $value4 Sql value 4
127 * @return boolean returns true if the sql row does exist
129 function tableHasRow4D($tblname, $colname, $value, $colname2, $value2, $colname3, $value3, $colname4, $value4) {
130 $row = sqlQuery("SELECT COUNT(*) AS count FROM $tblname WHERE " .
131 "$colname LIKE '$value' AND $colname2 LIKE '$value2' AND $colname3 LIKE '$value3' AND $colname4 LIKE '$value4'");
132 return $row['count'] ? true : false;
136 * Check if a Sql table has a certain index/key.
138 * @param string $tblname Sql Table Name
139 * @param string $colname Sql Index/Key
140 * @return boolean returns true if the sql tables has the specified index/key
142 function tableHasIndex($tblname, $colname) {
143 $row = sqlQuery("SHOW INDEX FROM `$tblname` WHERE `Key_name` = '$colname'");
144 return (empty($row)) ? false : true;
147 * Check if a list exists.
149 * @param string $option_id Sql List Option ID
150 * @return boolean returns true if the list exists
152 function listExists($option_id) {
153 $row = sqlQuery("SELECT * FROM list_options WHERE list_id = 'lists' AND option_id = ?", array($option_id));
154 if (empty($row)) return false;
155 return true;
158 * Function to migrate the Clickoptions settings (if exist) from the codebase into the database.
159 * Note this function is only run once in the sql upgrade script (from 4.1.1 to 4.1.2) if the
160 * issue_types sql table does not exist.
162 function clickOptionsMigrate() {
163 // If the clickoptions.txt file exist, then import it.
164 if (file_exists(dirname(__FILE__)."/../sites/".$_SESSION['site_id']."/clickoptions.txt")) {
165 $file_handle = fopen(dirname(__FILE__)."/../sites/".$_SESSION['site_id']."/clickoptions.txt", "rb");
166 $seq = 10;
167 $prev = '';
168 echo "Importing clickoption setting<br>";
169 while (!feof($file_handle) ) {
170 $line_of_text = fgets($file_handle);
171 if (preg_match('/^#/', $line_of_text)) continue;
172 if ($line_of_text == "") continue;
173 $parts = explode('::', $line_of_text);
174 $parts[0] = trim(str_replace("\r\n","",$parts[0]));
175 $parts[1] = trim(str_replace("\r\n","",$parts[1]));
176 if ($parts[0] != $prev) {
177 $sql1 = "INSERT INTO list_options (`list_id`,`option_id`,`title`) VALUES (?,?,?)";
178 SqlStatement($sql1, array('lists',$parts[0].'_issue_list',ucwords(str_replace("_"," ",$parts[0])).' Issue List') );
179 $seq = 10;
181 $sql2 = "INSERT INTO list_options (`list_id`,`option_id`,`title`,`seq`) VALUES (?,?,?,?)";
182 SqlStatement($sql2, array($parts[0].'_issue_list', $parts[1], $parts[1], $seq) );
183 $seq = $seq + 10;
184 $prev = $parts[0];
186 fclose($file_handle);
190 * Function to create list Occupation.
191 * Note this function is only run once in the sql upgrade script if the list Occupation does not exist
193 function CreateOccupationList() {
194 $res = sqlStatement("SELECT DISTINCT occupation FROM patient_data WHERE occupation <> ''");
195 while($row = sqlFetchArray($res)) {
196 $records[] = $row['occupation'];
198 sqlStatement("INSERT INTO list_options (list_id, option_id, title) VALUES('lists', 'Occupation', 'Occupation')");
199 if(count($records)>0) {
200 $seq = 0;
201 foreach ($records as $key => $value) {
202 sqlStatement("INSERT INTO list_options ( list_id, option_id, title, seq) VALUES ('Occupation', ?, ?, ?)", array($value, $value, ($seq+10)));
203 $seq = $seq + 10;
208 * Function to create list reaction.
209 * Note this function is only run once in the sql upgrade script if the list reaction does not exist
211 function CreateReactionList() {
212 $res = sqlStatement("SELECT DISTINCT reaction FROM lists WHERE reaction <> ''");
213 while($row = sqlFetchArray($res)) {
214 $records[] = $row['reaction'];
216 sqlStatement("INSERT INTO list_options (list_id, option_id, title) VALUES('lists', 'reaction', 'Reaction')");
217 if(count($records)>0) {
218 $seq = 0;
219 foreach ($records as $key => $value) {
220 sqlStatement("INSERT INTO list_options ( list_id, option_id, title, seq) VALUES ('reaction', ?, ?, ?)", array($value, $value, ($seq+10)));
221 $seq = $seq + 10;
227 * Function to add existing values in the immunization table to the new immunization manufacturer list
228 * This function will be executed always, but only missing values will ne inserted to the list
230 function CreateImmunizationManufacturerList() {
231 $res = sqlStatement("SELECT DISTINCT manufacturer FROM immunizations WHERE manufacturer <> ''");
232 while($row = sqlFetchArray($res)) {
233 $records[] = $row['manufacturer'];
235 sqlStatement("INSERT INTO list_options (list_id, option_id, title) VALUES ('lists','Immunization_Manufacturer','Immunization Manufacturer')");
236 if(count($records)>0) {
237 $seq = 0;
238 foreach ($records as $key => $value) {
239 sqlStatement("INSERT INTO list_options ( list_id, option_id, title, seq) VALUES ('Immunization_Manufacturer', ?, ?, ?)", array($value, $value, ($seq+10)));
240 $seq = $seq + 10;
246 * Upgrade or patch the database with a selected upgrade/patch file.
248 * The following "functions" within the selected file will be processed:
250 * #IfNotTable
251 * argument: table_name
252 * behavior: if the table_name does not exist, the block will be executed
254 * #IfTable
255 * argument: table_name
256 * behavior: if the table_name does exist, the block will be executed
258 * #IfColumn
259 * arguments: table_name colname
260 * behavior: if the table and column exist, the block will be executed
262 * #IfMissingColumn
263 * arguments: table_name colname
264 * behavior: if the table exists but the column does not, the block will be executed
266 * #IfNotColumnType
267 * arguments: table_name colname value
268 * behavior: If the table table_name does not have a column colname with a data type equal to value, then the block will be executed
270 * #IfNotRow
271 * arguments: table_name colname value
272 * behavior: If the table table_name does not have a row where colname = value, the block will be executed.
274 * #IfNotRow2D
275 * arguments: table_name colname value colname2 value2
276 * behavior: If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed.
278 * #IfNotRow3D
279 * arguments: table_name colname value colname2 value2 colname3 value3
280 * behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed.
282 * #IfNotRow4D
283 * arguments: table_name colname value colname2 value2 colname3 value3 colname4 value4
284 * behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3 AND colname4 = value4, the block will be executed.
286 * #IfNotRow2Dx2
287 * desc: This is a very specialized function to allow adding items to the list_options table to avoid both redundant option_id and title in each element.
288 * arguments: table_name colname value colname2 value2 colname3 value3
289 * behavior: The block will be executed if both statements below are true:
290 * 1) The table table_name does not have a row where colname = value AND colname2 = value2.
291 * 2) The table table_name does not have a row where colname = value AND colname3 = value3.
293 * #IfRow2D
294 * arguments: table_name colname value colname2 value2
295 * behavior: If the table table_name does have a row where colname = value AND colname2 = value2, the block will be executed.
297 * #IfRow3D
298 * arguments: table_name colname value colname2 value2 colname3 value3
299 * behavior: If the table table_name does have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed.
301 * #IfIndex
302 * desc: This function is most often used for dropping of indexes/keys.
303 * arguments: table_name colname
304 * behavior: If the table and index exist the relevant statements are executed, otherwise not.
306 * #IfNotIndex
307 * desc: This function will allow adding of indexes/keys.
308 * arguments: table_name colname
309 * behavior: If the index does not exist, it will be created
311 * #IfNotMigrateClickOptions
312 * Custom function for the importing of the Clickoptions settings (if exist) from the codebase into the database
314 * #IfNotListOccupation
315 * Custom function for creating Occupation List
317 * #IfNotListReaction
318 * Custom function for creating Reaction List
320 * #EndIf
321 * all blocks are terminated with a #EndIf statement.
323 * @param string $filename Sql upgrade/patch filename
325 function upgradeFromSqlFile($filename) {
326 global $webserver_root;
328 flush();
329 echo "<font color='green'>Processing $filename ...</font><br />\n";
331 $fullname = "$webserver_root/sql/$filename";
333 $fd = fopen($fullname, 'r');
334 if ($fd == FALSE) {
335 echo "ERROR. Could not open '$fullname'.\n";
336 flush();
337 break;
340 $query = "";
341 $line = "";
342 $skipping = false;
344 while (!feof ($fd)){
345 $line = fgets($fd, 2048);
346 $line = rtrim($line);
348 if (preg_match('/^\s*--/', $line)) continue;
349 if ($line == "") continue;
351 if (preg_match('/^#IfNotTable\s+(\S+)/', $line, $matches)) {
352 $skipping = tableExists($matches[1]);
353 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
355 else if (preg_match('/^#IfTable\s+(\S+)/', $line, $matches)) {
356 $skipping = ! tableExists($matches[1]);
357 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
359 else if (preg_match('/^#IfColumn\s+(\S+)\s+(\S+)/', $line, $matches)) {
360 if (tableExists($matches[1])) {
361 $skipping = !columnExists($matches[1], $matches[2]);
363 else {
364 // If no such table then the column is deemed "missing".
365 $skipping = true;
367 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
369 else if (preg_match('/^#IfMissingColumn\s+(\S+)\s+(\S+)/', $line, $matches)) {
370 if (tableExists($matches[1])) {
371 $skipping = columnExists($matches[1], $matches[2]);
373 else {
374 // If no such table then the column is deemed not "missing".
375 $skipping = true;
377 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
379 else if (preg_match('/^#IfNotColumnType\s+(\S+)\s+(\S+)\s+(\S+)/', $line, $matches)) {
380 if (tableExists($matches[1])) {
381 $skipping = columnHasType($matches[1], $matches[2], $matches[3]);
383 else {
384 // If no such table then the column type is deemed not "missing".
385 $skipping = true;
387 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
389 else if (preg_match('/^#IfIndex\s+(\S+)\s+(\S+)/', $line, $matches)) {
390 if (tableExists($matches[1])) {
391 // If no such index then skip.
392 $skipping = !tableHasIndex($matches[1], $matches[2]);
394 else {
395 // If no such table then skip.
396 $skipping = true;
398 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
400 else if (preg_match('/^#IfNotIndex\s+(\S+)\s+(\S+)/', $line, $matches)) {
401 if (tableExists($matches[1])) {
402 $skipping = tableHasIndex($matches[1], $matches[2]);
404 else {
405 // If no such table then the index is deemed not "missing".
406 $skipping = true;
408 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
410 else if (preg_match('/^#IfNotRow\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
411 if (tableExists($matches[1])) {
412 $skipping = tableHasRow($matches[1], $matches[2], $matches[3]);
414 else {
415 // If no such table then the row is deemed not "missing".
416 $skipping = true;
418 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
420 else if (preg_match('/^#IfNotRow2D\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
421 if (tableExists($matches[1])) {
422 $skipping = tableHasRow2D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]);
424 else {
425 // If no such table then the row is deemed not "missing".
426 $skipping = true;
428 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
430 else if (preg_match('/^#IfNotRow3D\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
431 if (tableExists($matches[1])) {
432 $skipping = tableHasRow3D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6], $matches[7]);
434 else {
435 // If no such table then the row is deemed not "missing".
436 $skipping = true;
438 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
440 else if (preg_match('/^#IfNotRow4D\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
441 if (tableExists($matches[1])) {
442 $skipping = tableHasRow4D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6], $matches[7], $matches[8], $matches[9]);
444 else {
445 // If no such table then the row is deemed not "missing".
446 $skipping = true;
448 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
450 else if (preg_match('/^#IfNotRow2Dx2\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
451 if (tableExists($matches[1])) {
452 // If either check exist, then will skip
453 $firstCheck = tableHasRow2D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]);
454 $secondCheck = tableHasRow2D($matches[1], $matches[2], $matches[3], $matches[6], $matches[7]);
455 if ($firstCheck || $secondCheck) {
456 $skipping = true;
458 else {
459 $skipping = false;
462 else {
463 // If no such table then the row is deemed not "missing".
464 $skipping = true;
466 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
468 else if (preg_match('/^#IfRow2D\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
469 if (tableExists($matches[1])) {
470 $skipping = !(tableHasRow2D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]));
472 else {
473 // If no such table then should skip.
474 $skipping = true;
476 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
478 else if (preg_match('/^#IfRow3D\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $line, $matches)) {
479 if (tableExists($matches[1])) {
480 $skipping = !(tableHasRow3D($matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6], $matches[7]));
482 else {
483 // If no such table then should skip.
484 $skipping = true;
486 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
488 else if (preg_match('/^#IfNotMigrateClickOptions/', $line)) {
489 if (tableExists("issue_types")) {
490 $skipping = true;
492 else {
493 // Create issue_types table and import the Issue Types and clickoptions settings from codebase into the database
494 clickOptionsMigrate();
495 $skipping = false;
497 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
499 else if (preg_match('/^#IfNotListOccupation/', $line)) {
500 if ( (listExists("Occupation")) || (!columnExists('patient_data','occupation')) ) {
501 $skipping = true;
503 else {
504 // Create Occupation list
505 CreateOccupationList();
506 $skipping = false;
507 echo "<font color='green'>Built Occupation List</font><br />\n";
509 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
511 else if (preg_match('/^#IfNotListReaction/', $line)) {
512 if ( (listExists("reaction")) || (!columnExists('lists','reaction')) ) {
513 $skipping = true;
515 else {
516 // Create Reaction list
517 CreateReactionList();
518 $skipping = false;
519 echo "<font color='green'>Built Reaction List</font><br />\n";
521 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
523 else if (preg_match('/^#IfNotListImmunizationManufacturer/', $line)){
524 if ( listExists("Immunization_Manufacturer") ) {
525 $skipping = true;
527 else {
528 // Create Immunization Manufacturer list
529 CreateImmunizationManufacturerList();
530 $skipping = false;
531 echo "<font color='green'>Built Immunization Manufacturer List</font><br />\n";
533 if ($skipping) echo "<font color='green'>Skipping section $line</font><br />\n";
535 else if (preg_match('/^#EndIf/', $line)) {
536 $skipping = false;
539 if (preg_match('/^\s*#/', $line)) continue;
540 if ($skipping) continue;
542 $query = $query . $line;
543 if (substr($query, -1) == ';') {
544 $query = rtrim($query, ';');
545 echo "$query<br />\n";
546 if (!sqlStatement($query)) {
547 echo "<font color='red'>The above statement failed: " .
548 getSqlLastError() . "<br />Upgrading will continue.<br /></font>\n";
550 $query = '';
553 flush();
554 } // end function