2 require_once("dbm.php");
3 require_once('include/datetime.php');
6 * @class MySQL database class
8 * For debugging, insert 'dbg(1);' anywhere in the program flow.
9 * dbg(0); will turn it off. Logging is performed at LOGGER_DATA level.
10 * When logging, all binary info is converted to text and html entities are escaped so that
11 * the debugging stream is safe to view within both terminals and web pages.
13 * This class is for the low level database stuff that does driver specific things.
22 public $connected = false;
23 public $error = false;
24 private $_server_info = '';
26 private static $relation = array();
28 function __construct($server, $user, $pass, $db, $install = false) {
31 $stamp1 = microtime(true);
33 $server = trim($server);
38 if (!(strlen($server) && strlen($user))) {
39 $this->connected = false;
45 if (strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
46 if (! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) {
47 $this->error = sprintf(t('Cannot locate DNS info for database server \'%s\''), $server);
48 $this->connected = false;
55 if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) {
56 $this->driver = 'pdo';
57 $connect = "mysql:host=".$server.";dbname=".$db;
58 if (isset($a->config["system"]["db_charset"])) {
59 $connect .= ";charset=".$a->config["system"]["db_charset"];
61 $this->db = @new PDO($connect, $user, $pass);
62 if (!$this->db->errorCode()) {
63 $this->connected = true;
65 } elseif (class_exists('mysqli')) {
66 $this->driver = 'mysqli';
67 $this->db = @new mysqli($server,$user,$pass,$db);
68 if (!mysqli_connect_errno()) {
69 $this->connected = true;
71 if (isset($a->config["system"]["db_charset"])) {
72 $this->db->set_charset($a->config["system"]["db_charset"]);
75 } elseif (function_exists('mysql_connect')) {
76 $this->driver = 'mysql';
77 $this->db = mysql_connect($server,$user,$pass);
78 if ($this->db && mysql_select_db($db,$this->db)) {
79 $this->connected = true;
81 if (isset($a->config["system"]["db_charset"])) {
82 mysql_set_charset($a->config["system"]["db_charset"], $this->db);
86 // No suitable SQL driver was found.
92 if (!$this->connected) {
98 $a->save_timestamp($stamp1, "network");
104 * @brief Returns the MySQL server version string
106 * This function discriminate between the deprecated mysql API and the current
107 * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
111 public function server_info() {
112 if ($this->_server_info == '') {
113 switch ($this->driver) {
115 $this->_server_info = $this->db->getAttribute(PDO::ATTR_SERVER_VERSION);
118 $this->_server_info = $this->db->server_info;
121 $this->_server_info = mysql_get_server_info($this->db);
125 return $this->_server_info;
129 * @brief Returns the selected database name
133 public function database_name() {
134 $r = $this->q("SELECT DATABASE() AS `db`");
140 * @brief Analyze a database query and log this if some conditions are met.
142 * @param string $query The database query that will be analyzed
144 public function log_index($query) {
147 if ($a->config["system"]["db_log_index"] == "") {
151 // Don't explain an explain statement
152 if (strtolower(substr($query, 0, 7)) == "explain") {
156 // Only do the explain on "select", "update" and "delete"
157 if (!in_array(strtolower(substr($query, 0, 6)), array("select", "update", "delete"))) {
161 $r = $this->q("EXPLAIN ".$query);
162 if (!dbm::is_result($r)) {
166 $watchlist = explode(',', $a->config["system"]["db_log_index_watch"]);
167 $blacklist = explode(',', $a->config["system"]["db_log_index_blacklist"]);
169 foreach ($r AS $row) {
170 if ((intval($a->config["system"]["db_loglimit_index"]) > 0)) {
171 $log = (in_array($row['key'], $watchlist) AND
172 ($row['rows'] >= intval($a->config["system"]["db_loglimit_index"])));
177 if ((intval($a->config["system"]["db_loglimit_index_high"]) > 0) AND ($row['rows'] >= intval($a->config["system"]["db_loglimit_index_high"]))) {
181 if (in_array($row['key'], $blacklist) OR ($row['key'] == "")) {
186 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
187 @file_put_contents($a->config["system"]["db_log_index"], datetime_convert()."\t".
188 $row['key']."\t".$row['rows']."\t".$row['Extra']."\t".
189 basename($backtrace[1]["file"])."\t".
190 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
191 substr($query, 0, 2000)."\n", FILE_APPEND);
196 public function q($sql, $onlyquery = false) {
199 if (!$this->db || !$this->connected) {
205 $connstr = ($this->connected() ? "Connected" : "Disonnected");
207 $stamp1 = microtime(true);
211 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
212 $sql = "/*".$a->callstack()." */ ".$sql;
217 switch ($this->driver) {
219 $result = @$this->db->query($sql);
220 // Is used to separate between queries that returning data - or not
221 if (!is_bool($result)) {
222 $columns = $result->columnCount();
226 $result = @$this->db->query($sql);
229 $result = @mysql_query($sql,$this->db);
232 $stamp2 = microtime(true);
233 $duration = (float)($stamp2 - $stamp1);
235 $a->save_timestamp($stamp1, "database");
237 if (strtolower(substr($orig_sql, 0, 6)) != "select") {
238 $a->save_timestamp($stamp1, "database_write");
240 if (x($a->config,'system') && x($a->config['system'],'db_log')) {
241 if (($duration > $a->config["system"]["db_loglimit"])) {
242 $duration = round($duration, 3);
243 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
244 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
245 basename($backtrace[1]["file"])."\t".
246 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
247 substr($sql, 0, 2000)."\n", FILE_APPEND);
251 switch ($this->driver) {
253 $errorInfo = $this->db->errorInfo();
255 $this->error = $errorInfo[2];
256 $this->errorno = $errorInfo[1];
260 if ($this->db->errno) {
261 $this->error = $this->db->error;
262 $this->errorno = $this->db->errno;
266 if (mysql_errno($this->db)) {
267 $this->error = mysql_error($this->db);
268 $this->errorno = mysql_errno($this->db);
272 if (strlen($this->error)) {
273 logger('DB Error ('.$connstr.') '.$this->errorno.': '.$this->error);
280 if ($result === false) {
282 } elseif ($result === true) {
285 switch ($this->driver) {
287 $mesg = $result->rowCount().' results'.EOL;
290 $mesg = $result->num_rows.' results'.EOL;
293 $mesg = mysql_num_rows($result).' results'.EOL;
298 $str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
299 . (($this->error) ? ' error: ' . $this->error : '')
302 logger('dba: ' . $str );
306 * If dbfail.out exists, we will write any failed calls directly to it,
307 * regardless of any logging that may or may nor be in effect.
308 * These usually indicate SQL syntax errors that need to be resolved.
311 if ($result === false) {
312 logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
313 if (file_exists('dbfail.out')) {
314 file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
318 if (is_bool($result)) {
322 $this->result = $result;
327 switch ($this->driver) {
329 while ($x = $result->fetch(PDO::FETCH_ASSOC)) {
332 $result->closeCursor();
335 while ($x = $result->fetch_array(MYSQLI_ASSOC)) {
338 $result->free_result();
341 while ($x = mysql_fetch_array($result, MYSQL_ASSOC)) {
344 mysql_free_result($result);
348 // PDO doesn't return "true" on successful operations - like mysqli does
349 // Emulate this behaviour by checking if the query returned data and had columns
350 // This should be reliable enough
351 if (($this->driver == 'pdo') AND (count($r) == 0) AND ($columns == 0)) {
355 //$a->save_timestamp($stamp1, "database");
358 logger('dba: ' . printable(print_r($r, true)));
363 public function dbg($dbg) {
367 public function escape($str) {
368 if ($this->db && $this->connected) {
369 switch ($this->driver) {
371 return substr(@$this->db->quote($str, PDO::PARAM_STR), 1, -1);
373 return @$this->db->real_escape_string($str);
375 return @mysql_real_escape_string($str,$this->db);
380 function connected() {
381 switch ($this->driver) {
383 // Not sure if this really is working like expected
384 $connected = ($this->db->getAttribute(PDO::ATTR_CONNECTION_STATUS) != "");
387 $connected = $this->db->ping();
390 $connected = mysql_ping($this->db);
396 function insert_id() {
397 switch ($this->driver) {
399 $id = $this->db->lastInsertId();
402 $id = $this->db->insert_id;
405 $id = mysql_insert_id($this->db);
411 function __destruct() {
413 switch ($this->driver) {
421 mysql_close($this->db);
428 * @brief Replaces ANY_VALUE() function by MIN() function,
429 * if the database server does not support ANY_VALUE().
431 * Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
432 * ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
433 * A standard fall-back is to use MIN().
435 * @param string $sql An SQL string without the values
436 * @return string The input SQL string modified if necessary.
438 public function any_value_fallback($sql) {
439 $server_info = $this->server_info();
440 if (version_compare($server_info, '5.7.5', '<') ||
441 (stripos($server_info, 'MariaDB') !== false)) {
442 $sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
448 * @brief Replaces the ? placeholders with the parameters in the $args array
450 * @param string $sql SQL query
451 * @param array $args The parameters that are to replace the ? placeholders
452 * @return string The replaced SQL query
454 static private function replace_parameters($sql, $args) {
456 foreach ($args AS $param => $value) {
457 if (is_int($args[$param]) OR is_float($args[$param])) {
458 $replace = intval($args[$param]);
460 $replace = "'".self::$dbo->escape($args[$param])."'";
463 $pos = strpos($sql, '?', $offset);
464 if ($pos !== false) {
465 $sql = substr_replace($sql, $replace, $pos, 1);
467 $offset = $pos + strlen($replace);
473 * @brief Executes a prepared statement that returns data
474 * @usage Example: $r = p("SELECT * FROM `item` WHERE `guid` = ?", $guid);
475 * @param string $sql SQL statement
476 * @return object statement object
478 static public function p($sql) {
481 $stamp1 = microtime(true);
483 $args = func_get_args();
486 // When the second function parameter is an array then use this as the parameter array
487 if ((count($args) == 1) AND (is_array($args[1]))) {
490 foreach ($params AS $param) {
491 $args[++$i] = $param;
495 if (!self::$dbo OR !self::$dbo->connected) {
499 $sql = self::$dbo->any_value_fallback($sql);
501 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
502 $sql = "/*".$a->callstack()." */ ".$sql;
505 self::$dbo->error = '';
506 self::$dbo->errorno = 0;
508 switch (self::$dbo->driver) {
510 if (!$stmt = self::$dbo->db->prepare($sql)) {
511 $errorInfo = self::$dbo->db->errorInfo();
512 self::$dbo->error = $errorInfo[2];
513 self::$dbo->errorno = $errorInfo[1];
518 foreach ($args AS $param => $value) {
519 $stmt->bindParam($param, $args[$param]);
522 if (!$stmt->execute()) {
523 $errorInfo = $stmt->errorInfo();
524 self::$dbo->error = $errorInfo[2];
525 self::$dbo->errorno = $errorInfo[1];
532 $stmt = self::$dbo->db->stmt_init();
534 if (!$stmt->prepare($sql)) {
535 self::$dbo->error = $stmt->error;
536 self::$dbo->errorno = $stmt->errno;
543 foreach ($args AS $param => $value) {
544 if (is_int($args[$param])) {
546 } elseif (is_float($args[$param])) {
548 } elseif (is_string($args[$param])) {
553 $values[] = &$args[$param];
556 array_unshift($values, $params);
558 call_user_func_array(array($stmt, 'bind_param'), $values);
560 if (!$stmt->execute()) {
561 self::$dbo->error = self::$dbo->db->error;
562 self::$dbo->errorno = self::$dbo->db->errno;
565 $stmt->store_result();
570 // For the old "mysql" functions we cannot use prepared statements
571 $retval = mysql_query(self::replace_parameters($sql, $args), self::$dbo->db);
572 if (mysql_errno(self::$dbo->db)) {
573 self::$dbo->error = mysql_error(self::$dbo->db);
574 self::$dbo->errorno = mysql_errno(self::$dbo->db);
579 if (self::$dbo->errorno != 0) {
580 logger('DB Error '.self::$dbo->errorno.': '.self::$dbo->error."\n".self::replace_parameters($sql, $args));
583 $a->save_timestamp($stamp1, 'database');
585 if (x($a->config,'system') && x($a->config['system'], 'db_log')) {
587 $stamp2 = microtime(true);
588 $duration = (float)($stamp2 - $stamp1);
590 if (($duration > $a->config["system"]["db_loglimit"])) {
591 $duration = round($duration, 3);
592 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
594 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
595 basename($backtrace[1]["file"])."\t".
596 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
597 substr(self::replace_parameters($sql, $args), 0, 2000)."\n", FILE_APPEND);
604 * @brief Executes a prepared statement like UPDATE or INSERT that doesn't return data
606 * @param string $sql SQL statement
607 * @return boolean Was the query successfull? False is returned only if an error occurred
609 static public function e($sql) {
612 $stamp = microtime(true);
614 $args = func_get_args();
616 $stmt = call_user_func_array('self::p', $args);
618 if (is_bool($stmt)) {
620 } elseif (is_object($stmt)) {
628 $a->save_timestamp($stamp, "database_write");
634 * @brief Check if data exists
636 * @param string $sql SQL statement
637 * @return boolean Are there rows for that query?
639 static public function exists($sql) {
640 $args = func_get_args();
642 $stmt = call_user_func_array('self::p', $args);
644 if (is_bool($stmt)) {
647 $retval = (self::num_rows($stmt) > 0);
656 * @brief Fetches the first row
658 * @param string $sql SQL statement
659 * @return array first row of query
661 static public function fetch_first($sql) {
662 $args = func_get_args();
664 $stmt = call_user_func_array('self::p', $args);
666 if (is_bool($stmt)) {
669 $retval = self::fetch($stmt);
678 * @brief Returns the number of rows of a statement
680 * @param object Statement object
681 * @return int Number of rows
683 static public function num_rows($stmt) {
684 switch (self::$dbo->driver) {
686 return $stmt->rowCount();
688 return $stmt->num_rows;
690 return mysql_num_rows($stmt);
696 * @brief Fetch a single row
698 * @param object $stmt statement object
699 * @return array current row
701 static public function fetch($stmt) {
702 if (!is_object($stmt)) {
706 switch (self::$dbo->driver) {
708 return $stmt->fetch(PDO::FETCH_ASSOC);
710 // This code works, but is slow
712 // Bind the result to a result array
716 for ($x = 0; $x < $stmt->field_count; $x++) {
717 $cols[] = &$cols_num[$x];
720 call_user_func_array(array($stmt, 'bind_result'), $cols);
722 if (!$stmt->fetch()) {
727 // We need to get the field names for the array keys
728 // It seems that there is no better way to do this.
729 $result = $stmt->result_metadata();
730 $fields = $result->fetch_fields();
733 foreach ($cols_num AS $param => $col) {
734 $columns[$fields[$param]->name] = $col;
738 return mysql_fetch_array(self::$dbo->result, MYSQL_ASSOC);
743 * @brief Insert a row into a table
745 * @param string $table Table name
746 * @param array $param parameter array
748 * @return boolean was the insert successfull?
750 static public function insert($table, $param) {
751 $sql = "INSERT INTO `".self::$dbo->escape($table)."` (`".implode("`, `", array_keys($param))."`) VALUES (".
752 substr(str_repeat("?, ", count($param)), 0, -2).");";
754 return self::e($sql, $param);
758 * @brief Build the array with the table relations
760 * The array is build from the database definitions in dbstructure.php
762 * This process must only be started once, since the value is cached.
764 static private function build_relation_data() {
765 $definition = db_definition();
767 foreach ($definition AS $table => $structure) {
768 foreach ($structure['fields'] AS $field => $field_struct) {
769 if (isset($field_struct['relation'])) {
770 foreach ($field_struct['relation'] AS $rel_table => $rel_field) {
771 self::$relation[$rel_table][$rel_field][$table][] = $field;
779 * @brief Insert a row into a table
781 * @param string $table Table name
782 * @param array $param parameter array
783 * @param boolean $in_commit Internal use: Only do a commit after the last delete
784 * @param array $callstack Internal use: prevent endless loops
786 * @return boolean|array was the delete successfull? When $in_commit is set: deletion data
788 static public function delete($table, $param, $in_commit = false, &$callstack = array()) {
792 // Create a key for the loop prevention
793 $key = $table.':'.implode(':', array_keys($param)).':'.implode(':', $param);
795 // We quit when this key already exists in the callstack.
796 if (isset($callstack[$key])) {
800 $callstack[$key] = true;
802 $table = self::$dbo->escape($table);
804 $commands[$key] = array('table' => $table, 'param' => $param);
806 // To speed up the whole process we cache the table relations
807 if (count(self::$relation) == 0) {
808 self::build_relation_data();
811 // Is there a relation entry for the table?
812 if (isset(self::$relation[$table])) {
813 // We only allow a simple "one field" relation.
814 $field = array_keys(self::$relation[$table])[0];
815 $rel_def = array_values(self::$relation[$table])[0];
817 // Create a key for preventing double queries
818 $qkey = $field.'-'.$table.':'.implode(':', array_keys($param)).':'.implode(':', $param);
820 // When the search field is the relation field, we don't need to fetch the rows
821 // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend
822 if ((count($param) == 1) AND ($field == array_keys($param)[0])) {
823 foreach ($rel_def AS $rel_table => $rel_fields) {
824 foreach ($rel_fields AS $rel_field) {
825 $retval = self::delete($rel_table, array($rel_field => array_values($param)[0]), true, $callstack);
826 $commands = array_merge($commands, $retval);
829 // We quit when this key already exists in the callstack.
830 } elseif (!isset($callstack[$qkey])) {
832 $callstack[$qkey] = true;
834 // Fetch all rows that are to be deleted
835 $sql = "SELECT ".self::$dbo->escape($field)." FROM `".$table."` WHERE `".
836 implode("` = ? AND `", array_keys($param))."` = ?";
838 $data = self::p($sql, $param);
839 while ($row = self::fetch($data)) {
840 // Now we accumulate the delete commands
841 $retval = self::delete($table, array($field => $row[$field]), true, $callstack);
842 $commands = array_merge($commands, $retval);
845 // Since we had split the delete command we don't need the original command anymore
846 unset($commands[$key]);
851 // Now we finalize the process
853 self::p("START TRANSACTION");
855 $compacted = array();
857 foreach ($commands AS $command) {
858 if (count($command['param']) > 1) {
859 $sql = "DELETE FROM `".$command['table']."` WHERE `".
860 implode("` = ? AND `", array_keys($command['param']))."` = ?";
862 logger(dba::replace_parameters($sql, $command['param']), LOGGER_DATA);
864 if (!self::e($sql, $param)) {
869 $key_table = $command['table'];
870 $key_param = array_keys($command['param'])[0];
871 $value = array_values($command['param'])[0];
873 // Split the SQL queries in chunks of 100 values
874 // We do the $i stuff here to make the code better readable
875 $i = $counter[$key_table][$key_param];
876 if (count($compacted[$key_table][$key_param][$i]) > 100) {
880 $compacted[$key_table][$key_param][$i][$value] = $value;
881 $counter[$key_table][$key_param] = $i;
884 foreach ($compacted AS $table => $values) {
885 foreach ($values AS $field => $field_value_list) {
886 foreach ($field_value_list AS $field_values) {
887 $sql = "DELETE FROM `".$table."` WHERE `".$field."` IN (".
888 substr(str_repeat("?, ", count($field_values)), 0, -2).");";
890 logger(dba::replace_parameters($sql, $field_values), LOGGER_DATA);
892 if (!self::e($sql, $param)) {
907 * @brief Updates rows
909 * Updates rows in the database. When $old_fields is set to an array,
910 * the system will only do an update if the fields in that array changed.
913 * Only the values in $old_fields are compared.
914 * This is an intentional behaviour.
917 * We include the timestamp field in $fields but not in $old_fields.
918 * Then the row will only get the new timestamp when the other fields had changed.
920 * When $old_fields is set to a boolean value the system will do this compare itself.
921 * When $old_fields is set to "true" the system will do an insert if the row doesn't exists.
924 * Only set $old_fields to a boolean value when you are sure that you will update a single row.
925 * When you set $old_fields to "true" then $fields must contain all relevant fields!
927 * @param string $table Table name
928 * @param array $fields contains the fields that are updated
929 * @param array $condition condition array with the key values
930 * @param array|boolean $old_fields array with the old field values that are about to be replaced
932 * @return boolean was the update successfull?
934 static public function update($table, $fields, $condition, $old_fields = array()) {
936 /** @todo We may use MySQL specific functions here:
937 * INSERT INTO `config` (`cat`, `k`, `v`) VALUES ('%s', '%s', '%s') ON DUPLICATE KEY UPDATE `v` = '%s'"
938 * But I think that it doesn't make sense here.
941 $table = self::$dbo->escape($table);
943 if (is_bool($old_fields)) {
944 $sql = "SELECT * FROM `".$table."` WHERE `".
945 implode("` = ? AND `", array_keys($condition))."` = ? LIMIT 1";
948 foreach ($condition AS $value) {
952 $do_insert = $old_fields;
954 $old_fields = self::fetch_first($sql, $params);
955 if (is_bool($old_fields)) {
957 return self::insert($table, $fields);
959 $old_fields = array();
963 $do_update = (count($old_fields) == 0);
965 foreach ($old_fields AS $fieldname => $content) {
966 if (isset($fields[$fieldname])) {
967 if ($fields[$fieldname] == $content) {
968 unset($fields[$fieldname]);
975 if (!$do_update OR (count($fields) == 0)) {
979 $sql = "UPDATE `".$table."` SET `".
980 implode("` = ?, `", array_keys($fields))."` = ? WHERE `".
981 implode("` = ? AND `", array_keys($condition))."` = ?";
984 foreach ($fields AS $value) {
987 foreach ($condition AS $value) {
991 return self::e($sql, $params);
995 * @brief Closes the current statement
997 * @param object $stmt statement object
998 * @return boolean was the close successfull?
1000 static public function close($stmt) {
1001 if (!is_object($stmt)) {
1005 switch (self::$dbo->driver) {
1007 return $stmt->closeCursor();
1009 return $stmt->free_result();
1010 return $stmt->close();
1012 return mysql_free_result($stmt);
1017 function printable($s) {
1018 $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
1019 $s = str_replace("\x00",'.',$s);
1020 if (x($_SERVER,'SERVER_NAME')) {
1021 $s = escape_tags($s);
1026 // Procedural functions
1027 function dbg($state) {
1035 function dbesc($str) {
1038 if ($db && $db->connected) {
1039 return($db->escape($str));
1041 return(str_replace("'","\\'",$str));
1045 // Function: q($sql,$args);
1046 // Description: execute SQL query with printf style args.
1047 // Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
1051 $args = func_get_args();
1054 if ($db && $db->connected) {
1055 $sql = $db->any_value_fallback($sql);
1056 $stmt = @vsprintf($sql,$args); // Disabled warnings
1057 //logger("dba: q: $stmt", LOGGER_ALL);
1058 if ($stmt === false)
1059 logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
1061 $db->log_index($stmt);
1063 return $db->q($stmt);
1068 * This will happen occasionally trying to store the
1069 * session data after abnormal program termination
1072 logger('dba: no database: ' . print_r($args,true));
1077 * @brief Performs a query with "dirty reads"
1079 * By doing dirty reads (reading uncommitted data) no locks are performed
1080 * This function can be used to fetch data that doesn't need to be reliable.
1082 * @param $args Query parameters (1 to N parameters of different types)
1083 * @return array Query array
1088 $args = func_get_args();
1091 if ($db && $db->connected) {
1092 $sql = $db->any_value_fallback($sql);
1093 $stmt = @vsprintf($sql,$args); // Disabled warnings
1094 if ($stmt === false)
1095 logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
1097 $db->log_index($stmt);
1099 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
1100 $retval = $db->q($stmt);
1101 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
1107 * This will happen occasionally trying to store the
1108 * session data after abnormal program termination
1111 logger('dba: no database: ' . print_r($args,true));
1117 * Raw db query, no arguments
1120 function dbq($sql) {
1123 if ($db && $db->connected) {
1124 $ret = $db->q($sql);
1131 // Caller is responsible for ensuring that any integer arguments to
1132 // dbesc_array are actually integers and not malformed strings containing
1133 // SQL injection vectors. All integer array elements should be specifically
1134 // cast to int to avoid trouble.
1135 function dbesc_array_cb(&$item, $key) {
1136 if (is_string($item))
1137 $item = dbesc($item);
1140 function dbesc_array(&$arr) {
1141 if (is_array($arr) && count($arr)) {
1142 array_walk($arr,'dbesc_array_cb');
1146 function dba_timer() {
1147 return microtime(true);