some cleanup - several user interface improvements
[openemr.git] / library / sql.inc
blob9feb0d5d44444398d7df37f0fea964e5e7130aff
1 <?php
2 /**
3 * Sql functions/classes for OpenEMR.
5 * Includes classes and functions that OpenEMR uses
6 * to interact with SQL.
8 * LICENSE: This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program.  If not, see <http://opensource.org/licenses/gpl-license.php>.
19 * @package   OpenEMR
20 * @link      http://www.open-emr.org
23 require_once(dirname(__FILE__) . "/sqlconf.php");
24 require_once(dirname(__FILE__) . "/../vendor/adodb/adodb-php/adodb.inc.php");
25 require_once(dirname(__FILE__) . "/../vendor/adodb/adodb-php/drivers/adodb-mysqli.inc.php");
26 require_once(dirname(__FILE__) . "/log.inc");
28 /**
29 * ADODB_mysql class wrapper to ensure proper auditing in OpenEMR.
31 * @author  Kevin Yeh <kevin.y@integralemr.com>
33 class ADODB_mysqli_log extends ADODB_mysqli
35         /**
36         * ADODB Execute function wrapper to ensure proper auditing in OpenEMR.
37         *
38         * @param  string  $sql         query
39         * @param  array   $inputarr    binded variables array (optional)
40         * @return boolean              returns false if error
41         */
42     function Execute($sql, $inputarr = false)
43     {
44         $retval= parent::Execute($sql, $inputarr);
45         if ($retval === false) {
46             $outcome = false;
47           // Stash the error into last_mysql_error so it doesn't get clobbered when
48           // we insert into the audit log.
49             $GLOBALS['last_mysql_error']=$this->ErrorMsg();
51           // Last error no
52             $GLOBALS['last_mysql_error_no']=$this->ErrorNo();
53         } else {
54             $outcome = true;
55         }
57         // Stash the insert ID into lastidado so it doesn't get clobbered when
58         // we insert into the audit log.
59         $GLOBALS['lastidado']=$this->Insert_ID();
60         auditSQLEvent($sql, $outcome, $inputarr);
61         return $retval;
62     }
64         /**
65         * ADODB Execute function wrapper to skip auditing in OpenEMR.
66         *
67         * Bypasses the OpenEMR auditing engine.
68         *
69         * @param  string  $sql         query
70         * @param  array   $inputarr    binded variables array (optional)
71         * @return boolean              returns false if error
72         */
73     function ExecuteNoLog($sql, $inputarr = false)
74     {
75         return parent::Execute($sql, $inputarr);
76     }
78         /*
79         * ADODB GenID function wrapper to work with OpenEMR.
80         *
81         * Need to override to fix a bug where call to GenID was updating
82         * sequences table but always returning a zero with the OpenEMR audit
83         * engine both on and off. Note this bug only appears to occur in recent
84         * php versions on windows. The fix is to use the ExecuteNoLog() function
85         * rather than the Execute() functions within this function (otherwise,
86         * there are no other changes from the original ADODB GenID function).
87         *
88         * @param  string  $seqname     table name containing sequence (default is adodbseq)
89         * @param  integer $startID     id to start with for a new sequence (default is 1)
90         * @return integer              returns the sequence integer
91         */
92     function GenID($seqname = 'adodbseq', $startID = 1)
93     {
94         // post-nuke sets hasGenID to false
95         if (!$this->hasGenID) {
96             return false;
97         }
99         $getnext = sprintf($this->_genIDSQL, $seqname);
100         $holdtransOK = $this->_transOK; // save the current status
101         $rs = @$this->ExecuteNoLog($getnext);
102         if (!$rs) {
103             if ($holdtransOK) {
104                 $this->_transOK = true; //if the status was ok before reset
105             }
107             $u = strtoupper($seqname);
108             $this->ExecuteNoLog(sprintf($this->_genSeqSQL, $seqname));
109             $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL, $seqname));
110             if (!$cnt) {
111                 $this->ExecuteNoLog(sprintf($this->_genSeq2SQL, $seqname, $startID-1));
112             }
114             $rs = $this->ExecuteNoLog($getnext);
115         }
117         if ($rs) {
118             $this->genID = mysqli_insert_id($this->_connectionID);
119             $rs->Close();
120         } else {
121             $this->genID = 0;
122         }
124         return $this->genID;
125     }
127 if (!defined('ADODB_FETCH_ASSOC')) {
128     define('ADODB_FETCH_ASSOC', 2);
131 $database = NewADOConnection("mysqli_log"); // Use the subclassed driver which logs execute events
132 // Below clientFlags flag is telling the mysql connection to allow local_infile setting,
133 // which is needed to import data in the Administration->Other->External Data Loads feature.
134 // Note this is a specific bug to work in Ubuntu 12.04, of which the Data Load feature does not
135 // work and is suspicious for a bug in PHP of that OS; Setting this clientFlags fixes this bug
136 // and appears to not cause problems in other operating systems.
137 $database->clientFlags = 128;
138 $database->port = $port;
139 $database->PConnect($host, $login, $pass, $dbase);
140 $GLOBALS['adodb']['db'] = $database;
141 $GLOBALS['dbh'] = $database->_connectionID;
143 // Modified 5/2009 by BM for UTF-8 project ---------
144 if (!$disable_utf8_flag) {
145     $success_flag = $database->Execute("SET NAMES 'utf8'");
146     if (!$success_flag) {
147         error_log("PHP custom error: from openemr library/sql.inc  - Unable to set up UTF8 encoding with mysql database: ".getSqlLastError(), 0);
148     }
151 // Turn off STRICT SQL
152 $sql_strict_set_success = $database->Execute("SET sql_mode = ''");
153 if (!$sql_strict_set_success) {
154     error_log("Unable to set strict sql setting: ".getSqlLastError(), 0);
157 // set up associations in adodb calls (not sure why above define
158 //  command does not work)
159 $GLOBALS['adodb']['db']->SetFetchMode(ADODB_FETCH_ASSOC);
161 //fmg: This makes the login screen informative when no connection can be made
162 if (!$GLOBALS['dbh']) {
163   //try to be more helpful
164     if ($host == "localhost") {
165         echo "Check that mysqld is running.<p>";
166     } else {
167         echo "Check that you can ping the server " . text($host) . ".<p>";
168     }//if local
169     HelpfulDie("Could not connect to server!", getSqlLastError());
170     exit;
171 }//if no connection
174 * Standard sql query in OpenEMR.
176 * Function that will allow use of the adodb binding
177 * feature to prevent sql-injection. Will continue to
178 * be compatible with previous function calls that do
179 * not use binding.
180 * It will return a recordset object.
181 * The sqlFetchArray() function should be used to
182 * utilize the return object.
184 * @param  string  $statement  query
185 * @param  array   $binds      binded variables array (optional)
186 * @return recordset
188 function sqlStatement($statement, $binds = false)
190   // Below line is to avoid a nasty bug in windows.
191     if (empty($binds)) {
192         $binds = false;
193     }
195   // Use adodb Execute with binding and return a recordset.
196   //   Note that the auditSQLEvent function is embedded
197   //    in the Execute command.
198     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
199     if ($recordset === false) {
200         HelpfulDie("query failed: $statement", getSqlLastError());
201     }
203     return $recordset;
207 * Specialized sql query in OpenEMR that skips auditing.
209 * Function that will allow use of the adodb binding
210 * feature to prevent sql-injection. Will continue to
211 * be compatible with previous function calls that do
212 * not use binding. It is equivalent to the
213 * sqlStatement() function, EXCEPT it skips the
214 * audit engine. This function should only be used
215 * in very special situations.
216 * It will return a recordset object.
217 * The sqlFetchArray() function should be used to
218 * utilize the return object.
220 * @param  string  $statement  query
221 * @param  array   $binds      binded variables array (optional)
222 * @return recordset
224 function sqlStatementNoLog($statement, $binds = false)
226   // Below line is to avoid a nasty bug in windows.
227     if (empty($binds)) {
228         $binds = false;
229     }
231   // Use adodb ExecuteNoLog with binding and return a recordset.
232     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
233     if ($recordset === false) {
234         HelpfulDie("query failed: $statement", getSqlLastError());
235     }
237     return $recordset;
241 * sqlStatement() function wrapper for CDR engine in OpenEMR.
242 * Allows option to turn on/off auditing specifically for the
243 * CDR engine.
245 * @param  string  $statement  query
246 * @param  array   $binds      binded variables array (optional)
247 * @return recordset/resource
249 function sqlStatementCdrEngine($statement, $binds = false)
251   // Below line is to avoid a nasty bug in windows.
252     if (empty($binds)) {
253         $binds = false;
254     }
256     if ($GLOBALS['audit_events_cdr']) {
257         return sqlStatement($statement, $binds);
258     } else {
259         return sqlStatementNoLog($statement, $binds);
260     }
264 * Returns a row (as an array) from a sql recordset.
266 * Function that will allow use of the adodb binding
267 * feature to prevent sql-injection.
268 * It will act upon the object returned from the
269 * sqlStatement() function (and sqlQ() function).
271 * @param recordset $r
272 * @return array
274 function sqlFetchArray($r)
276   //treat as an adodb recordset
277     if ($r === false) {
278         return false;
279     }
281     if ($r->EOF) {
282         return false;
283     }
285   //ensure it's an object (ie. is set)
286     if (!is_object($r)) {
287         return false;
288     }
290     return $r->FetchRow();
295  * Wrapper for ADODB getAssoc
297  * @see http://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getassoc
299  * @param string $sql
300  * @param string[] $bindvars
301  * @param boolean $forceArray
302  * @param boolean $first2Cols
303  * @return array
304  */
305 function sqlGetAssoc($sql, $bindvars = false, $forceArray = false, $first2Cols = false)
308     return $GLOBALS['adodb']['db']->getAssoc($sql, $bindvars, $forceArray, $first2Cols);
312 * Standard sql insert query in OpenEMR.
314 * Function that will allow use of the adodb binding
315 * feature to prevent sql-injection. This function
316 * is specialized for insert function and will return
317 * the last id generated from the insert.
319 * @param  string   $statement  query
320 * @param  array    $binds      binded variables array (optional)
321 * @return integer  Last id generated from the sql insert command
323 function sqlInsert($statement, $binds = false)
325   // Below line is to avoid a nasty bug in windows.
326     if (empty($binds)) {
327         $binds = false;
328     }
330   //Run a adodb execute
331   // Note the auditSQLEvent function is embedded in the
332   //   Execute function.
333     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
334     if ($recordset === false) {
335         HelpfulDie("insert failed: $statement", getSqlLastError());
336     }
338   // Return the correct last id generated using function
339   //   that is safe with the audit engine.
340     return getSqlLastID();
344 * Specialized sql query in OpenEMR that only returns
345 * the first row of query results as an associative array.
347 * Function that will allow use of the adodb binding
348 * feature to prevent sql-injection.
350 * @param  string  $statement  query
351 * @param  array   $binds      binded variables array (optional)
352 * @return array
354 function sqlQuery($statement, $binds = false)
356   // Below line is to avoid a nasty bug in windows.
357     if (empty($binds)) {
358         $binds = false;
359     }
361     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
363     if ($recordset === false) {
364         HelpfulDie("query failed: $statement", getSqlLastError());
365     }
367     if ($recordset->EOF) {
368         return false;
369     }
371     $rez = $recordset->FetchRow();
372     if ($rez == false) {
373         return false;
374     }
376     return $rez;
380 * Specialized sql query in OpenEMR that bypasses the auditing engine
381 * and only returns the first row of query results as an associative array.
383 * Function that will allow use of the adodb binding
384 * feature to prevent sql-injection. It is equivalent to the
385 * sqlQuery() function, EXCEPT it skips the
386 * audit engine. This function should only be used
387 * in very special situations.
389 * @param  string  $statement  query
390 * @param  array   $binds      binded variables array (optional)
391 * @return array
393 function sqlQueryNoLog($statement, $binds = false)
395   // Below line is to avoid a nasty bug in windows.
396     if (empty($binds)) {
397         $binds = false;
398     }
400     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
402     if ($recordset === false) {
403         HelpfulDie("query failed: $statement", getSqlLastError());
404     }
406     if ($recordset->EOF) {
407         return false;
408     }
410     $rez = $recordset->FetchRow();
411     if ($rez == false) {
412         return false;
413     }
415     return $rez;
419 * Specialized sql query in OpenEMR that ignores sql errors, bypasses the
420 * auditing engine and only returns the first row of query results as an
421 * associative array.
423 * Function that will allow use of the adodb binding
424 * feature to prevent sql-injection. It is equivalent to the
425 * sqlQuery() function, EXCEPT it skips the
426 * audit engine and ignores erros. This function should only be used
427 * in very special situations.
429 * @param  string  $statement  query
430 * @param  array   $binds      binded variables array (optional)
431 * @return array
433 function sqlQueryNoLogIgnoreError($statement, $binds = false)
435   // Below line is to avoid a nasty bug in windows.
436     if (empty($binds)) {
437         $binds = false;
438     }
440     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
442     if ($recordset === false) {
443         // ignore the error and return FALSE
444         return false;
445     }
447     if ($recordset->EOF) {
448         return false;
449     }
451     $rez = $recordset->FetchRow();
452     if ($rez == false) {
453         return false;
454     }
456     return $rez;
460 * sqlQuery() function wrapper for CDR engine in OpenEMR.
461 * Allows option to turn on/off auditing specifically for the
462 * CDR engine.
464 * @param  string  $statement  query
465 * @param  array   $binds      binded variables array (optional)
466 * @return array
468 function sqlQueryCdrEngine($statement, $binds = false)
470   // Below line is to avoid a nasty bug in windows.
471     if (empty($binds)) {
472         $binds = false;
473     }
475     if ($GLOBALS['audit_events_cdr']) {
476         return sqlQuery($statement, $binds);
477     } else {
478         return sqlQueryNoLog($statement, $binds);
479     }
483 * Specialized sql query in OpenEMR that skips auditing.
485 * This function should only be used in very special situations.
487 * @param  string  $statement  query
489 function sqlInsertClean_audit($statement)
492     $ret = $GLOBALS['adodb']['db']->ExecuteNoLog($statement);
493     if ($ret === false) {
494         HelpfulDie("insert failed: $statement", getSqlLastError());
495     }
499 * Function that will safely return the last ID inserted,
500 * and accounts for the audit engine.
502 * @return  integer Last ID that was inserted into sql
504 function getSqlLastID()
506     return $GLOBALS['lastidado'] > 0 ? $GLOBALS['lastidado'] : $GLOBALS['adodb']['db']->Insert_ID();
510 * Function that will safely return the last error,
511 * and accounts for the audit engine.
513 * @param   string  $mode either adodb(default) or native_mysql
514 * @return  string        last mysql error
516 function getSqlLastError()
518     return !empty($GLOBALS['last_mysql_error']) ? $GLOBALS['last_mysql_error'] : $GLOBALS['adodb']['db']->ErrorMsg();
522  * Function that will safely return the last error no,
523  * and accounts for the audit engine.
525  * @param   string  $mode either adodb(default) or native_mysql
526  * @return  string        last mysql error no
527  */
528 function getSqlLastErrorNo()
530     return !empty($GLOBALS['last_mysql_error_no']) ? $GLOBALS['last_mysql_error_no'] : $GLOBALS['adodb']['db']->ErrorNo();
534 * Function that will return an array listing
535 * of columns that exist in a table.
537 * @param   string  $table sql table
538 * @return  array
540 function sqlListFields($table)
542     $sql = "SHOW COLUMNS FROM ". add_escape_custom($table);
543     $resource = sqlQ($sql);
544     $field_list = array();
545     while ($row = sqlFetchArray($resource)) {
546         $field_list[] = $row['Field'];
547     }
549     return $field_list;
553 * Returns the number of sql rows
555 * @param recordset $r
556 * @return integer Number of rows
558 function sqlNumRows($r)
560     return $r->RecordCount();
564 * Error function for OpenEMR sql functions
566 * @param string $statement
567 * @param string $sqlerr
569 function HelpfulDie($statement, $sqlerr = '')
572     echo "<h2><font color='red'>" . xlt('Query Error') . "</font></h2>";
574     if (!$GLOBALS['sql_string_no_show_screen']) {
575         echo "<p><font color='red'>ERROR:</font> " . text($statement) . "</p>";
576     }
578     $logMsg="SQL Error with statement:".$statement;
580     if ($sqlerr) {
581         if (!$GLOBALS['sql_string_no_show_screen']) {
582              echo "<p>Error: <font color='red'>" . text($sqlerr) . "</font></p>";
583         }
585         $logMsg.="--".$sqlerr;
586     }//if error
588     $backtrace = debug_backtrace();
590     if (!$GLOBALS['sql_string_no_show_screen']) {
591         for ($level = 1; $level < count($backtrace); $level++) {
592             $info = $backtrace[$level];
593             echo "<br>" . text($info["file"] . " at " . $info["line"] . ":" . $info["function"]);
594             if ($level > 1) {
595                 echo "(" . text(implode(",", $info["args"])) . ")";
596             }
597         }
598     }
600     $logMsg.="==>".$backtrace[1]["file"]." at ".$backtrace[1]["line"].":".$backtrace[1]["function"];
602     error_log($logMsg);
604     exit;
608 * @todo document use of the generate_id function
610 function generate_id()
612     $database = $GLOBALS['adodb']['db'];
613     return $database->GenID("sequences");
617 * Deprecated function. Standard sql query in OpenEMR.
619 * Function that will allow use of the adodb binding
620 * feature to prevent sql-injection. Will continue to
621 * be compatible with previous function calls that do
622 * not use binding.
623 * It will return a recordset object.
624 * The sqlFetchArray() function should be used to
625 * utilize the return object.
627 * @deprecated
628 * @param  string  $statement  query
629 * @param  array   $binds      binded variables array (optional)
630 * @return recordset
632 function sqlQ($statement, $binds = false)
634   // Below line is to avoid a nasty bug in windows.
635     if (empty($binds)) {
636         $binds = false;
637     }
639     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds) or
640     HelpfulDie("query failed: $statement", getSqlLastError());
641     return $recordset;
645 * Simple wrapper for sqlInsert() function (deprecated).
647 * Function that will allow use of the adodb binding feature
648 * to prevent sql-injection.
650 * @deprecated
651 * @param  string   $statement  query
652 * @param  array    $binds      binded variables array (optional)
653 * @return integer  Last id generated from the sql insert command
655 function idSqlStatement($statement, $binds = false)
657   // Below line is to avoid a nasty bug in windows.
658     if (empty($binds)) {
659         $binds = false;
660     }
662     return sqlInsert($statement, $binds);
666 * Simple wrapper for sqlInsert() function (deprecated).
668 * Function that will allow use of the adodb binding feature
669 * to prevent sql-injection.
671 * @deprecated
672 * @param  string   $statement  query
673 * @param  array    $binds      binded variables array (optional)
674 * @return integer  Last id generated from the sql insert command
676 function sqlInsertClean($statement, $binds = false)
678   // Below line is to avoid a nasty bug in windows.
679     if (empty($binds)) {
680         $binds = false;
681     }
683     return sqlInsert($statement, $binds);
688 * Sql close connection function (deprecated)
690 * No longer needed since PHP does this automatically.
692 * @deprecated
693 * @return boolean
695 function sqlClose()
697   //----------Close our mysql connection
698     $closed = $GLOBALS['adodb']['db']->close or
699     HelpfulDie("could not disconnect from mysql server link", getSqlLastError());
700     return $closed;
704 * Very simple wrapper function and not necessary (deprecated)
706 * Do not use.
708 * @deprecated
709 * @return connection
711 function get_db()
713     return $GLOBALS['adodb']['db'];
717  * Generic mysql select db function
718  * Used when converted to mysqli to centralize special circumstances.
719  * @param string $database
720  */
721 function generic_sql_select_db($database, $link = null)
723     if (is_null($link)) {
724         $link = $GLOBALS['dbh'];
725     }
727     mysqli_select_db($link, $database);
731  * Generic mysql affected rows function
732  * Used when converted to mysqli to centralize special circumstances.
734  */
735 function generic_sql_affected_rows()
737     return mysqli_affected_rows($GLOBALS['dbh']);
741  * Generic mysql insert id function
742  * Used when converted to mysqli to centralize special circumstances.
744                  */
745 function generic_sql_insert_id()
747     return mysqli_insert_id($GLOBALS['dbh']);
752  * Begin a Transaction.
753  */
754 function sqlBeginTrans()
756     $GLOBALS['adodb']['db']->BeginTrans();
761  * Commit a transaction
762  */
763 function sqlCommitTrans($ok = true)
765     $GLOBALS['adodb']['db']->CommitTrans();
770  * Rollback a transaction
771  */
772 function sqlRollbackTrans()
774     $GLOBALS['adodb']['db']->RollbackTrans();