Converted referrals_report.php to standard security model, take 2.
[openemr.git] / library / sql.inc
blob7d40171274b8f85b1167e0fff1b337e3ef97e88e
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>.
18
19 * @package   OpenEMR
20 * @link      http://www.open-emr.org
23 require_once(dirname(__FILE__) . "/sqlconf.php");
24 require_once(dirname(__FILE__) . "/adodb/adodb.inc.php");
25 require_once(dirname(__FILE__) . "/adodb/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             }
54             else {
55               $outcome = true;
56             }
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) return false;
97                 $getnext = sprintf($this->_genIDSQL,$seqname);
98                 $holdtransOK = $this->_transOK; // save the current status
99                 $rs = @$this->ExecuteNoLog($getnext);
100                 if (!$rs) {
101                         if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
102                         $u = strtoupper($seqname);
103                         $this->ExecuteNoLog(sprintf($this->_genSeqSQL,$seqname));
104                         $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname));
105                         if (!$cnt) $this->ExecuteNoLog(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
106                         $rs = $this->ExecuteNoLog($getnext);
107                 }
109                 if ($rs) {
110                         $this->genID = mysqli_insert_id($this->_connectionID);
111                         $rs->Close();
112                 } else
113                         $this->genID = 0;
115                 return $this->genID;
116         }
118 if (!defined('ADODB_FETCH_ASSOC')) define('ADODB_FETCH_ASSOC', 2);
119 $database = NewADOConnection("mysqli_log"); // Use the subclassed driver which logs execute events
120 // Below clientFlags flag is telling the mysql connection to allow local_infile setting,
121 // which is needed to import data in the Administration->Other->External Data Loads feature.
122 // Note this is a specific bug to work in Ubuntu 12.04, of which the Data Load feature does not
123 // work and is suspicious for a bug in PHP of that OS; Setting this clientFlags fixes this bug
124 // and appears to not cause problems in other operating systems.
125 $database->clientFlags = 128;
126 $database->port = $port;
127 $database->PConnect($host, $login, $pass, $dbase);
128 $GLOBALS['adodb']['db'] = $database;
129 $GLOBALS['dbh'] = $database->_connectionID;
131 // Modified 5/2009 by BM for UTF-8 project ---------
132 if (!$disable_utf8_flag) {
133  $success_flag = $database->Execute("SET NAMES 'utf8'");
134   if (!$success_flag) {
135    error_log("PHP custom error: from openemr library/sql.inc  - Unable to set up UTF8 encoding with mysql database: ".getSqlLastError(), 0);
136   }
139 // Turn off STRICT SQL
140 $sql_strict_set_success = $database->Execute("SET sql_mode = ''");
141 if (!$sql_strict_set_success) {
142  error_log("Unable to set strict sql setting: ".getSqlLastError(), 0);
145 // set up associations in adodb calls (not sure why above define
146 //  command does not work)
147 $GLOBALS['adodb']['db']->SetFetchMode(ADODB_FETCH_ASSOC);
149 //fmg: This makes the login screen informative when no connection can be made
150 if (!$GLOBALS['dbh']) {
151   //try to be more helpful
152   if ($host == "localhost") {
153     echo "Check that mysqld is running.<p>";
154   } else {
155     echo "Check that you can ping the server '".text($host)."'.<p>";
156   }//if local
157   HelpfulDie("Could not connect to server!", getSqlLastError());
158   exit;
159 }//if no connection
162 * Standard sql query in OpenEMR.
164 * Function that will allow use of the adodb binding
165 * feature to prevent sql-injection. Will continue to
166 * be compatible with previous function calls that do
167 * not use binding.
168 * It will return a recordset object.
169 * The sqlFetchArray() function should be used to
170 * utilize the return object.
172 * @param  string  $statement  query
173 * @param  array   $binds      binded variables array (optional)
174 * @return recordset
176 function sqlStatement($statement, $binds=false )
178   // Below line is to avoid a nasty bug in windows.
179   if (empty($binds)) $binds = false;
181   // Use adodb Execute with binding and return a recordset.
182   //   Note that the auditSQLEvent function is embedded
183   //    in the Execute command.
184   $recordset = $GLOBALS['adodb']['db']->Execute( $statement, $binds );
185   if ($recordset === FALSE) {
186     HelpfulDie("query failed: $statement", getSqlLastError());
187   }
188   return $recordset;
192 * Specialized sql query in OpenEMR that skips auditing.
194 * Function that will allow use of the adodb binding
195 * feature to prevent sql-injection. Will continue to
196 * be compatible with previous function calls that do
197 * not use binding. It is equivalent to the 
198 * sqlStatement() function, EXCEPT it skips the
199 * audit engine. This function should only be used
200 * in very special situations.
201 * It will return a recordset object.
202 * The sqlFetchArray() function should be used to
203 * utilize the return object.
205 * @param  string  $statement  query
206 * @param  array   $binds      binded variables array (optional)
207 * @return recordset
209 function sqlStatementNoLog($statement, $binds=false )
211   // Below line is to avoid a nasty bug in windows.
212   if (empty($binds)) $binds = false;
214   // Use adodb ExecuteNoLog with binding and return a recordset.
215   $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog( $statement, $binds );
216   if ($recordset === FALSE) {
217     HelpfulDie("query failed: $statement", getSqlLastError());
218   }
219   return $recordset;
223 * sqlStatement() function wrapper for CDR engine in OpenEMR.
224 * Allows option to turn on/off auditing specifically for the
225 * CDR engine.
227 * @param  string  $statement  query
228 * @param  array   $binds      binded variables array (optional)
229 * @return recordset/resource
231 function sqlStatementCdrEngine($statement, $binds=false )
233   // Below line is to avoid a nasty bug in windows.
234   if (empty($binds)) $binds = false;
236   if ($GLOBALS['audit_events_cdr']) {
237     return sqlStatement($statement,$binds);
238   }
239   else {
240     return sqlStatementNoLog($statement,$binds);
241   }
245 * Returns a row (as an array) from a sql recordset.
247 * Function that will allow use of the adodb binding
248 * feature to prevent sql-injection.
249 * It will act upon the object returned from the
250 * sqlStatement() function (and sqlQ() function).
252 * @param recordset $r
253 * @return array
255 function sqlFetchArray($r)
257   //treat as an adodb recordset
258   if ($r === FALSE)
259     return false;
260   if ($r->EOF)
261     return false;
262   //ensure it's an object (ie. is set)
263   if (!is_object($r))
264     return false;
266     return $r->FetchRow();
270 * Standard sql insert query in OpenEMR.
272 * Function that will allow use of the adodb binding
273 * feature to prevent sql-injection. This function
274 * is specialized for insert function and will return
275 * the last id generated from the insert.
277 * @param  string   $statement  query
278 * @param  array    $binds      binded variables array (optional)
279 * @return integer  Last id generated from the sql insert command
281 function sqlInsert($statement, $binds=false)
283   // Below line is to avoid a nasty bug in windows.
284   if (empty($binds)) $binds = false;
286   //Run a adodb execute
287   // Note the auditSQLEvent function is embedded in the
288   //   Execute function.
289   $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
290   if ($recordset === FALSE) {
291     HelpfulDie("insert failed: $statement", getSqlLastError());
292   }
293   // Return the correct last id generated using function
294   //   that is safe with the audit engine.
295   return getSqlLastID();
299 * Specialized sql query in OpenEMR that only returns
300 * the first row of query results as an associative array.
302 * Function that will allow use of the adodb binding
303 * feature to prevent sql-injection.
305 * @param  string  $statement  query
306 * @param  array   $binds      binded variables array (optional)
307 * @return array
309 function sqlQuery($statement, $binds=false)
311   // Below line is to avoid a nasty bug in windows.
312   if (empty($binds)) $binds = false;
314   $recordset = $GLOBALS['adodb']['db']->Execute( $statement, $binds );
316   if ($recordset === FALSE) {
317     HelpfulDie("query failed: $statement", getSqlLastError());
318   }
319   if ($recordset->EOF)
320    return FALSE;
321   $rez = $recordset->FetchRow();
322   if ($rez == FALSE)
323     return FALSE;
324   return $rez;
328 * Specialized sql query in OpenEMR that bypasses the auditing engine
329 * and only returns the first row of query results as an associative array.
331 * Function that will allow use of the adodb binding
332 * feature to prevent sql-injection. It is equivalent to the
333 * sqlQuery() function, EXCEPT it skips the
334 * audit engine. This function should only be used
335 * in very special situations.
337 * @param  string  $statement  query
338 * @param  array   $binds      binded variables array (optional)
339 * @return array
341 function sqlQueryNoLog($statement, $binds=false)
343   // Below line is to avoid a nasty bug in windows.
344   if (empty($binds)) $binds = false;
346   $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog( $statement, $binds );
348   if ($recordset === FALSE) {
349     HelpfulDie("query failed: $statement", getSqlLastError());
350   }
351   if ($recordset->EOF)
352    return FALSE;
353   $rez = $recordset->FetchRow();
354   if ($rez == FALSE)
355     return FALSE;
356   return $rez;
360 * Specialized sql query in OpenEMR that ignores sql errors, bypasses the
361 * auditing engine and only returns the first row of query results as an
362 * associative array.
364 * Function that will allow use of the adodb binding
365 * feature to prevent sql-injection. It is equivalent to the
366 * sqlQuery() function, EXCEPT it skips the
367 * audit engine and ignores erros. This function should only be used
368 * in very special situations.
370 * @param  string  $statement  query
371 * @param  array   $binds      binded variables array (optional)
372 * @return array
374 function sqlQueryNoLogIgnoreError($statement, $binds=false)
376   // Below line is to avoid a nasty bug in windows.
377   if (empty($binds)) $binds = false;
379   $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog( $statement, $binds );
381   if ($recordset === FALSE) {
382     // ignore the error and return FALSE
383     return FALSE;
384   }
385   if ($recordset->EOF)
386    return FALSE;
387   $rez = $recordset->FetchRow();
388   if ($rez == FALSE)
389     return FALSE;
390   return $rez;
394 * sqlQuery() function wrapper for CDR engine in OpenEMR.
395 * Allows option to turn on/off auditing specifically for the
396 * CDR engine.
398 * @param  string  $statement  query
399 * @param  array   $binds      binded variables array (optional)
400 * @return array
402 function sqlQueryCdrEngine($statement, $binds=false )
404   // Below line is to avoid a nasty bug in windows.
405   if (empty($binds)) $binds = false;
407   if ($GLOBALS['audit_events_cdr']) {
408     return sqlQuery($statement,$binds);
409   }
410   else {
411     return sqlQueryNoLog($statement,$binds);
412   }
416 * Specialized sql query in OpenEMR that skips auditing.
418 * This function should only be used in very special situations.
420 * @param  string  $statement  query
422 function sqlInsertClean_audit($statement)
425   $ret = $GLOBALS['adodb']['db']->ExecuteNoLog($statement);
426   if ($ret === FALSE) {
427     HelpfulDie("insert failed: $statement", getSqlLastError());
428   }
432 * Function that will safely return the last ID inserted,
433 * and accounts for the audit engine.
435 * @return  integer Last ID that was inserted into sql
437 function getSqlLastID() {
438     return $GLOBALS['lastidado'] > 0 ? $GLOBALS['lastidado'] : $GLOBALS['adodb']['db']->Insert_ID();
442 * Function that will safely return the last error,
443 * and accounts for the audit engine.
445 * @param   string  $mode either adodb(default) or native_mysql
446 * @return  string        last mysql error
448 function getSqlLastError() {
449     return !empty($GLOBALS['last_mysql_error']) ? $GLOBALS['last_mysql_error'] : $GLOBALS['adodb']['db']->ErrorMsg();
453  * Function that will safely return the last error no,
454  * and accounts for the audit engine.
456  * @param   string  $mode either adodb(default) or native_mysql
457  * @return  string        last mysql error no
458  */
459 function getSqlLastErrorNo() {
460     return !empty($GLOBALS['last_mysql_error_no']) ? $GLOBALS['last_mysql_error_no'] : $GLOBALS['adodb']['db']->ErrorNo();
464 * Function that will return an array listing
465 * of columns that exist in a table.
467 * @param   string  $table sql table
468 * @return  array
470 function sqlListFields($table) {
471   $sql = "SHOW COLUMNS FROM ". add_escape_custom($table);
472   $resource = sqlQ($sql);
473   $field_list = array();
474   while($row = sqlFetchArray($resource)) {
475     $field_list[] = $row['Field'];
476   }
477   return $field_list;
481 * Returns the number of sql rows
483 * @param recordset $r
484 * @return integer Number of rows
486 function sqlNumRows($r)
488   return $r->RecordCount();
492 * Error function for OpenEMR sql functions
494 * @param string $statement
495 * @param string $sqlerr
497 function HelpfulDie ($statement, $sqlerr='')
499   echo "<p><p><font color='red'>ERROR:</font> ".text($statement)."<p>";
500   $logMsg="SQL Error with statement:".$statement;
501   if ($sqlerr) {
502     echo "Error: <font color='red'>".text($sqlerr)."</font><p>";
503     $logMsg.="--".$sqlerr;
504   }//if error
505   $backtrace=debug_backtrace();
506   for($level=1;$level<count($backtrace);$level++)
507   {
508       $info=$backtrace[$level];
509       echo "<br>".text($info["file"]." at ".$info["line"].":".$info["function"]);
510       if($level>1){
511           echo "(".text(implode(",",$info["args"])).")";
512       }
513   }
514   $logMsg.="==>".$backtrace[1]["file"]." at ".$backtrace[1]["line"].":".$backtrace[1]["function"];
515   error_log($logMsg);
516   
517   exit;
521 * @todo document use of the generate_id function
523 function generate_id () {
524   $database = $GLOBALS['adodb']['db'];
525   return $database->GenID("sequences");
529 * Deprecated function. Standard sql query in OpenEMR.
531 * Function that will allow use of the adodb binding
532 * feature to prevent sql-injection. Will continue to
533 * be compatible with previous function calls that do
534 * not use binding.
535 * It will return a recordset object.
536 * The sqlFetchArray() function should be used to
537 * utilize the return object.
539 * @deprecated
540 * @param  string  $statement  query
541 * @param  array   $binds      binded variables array (optional)
542 * @return recordset
544 function sqlQ($statement, $binds=false )
546   // Below line is to avoid a nasty bug in windows.
547   if (empty($binds)) $binds = false;
549   $recordset = $GLOBALS['adodb']['db']->Execute( $statement, $binds ) or
550     HelpfulDie("query failed: $statement", getSqlLastError());
551   return $recordset;
555 * Simple wrapper for sqlInsert() function (deprecated).
557 * Function that will allow use of the adodb binding feature
558 * to prevent sql-injection.
560 * @deprecated
561 * @param  string   $statement  query
562 * @param  array    $binds      binded variables array (optional)
563 * @return integer  Last id generated from the sql insert command
565 function idSqlStatement($statement , $binds=false )
567   // Below line is to avoid a nasty bug in windows.
568   if (empty($binds)) $binds = false;
570   return sqlInsert($statement, $binds);
574 * Simple wrapper for sqlInsert() function (deprecated).
576 * Function that will allow use of the adodb binding feature
577 * to prevent sql-injection.
579 * @deprecated
580 * @param  string   $statement  query
581 * @param  array    $binds      binded variables array (optional)
582 * @return integer  Last id generated from the sql insert command
584 function sqlInsertClean($statement, $binds=false )
586   // Below line is to avoid a nasty bug in windows.
587   if (empty($binds)) $binds = false;
589   return sqlInsert($statement, $binds);
594 * Sql close connection function (deprecated)
596 * No longer needed since PHP does this automatically.
598 * @deprecated
599 * @return boolean
601 function sqlClose()
603   //----------Close our mysql connection
604   $closed = $GLOBALS['adodb']['db']->close or
605     HelpfulDie("could not disconnect from mysql server link", getSqlLastError());
606   return $closed;
610 * Very simple wrapper function and not necessary (deprecated)
612 * Do not use.
614 * @deprecated
615 * @return connection
617 function get_db() {
618   return $GLOBALS['adodb']['db'];
622  * Generic mysql select db function
623  * Used when converted to mysqli to centralize special circumstances.
624  * @param string $database
625  */
626 function generic_sql_select_db($database, $link = null)
628   if (is_null($link))
629     $link = $GLOBALS['dbh'];
630   mysqli_select_db($link, $database);
634  * Generic mysql affected rows function
635  * Used when converted to mysqli to centralize special circumstances.
637  */
638 function generic_sql_affected_rows()
640   return mysqli_affected_rows($GLOBALS['dbh']);
644  * Generic mysql insert id function
645  * Used when converted to mysqli to centralize special circumstances.
647                                  */
648 function generic_sql_insert_id()
650   return mysqli_insert_id($GLOBALS['dbh']);