More Postings EOB features
[openemr.git] / library / sql.inc
blob4f8f99ebb7ea6a7aaef0a40922964608907fdc76
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");
27 require_once(dirname(__FILE__) . "/ADODB_mysqli_log.php");
29 if (!defined('ADODB_FETCH_ASSOC')) {
30     define('ADODB_FETCH_ASSOC', 2);
33 $database = NewADOConnection("mysqli_log"); // Use the subclassed driver which logs execute events
34 // Below optionFlags flag is telling the mysql connection to ensure local_infile setting,
35 // which is needed to import data in the Administration->Other->External Data Loads feature.
36 // (Note the MYSQLI_READ_DEFAULT_GROUP is just to keep the current setting hard-coded in adodb)
37 $database->optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0), array(MYSQLI_OPT_LOCAL_INFILE,1));
38 // Set mysql to use ssl, if applicable.
39 // Can support basic encryption by including just the mysql-ca pem (this is mandatory for ssl)
40 // Can also support client based certificate if also include mysql-cert and mysql-key (this is optional for ssl)
41 if (file_exists($GLOBALS['OE_SITE_DIR'] . "/documents/certificates/mysql-ca")) {
42     if (defined('MYSQLI_CLIENT_SSL')) {
43         $database->clientFlags = MYSQLI_CLIENT_SSL;
44     }
46 $database->port = $port;
47 $database->PConnect($host, $login, $pass, $dbase);
48 $GLOBALS['adodb']['db'] = $database;
49 $GLOBALS['dbh'] = $database->_connectionID;
51 // Modified 5/2009 by BM for UTF-8 project ---------
52 if (!$disable_utf8_flag) {
53     $success_flag = $database->Execute("SET NAMES 'utf8'");
54     if (!$success_flag) {
55         error_log("PHP custom error: from openemr library/sql.inc  - Unable to set up UTF8 encoding with mysql database: ".getSqlLastError(), 0);
56     }
59 // Turn off STRICT SQL
60 $sql_strict_set_success = $database->Execute("SET sql_mode = ''");
61 if (!$sql_strict_set_success) {
62     error_log("Unable to set strict sql setting: ".getSqlLastError(), 0);
65 // set up associations in adodb calls (not sure why above define
66 //  command does not work)
67 $GLOBALS['adodb']['db']->SetFetchMode(ADODB_FETCH_ASSOC);
69 if ($GLOBALS['debug_ssl_mysql_connection']) {
70     error_log("CHECK SSL CIPHER IN MAIN ADODB: " . print_r($GLOBALS['adodb']['db']->ExecuteNoLog("SHOW STATUS LIKE 'Ssl_cipher';")->fields, true));
73 //fmg: This makes the login screen informative when no connection can be made
74 if (!$GLOBALS['dbh']) {
75   //try to be more helpful
76     if ($host == "localhost") {
77         echo "Check that mysqld is running.<p>";
78     } else {
79         echo "Check that you can ping the server " . text($host) . ".<p>";
80     }//if local
81     HelpfulDie("Could not connect to server!", getSqlLastError());
82     exit;
83 }//if no connection
85 /**
86 * Standard sql query in OpenEMR.
88 * Function that will allow use of the adodb binding
89 * feature to prevent sql-injection. Will continue to
90 * be compatible with previous function calls that do
91 * not use binding.
92 * It will return a recordset object.
93 * The sqlFetchArray() function should be used to
94 * utilize the return object.
96 * @param  string  $statement  query
97 * @param  array   $binds      binded variables array (optional)
98 * @return recordset
100 function sqlStatement($statement, $binds = false)
102   // Below line is to avoid a nasty bug in windows.
103     if (empty($binds)) {
104         $binds = false;
105     }
107   // Use adodb Execute with binding and return a recordset.
108   //   Note that the auditSQLEvent function is embedded
109   //    in the Execute command.
110     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
111     if ($recordset === false) {
112         HelpfulDie("query failed: $statement", getSqlLastError());
113     }
115     return $recordset;
119 * Specialized sql query in OpenEMR that skips auditing.
121 * Function that will allow use of the adodb binding
122 * feature to prevent sql-injection. Will continue to
123 * be compatible with previous function calls that do
124 * not use binding. It is equivalent to the
125 * sqlStatement() function, EXCEPT it skips the
126 * audit engine. This function should only be used
127 * in very special situations.
128 * It will return a recordset object.
129 * The sqlFetchArray() function should be used to
130 * utilize the return object.
132 * @param  string  $statement  query
133 * @param  array   $binds      binded variables array (optional)
134 * @return recordset
136 function sqlStatementNoLog($statement, $binds = false)
138   // Below line is to avoid a nasty bug in windows.
139     if (empty($binds)) {
140         $binds = false;
141     }
143   // Use adodb ExecuteNoLog with binding and return a recordset.
144     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
145     if ($recordset === false) {
146         HelpfulDie("query failed: $statement", getSqlLastError());
147     }
149     return $recordset;
153 * sqlStatement() function wrapper for CDR engine in OpenEMR.
154 * Allows option to turn on/off auditing specifically for the
155 * CDR engine.
157 * @param  string  $statement  query
158 * @param  array   $binds      binded variables array (optional)
159 * @return recordset/resource
161 function sqlStatementCdrEngine($statement, $binds = false)
163   // Below line is to avoid a nasty bug in windows.
164     if (empty($binds)) {
165         $binds = false;
166     }
168     if ($GLOBALS['audit_events_cdr']) {
169         return sqlStatement($statement, $binds);
170     } else {
171         return sqlStatementNoLog($statement, $binds);
172     }
176 * Returns a row (as an array) from a sql recordset.
178 * Function that will allow use of the adodb binding
179 * feature to prevent sql-injection.
180 * It will act upon the object returned from the
181 * sqlStatement() function (and sqlQ() function).
183 * @param recordset $r
184 * @return array
186 function sqlFetchArray($r)
188   //treat as an adodb recordset
189     if ($r === false) {
190         return false;
191     }
193     if ($r->EOF) {
194         return false;
195     }
197   //ensure it's an object (ie. is set)
198     if (!is_object($r)) {
199         return false;
200     }
202     return $r->FetchRow();
207  * Wrapper for ADODB getAssoc
209  * @see http://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:getassoc
211  * @param string $sql
212  * @param string[] $bindvars
213  * @param boolean $forceArray
214  * @param boolean $first2Cols
215  * @return array
216  */
217 function sqlGetAssoc($sql, $bindvars = false, $forceArray = false, $first2Cols = false)
220     return $GLOBALS['adodb']['db']->getAssoc($sql, $bindvars, $forceArray, $first2Cols);
224 * Standard sql insert query in OpenEMR.
226 * Function that will allow use of the adodb binding
227 * feature to prevent sql-injection. This function
228 * is specialized for insert function and will return
229 * the last id generated from the insert.
231 * @param  string   $statement  query
232 * @param  array    $binds      binded variables array (optional)
233 * @return integer  Last id generated from the sql insert command
235 function sqlInsert($statement, $binds = false)
237   // Below line is to avoid a nasty bug in windows.
238     if (empty($binds)) {
239         $binds = false;
240     }
242   //Run a adodb execute
243   // Note the auditSQLEvent function is embedded in the
244   //   Execute function.
245     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
246     if ($recordset === false) {
247         HelpfulDie("insert failed: $statement", getSqlLastError());
248     }
250   // Return the correct last id generated using function
251   //   that is safe with the audit engine.
252     return getSqlLastID();
256 * Specialized sql query in OpenEMR that only returns
257 * the first row of query results as an associative array.
259 * Function that will allow use of the adodb binding
260 * feature to prevent sql-injection.
262 * @param  string  $statement  query
263 * @param  array   $binds      binded variables array (optional)
264 * @return array
266 function sqlQuery($statement, $binds = false)
268   // Below line is to avoid a nasty bug in windows.
269     if (empty($binds)) {
270         $binds = false;
271     }
273     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds);
275     if ($recordset === false) {
276         HelpfulDie("query failed: $statement", getSqlLastError());
277     }
279     if ($recordset->EOF) {
280         return false;
281     }
283     $rez = $recordset->FetchRow();
284     if ($rez == false) {
285         return false;
286     }
288     return $rez;
292 * Specialized sql query in OpenEMR that bypasses the auditing engine
293 * and only returns the first row of query results as an associative array.
295 * Function that will allow use of the adodb binding
296 * feature to prevent sql-injection. It is equivalent to the
297 * sqlQuery() function, EXCEPT it skips the
298 * audit engine. This function should only be used
299 * in very special situations.
301 * @param  string  $statement  query
302 * @param  array   $binds      binded variables array (optional)
303 * @return array
305 function sqlQueryNoLog($statement, $binds = false)
307   // Below line is to avoid a nasty bug in windows.
308     if (empty($binds)) {
309         $binds = false;
310     }
312     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
314     if ($recordset === false) {
315         HelpfulDie("query failed: $statement", getSqlLastError());
316     }
318     if ($recordset->EOF) {
319         return false;
320     }
322     $rez = $recordset->FetchRow();
323     if ($rez == false) {
324         return false;
325     }
327     return $rez;
331 * Specialized sql query in OpenEMR that ignores sql errors, bypasses the
332 * auditing engine and only returns the first row of query results as an
333 * associative array.
335 * Function that will allow use of the adodb binding
336 * feature to prevent sql-injection. It is equivalent to the
337 * sqlQuery() function, EXCEPT it skips the
338 * audit engine and ignores erros. This function should only be used
339 * in very special situations.
341 * @param  string  $statement  query
342 * @param  array   $binds      binded variables array (optional)
343 * @return array
345 function sqlQueryNoLogIgnoreError($statement, $binds = false)
347   // Below line is to avoid a nasty bug in windows.
348     if (empty($binds)) {
349         $binds = false;
350     }
352     $recordset = $GLOBALS['adodb']['db']->ExecuteNoLog($statement, $binds);
354     if ($recordset === false) {
355         // ignore the error and return FALSE
356         return false;
357     }
359     if ($recordset->EOF) {
360         return false;
361     }
363     $rez = $recordset->FetchRow();
364     if ($rez == false) {
365         return false;
366     }
368     return $rez;
372 * sqlQuery() function wrapper for CDR engine in OpenEMR.
373 * Allows option to turn on/off auditing specifically for the
374 * CDR engine.
376 * @param  string  $statement  query
377 * @param  array   $binds      binded variables array (optional)
378 * @return array
380 function sqlQueryCdrEngine($statement, $binds = false)
382   // Below line is to avoid a nasty bug in windows.
383     if (empty($binds)) {
384         $binds = false;
385     }
387     if ($GLOBALS['audit_events_cdr']) {
388         return sqlQuery($statement, $binds);
389     } else {
390         return sqlQueryNoLog($statement, $binds);
391     }
395 * Specialized sql query in OpenEMR that skips auditing.
397 * This function should only be used in very special situations.
399 * @param  string  $statement  query
401 function sqlInsertClean_audit($statement)
404     $ret = $GLOBALS['adodb']['db']->ExecuteNoLog($statement);
405     if ($ret === false) {
406         HelpfulDie("insert failed: $statement", getSqlLastError());
407     }
411 * Function that will safely return the last ID inserted,
412 * and accounts for the audit engine.
414 * @return  integer Last ID that was inserted into sql
416 function getSqlLastID()
418     return $GLOBALS['lastidado'] > 0 ? $GLOBALS['lastidado'] : $GLOBALS['adodb']['db']->Insert_ID();
422 * Function that will safely return the last error,
423 * and accounts for the audit engine.
425 * @param   string  $mode either adodb(default) or native_mysql
426 * @return  string        last mysql error
428 function getSqlLastError()
430     return !empty($GLOBALS['last_mysql_error']) ? $GLOBALS['last_mysql_error'] : $GLOBALS['adodb']['db']->ErrorMsg();
434  * Function that will safely return the last error no,
435  * and accounts for the audit engine.
437  * @param   string  $mode either adodb(default) or native_mysql
438  * @return  string        last mysql error no
439  */
440 function getSqlLastErrorNo()
442     return !empty($GLOBALS['last_mysql_error_no']) ? $GLOBALS['last_mysql_error_no'] : $GLOBALS['adodb']['db']->ErrorNo();
446 * Function that will return an array listing
447 * of columns that exist in a table.
449 * @param   string  $table sql table
450 * @return  array
452 function sqlListFields($table)
454     $sql = "SHOW COLUMNS FROM ". add_escape_custom($table);
455     $resource = sqlQ($sql);
456     $field_list = array();
457     while ($row = sqlFetchArray($resource)) {
458         $field_list[] = $row['Field'];
459     }
461     return $field_list;
465 * Returns the number of sql rows
467 * @param recordset $r
468 * @return integer Number of rows
470 function sqlNumRows($r)
472     return $r->RecordCount();
476 * Error function for OpenEMR sql functions
478 * @param string $statement
479 * @param string $sqlerr
481 function HelpfulDie($statement, $sqlerr = '')
484     echo "<h2><font color='red'>" . xlt('Query Error') . "</font></h2>";
486     if (!$GLOBALS['sql_string_no_show_screen']) {
487         echo "<p><font color='red'>ERROR:</font> " . text($statement) . "</p>";
488     }
490     $logMsg="SQL Error with statement:".$statement;
492     if ($sqlerr) {
493         if (!$GLOBALS['sql_string_no_show_screen']) {
494              echo "<p>Error: <font color='red'>" . text($sqlerr) . "</font></p>";
495         }
497         $logMsg.="--".$sqlerr;
498     }//if error
500     $backtrace = debug_backtrace();
502     if (!$GLOBALS['sql_string_no_show_screen']) {
503         for ($level = 1; $level < count($backtrace); $level++) {
504             $info = $backtrace[$level];
505             echo "<br>" . text($info["file"] . " at " . $info["line"] . ":" . $info["function"]);
506             if ($level > 1) {
507                 echo "(" . text(implode(",", $info["args"])) . ")";
508             }
509         }
510     }
512     $logMsg.="==>".$backtrace[1]["file"]." at ".$backtrace[1]["line"].":".$backtrace[1]["function"];
514     error_log($logMsg);
516     exit;
520 * @todo document use of the generate_id function
522 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)) {
548         $binds = false;
549     }
551     $recordset = $GLOBALS['adodb']['db']->Execute($statement, $binds) or
552     HelpfulDie("query failed: $statement", getSqlLastError());
553     return $recordset;
557 * Simple wrapper for sqlInsert() function (deprecated).
559 * Function that will allow use of the adodb binding feature
560 * to prevent sql-injection.
562 * @deprecated
563 * @param  string   $statement  query
564 * @param  array    $binds      binded variables array (optional)
565 * @return integer  Last id generated from the sql insert command
567 function idSqlStatement($statement, $binds = false)
569   // Below line is to avoid a nasty bug in windows.
570     if (empty($binds)) {
571         $binds = false;
572     }
574     return sqlInsert($statement, $binds);
578 * Simple wrapper for sqlInsert() function (deprecated).
580 * Function that will allow use of the adodb binding feature
581 * to prevent sql-injection.
583 * @deprecated
584 * @param  string   $statement  query
585 * @param  array    $binds      binded variables array (optional)
586 * @return integer  Last id generated from the sql insert command
588 function sqlInsertClean($statement, $binds = false)
590   // Below line is to avoid a nasty bug in windows.
591     if (empty($binds)) {
592         $binds = false;
593     }
595     return sqlInsert($statement, $binds);
600 * Sql close connection function (deprecated)
602 * No longer needed since PHP does this automatically.
604 * @deprecated
605 * @return boolean
607 function sqlClose()
609   //----------Close our mysql connection
610     $closed = $GLOBALS['adodb']['db']->close or
611     HelpfulDie("could not disconnect from mysql server link", getSqlLastError());
612     return $closed;
616 * Very simple wrapper function and not necessary (deprecated)
618 * Do not use.
620 * @deprecated
621 * @return connection
623 function get_db()
625     return $GLOBALS['adodb']['db'];
629  * Generic mysql select db function
630  * Used when converted to mysqli to centralize special circumstances.
631  * @param string $database
632  */
633 function generic_sql_select_db($database, $link = null)
635     if (is_null($link)) {
636         $link = $GLOBALS['dbh'];
637     }
639     mysqli_select_db($link, $database);
643  * Generic mysql affected rows function
644  * Used when converted to mysqli to centralize special circumstances.
646  */
647 function generic_sql_affected_rows()
649     return mysqli_affected_rows($GLOBALS['dbh']);
653  * Generic mysql insert id function
654  * Used when converted to mysqli to centralize special circumstances.
656                  */
657 function generic_sql_insert_id()
659     return mysqli_insert_id($GLOBALS['dbh']);
664  * Begin a Transaction.
665  */
666 function sqlBeginTrans()
668     $GLOBALS['adodb']['db']->BeginTrans();
673  * Commit a transaction
674  */
675 function sqlCommitTrans($ok = true)
677     $GLOBALS['adodb']['db']->CommitTrans();
682  * Rollback a transaction
683  */
684 function sqlRollbackTrans()
686     $GLOBALS['adodb']['db']->RollbackTrans();