]> git.mxchange.org Git - friendica.git/blob - include/dba.php
4f931e0202fa96f4f66764d973452a0fe0235427
[friendica.git] / include / dba.php
1 <?php
2 require_once("dbm.php");
3 require_once('include/datetime.php');
4
5 /**
6  * @class MySQL database class
7  *
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.
12  *
13  */
14
15 class dba {
16
17         private $debug = 0;
18         private $db;
19         private $result;
20         private $driver;
21         public  $connected = false;
22         public  $error = false;
23         private $_server_info = '';
24         private static $dbo;
25
26         function __construct($server, $user, $pass, $db, $install = false) {
27                 $a = get_app();
28
29                 $stamp1 = microtime(true);
30
31                 $server = trim($server);
32                 $user = trim($user);
33                 $pass = trim($pass);
34                 $db = trim($db);
35
36                 if (!(strlen($server) && strlen($user))) {
37                         $this->connected = false;
38                         $this->db = null;
39                         return;
40                 }
41
42                 if ($install) {
43                         if (strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
44                                 if (! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) {
45                                         $this->error = sprintf(t('Cannot locate DNS info for database server \'%s\''), $server);
46                                         $this->connected = false;
47                                         $this->db = null;
48                                         return;
49                                 }
50                         }
51                 }
52
53                 if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) {
54                         $this->driver = 'pdo';
55                         $connect = "mysql:host=".$server.";dbname=".$db;
56                         if (isset($a->config["system"]["db_charset"])) {
57                                 $connect .= ";charset=".$a->config["system"]["db_charset"];
58                         }
59                         $this->db = @new PDO($connect, $user, $pass);
60                         if (!$this->db->errorCode()) {
61                                 $this->connected = true;
62                         }
63                 } elseif (class_exists('mysqli')) {
64                         $this->driver = 'mysqli';
65                         $this->db = @new mysqli($server,$user,$pass,$db);
66                         if (!mysqli_connect_errno()) {
67                                 $this->connected = true;
68
69                                 if (isset($a->config["system"]["db_charset"])) {
70                                         $this->db->set_charset($a->config["system"]["db_charset"]);
71                                 }
72                         }
73                 } elseif (function_exists('mysql_connect')) {
74                         $this->driver = 'mysql';
75                         $this->db = mysql_connect($server,$user,$pass);
76                         if ($this->db && mysql_select_db($db,$this->db)) {
77                                 $this->connected = true;
78
79                                 if (isset($a->config["system"]["db_charset"])) {
80                                         mysql_set_charset($a->config["system"]["db_charset"], $this->db);
81                                 }
82                         }
83                 } else {
84                         // No suitable SQL driver was found.
85                         if (!$install) {
86                                 system_unavailable();
87                         }
88                 }
89
90                 if (!$this->connected) {
91                         $this->db = null;
92                         if (!$install) {
93                                 system_unavailable();
94                         }
95                 }
96                 $a->save_timestamp($stamp1, "network");
97
98                 self::$dbo = $this;
99         }
100
101         /**
102          * @brief Returns the MySQL server version string
103          * 
104          * This function discriminate between the deprecated mysql API and the current
105          * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
106          *
107          * @return string
108          */
109         public function server_info() {
110                 if ($this->_server_info == '') {
111                         switch ($this->driver) {
112                                 case 'pdo':
113                                         $this->_server_info = $this->db->getAttribute(PDO::ATTR_SERVER_VERSION);
114                                         break;
115                                 case 'mysqli':
116                                         $this->_server_info = $this->db->server_info;
117                                         break;
118                                 case 'mysql':
119                                         $this->_server_info = mysql_get_server_info($this->db);
120                                         break;
121                         }
122                 }
123                 return $this->_server_info;
124         }
125
126         /**
127          * @brief Returns the selected database name
128          *
129          * @return string
130          */
131         public function database_name() {
132                 $r = $this->q("SELECT DATABASE() AS `db`");
133
134                 return $r[0]['db'];
135         }
136
137         /**
138          * @brief Returns the number of rows
139          *
140          * @return integer
141          */
142         public function num_rows() {
143                 if (!$this->result) {
144                         return 0;
145                 }
146
147                 switch ($this->driver) {
148                         case 'pdo':
149                                 $rows = $this->result->rowCount();
150                                 break;
151                         case 'mysqli':
152                                 $rows = $this->result->num_rows;
153                                 break;
154                         case 'mysql':
155                                 $rows = mysql_num_rows($this->result);
156                                 break;
157                 }
158                 return $rows;
159         }
160
161         /**
162          * @brief Analyze a database query and log this if some conditions are met.
163          *
164          * @param string $query The database query that will be analyzed
165          */
166         public function log_index($query) {
167                 $a = get_app();
168
169                 if ($a->config["system"]["db_log_index"] == "") {
170                         return;
171                 }
172
173                 // Don't explain an explain statement
174                 if (strtolower(substr($query, 0, 7)) == "explain") {
175                         return;
176                 }
177
178                 // Only do the explain on "select", "update" and "delete"
179                 if (!in_array(strtolower(substr($query, 0, 6)), array("select", "update", "delete"))) {
180                         return;
181                 }
182
183                 $r = $this->q("EXPLAIN ".$query);
184                 if (!dbm::is_result($r)) {
185                         return;
186                 }
187
188                 $watchlist = explode(',', $a->config["system"]["db_log_index_watch"]);
189                 $blacklist = explode(',', $a->config["system"]["db_log_index_blacklist"]);
190
191                 foreach ($r AS $row) {
192                         if ((intval($a->config["system"]["db_loglimit_index"]) > 0)) {
193                                 $log = (in_array($row['key'], $watchlist) AND
194                                         ($row['rows'] >= intval($a->config["system"]["db_loglimit_index"])));
195                         } else {
196                                 $log = false;
197                         }
198
199                         if ((intval($a->config["system"]["db_loglimit_index_high"]) > 0) AND ($row['rows'] >= intval($a->config["system"]["db_loglimit_index_high"]))) {
200                                 $log = true;
201                         }
202
203                         if (in_array($row['key'], $blacklist) OR ($row['key'] == "")) {
204                                 $log = false;
205                         }
206
207                         if ($log) {
208                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
209                                 @file_put_contents($a->config["system"]["db_log_index"], datetime_convert()."\t".
210                                                 $row['key']."\t".$row['rows']."\t".$row['Extra']."\t".
211                                                 basename($backtrace[1]["file"])."\t".
212                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
213                                                 substr($query, 0, 2000)."\n", FILE_APPEND);
214                         }
215                 }
216         }
217
218         public function q($sql, $onlyquery = false) {
219                 $a = get_app();
220
221                 if (!$this->db || !$this->connected) {
222                         return false;
223                 }
224
225                 $this->error = '';
226
227                 $connstr = ($this->connected() ? "Connected" : "Disonnected");
228
229                 $stamp1 = microtime(true);
230
231                 $orig_sql = $sql;
232
233                 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
234                         $sql = "/*".$a->callstack()." */ ".$sql;
235                 }
236
237                 $columns = 0;
238
239                 switch ($this->driver) {
240                         case 'pdo':
241                                 $result = @$this->db->query($sql);
242                                 // Is used to separate between queries that returning data - or not
243                                 if (!is_bool($result)) {
244                                         $columns = $result->columnCount();
245                                 }
246                                 break;
247                         case 'mysqli':
248                                 $result = @$this->db->query($sql);
249                                 break;
250                         case 'mysql':
251                                 $result = @mysql_query($sql,$this->db);
252                                 break;
253                 }
254                 $stamp2 = microtime(true);
255                 $duration = (float)($stamp2 - $stamp1);
256
257                 $a->save_timestamp($stamp1, "database");
258
259                 if (strtolower(substr($orig_sql, 0, 6)) != "select") {
260                         $a->save_timestamp($stamp1, "database_write");
261                 }
262                 if (x($a->config,'system') && x($a->config['system'],'db_log')) {
263                         if (($duration > $a->config["system"]["db_loglimit"])) {
264                                 $duration = round($duration, 3);
265                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
266                                 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
267                                                 basename($backtrace[1]["file"])."\t".
268                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
269                                                 substr($sql, 0, 2000)."\n", FILE_APPEND);
270                         }
271                 }
272
273                 switch ($this->driver) {
274                         case 'pdo':
275                                 $errorInfo = $this->db->errorInfo();
276                                 if ($errorInfo) {
277                                         $this->error = $errorInfo[2];
278                                         $this->errorno = $errorInfo[1];
279                                 }
280                                 break;
281                         case 'mysqli':
282                                 if ($this->db->errno) {
283                                         $this->error = $this->db->error;
284                                         $this->errorno = $this->db->errno;
285                                 }
286                                 break;
287                         case 'mysql':
288                                 if (mysql_errno($this->db)) {
289                                         $this->error = mysql_error($this->db);
290                                         $this->errorno = mysql_errno($this->db);
291                                 }
292                                 break;
293                 }
294                 if (strlen($this->error)) {
295                         logger('DB Error ('.$connstr.') '.$this->errorno.': '.$this->error);
296                 }
297
298                 if ($this->debug) {
299
300                         $mesg = '';
301
302                         if ($result === false) {
303                                 $mesg = 'false';
304                         } elseif ($result === true) {
305                                 $mesg = 'true';
306                         } else {
307                                 switch ($this->driver) {
308                                         case 'pdo':
309                                                 $mesg = $result->rowCount().' results'.EOL;
310                                                 break;
311                                         case 'mysqli':
312                                                 $mesg = $result->num_rows.' results'.EOL;
313                                                 break;
314                                         case 'mysql':
315                                                 $mesg = mysql_num_rows($result).' results'.EOL;
316                                                 break;
317                                 }
318                         }
319
320                         $str =  'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
321                                 . (($this->error) ? ' error: ' . $this->error : '')
322                                 . EOL;
323
324                         logger('dba: ' . $str );
325                 }
326
327                 /**
328                  * If dbfail.out exists, we will write any failed calls directly to it,
329                  * regardless of any logging that may or may nor be in effect.
330                  * These usually indicate SQL syntax errors that need to be resolved.
331                  */
332
333                 if ($result === false) {
334                         logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
335                         if (file_exists('dbfail.out')) {
336                                 file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
337                         }
338                 }
339
340                 if (is_bool($result)) {
341                         return $result;
342                 }
343                 if ($onlyquery) {
344                         $this->result = $result;
345                         return true;
346                 }
347
348                 $r = array();
349                 switch ($this->driver) {
350                         case 'pdo':
351                                 while ($x = $result->fetch(PDO::FETCH_ASSOC)) {
352                                         $r[] = $x;
353                                 }
354                                 $result->closeCursor();
355                                 break;
356                         case 'mysqli':
357                                 while ($x = $result->fetch_array(MYSQLI_ASSOC)) {
358                                         $r[] = $x;
359                                 }
360                                 $result->free_result();
361                                 break;
362                         case 'mysql':
363                                 while ($x = mysql_fetch_array($result, MYSQL_ASSOC)) {
364                                         $r[] = $x;
365                                 }
366                                 mysql_free_result($result);
367                                 break;
368                 }
369
370                 // PDO doesn't return "true" on successful operations - like mysqli does
371                 // Emulate this behaviour by checking if the query returned data and had columns
372                 // This should be reliable enough
373                 if (($this->driver == 'pdo') AND (count($r) == 0) AND ($columns == 0)) {
374                         return true;
375                 }
376
377                 //$a->save_timestamp($stamp1, "database");
378
379                 if ($this->debug) {
380                         logger('dba: ' . printable(print_r($r, true)));
381                 }
382                 return($r);
383         }
384
385         public function qfetch() {
386                 $x = false;
387
388                 if ($this->result) {
389                         switch ($this->driver) {
390                                 case 'pdo':
391                                         $x = $this->result->fetch(PDO::FETCH_ASSOC);
392                                         break;
393                                 case 'mysqli':
394                                         $x = $this->result->fetch_array(MYSQLI_ASSOC);
395                                         break;
396                                 case 'mysql':
397                                         $x = mysql_fetch_array($this->result, MYSQL_ASSOC);
398                                         break;
399                         }
400                 }
401                 return($x);
402         }
403
404         public function qclose() {
405                 if ($this->result) {
406                         switch ($this->driver) {
407                                 case 'pdo':
408                                         $this->result->closeCursor();
409                                         break;
410                                 case 'mysqli':
411                                         $this->result->free_result();
412                                         break;
413                                 case 'mysql':
414                                         mysql_free_result($this->result);
415                                         break;
416                         }
417                 }
418         }
419
420         public function dbg($dbg) {
421                 $this->debug = $dbg;
422         }
423
424         public function escape($str) {
425                 if ($this->db && $this->connected) {
426                         switch ($this->driver) {
427                                 case 'pdo':
428                                         return substr(@$this->db->quote($str, PDO::PARAM_STR), 1, -1);
429                                 case 'mysqli':
430                                         return @$this->db->real_escape_string($str);
431                                 case 'mysql':
432                                         return @mysql_real_escape_string($str,$this->db);
433                         }
434                 }
435         }
436
437         function connected() {
438                 switch ($this->driver) {
439                         case 'pdo':
440                                 // Not sure if this really is working like expected
441                                 $connected = ($this->db->getAttribute(PDO::ATTR_CONNECTION_STATUS) != "");
442                                 break;
443                         case 'mysqli':
444                                 $connected = $this->db->ping();
445                                 break;
446                         case 'mysql':
447                                 $connected = mysql_ping($this->db);
448                                 break;
449                 }
450                 return $connected;
451         }
452
453         function insert_id() {
454                 switch ($this->driver) {
455                         case 'pdo':
456                                 $id = $this->db->lastInsertId();
457                                 break;
458                         case 'mysqli':
459                                 $id = $this->db->insert_id;
460                                 break;
461                         case 'mysql':
462                                 $id = mysql_insert_id($this->db);
463                                 break;
464                 }
465                 return $id;
466         }
467
468         function __destruct() {
469                 if ($this->db) {
470                         switch ($this->driver) {
471                                 case 'pdo':
472                                         $this->db = null;
473                                         break;
474                                 case 'mysqli':
475                                         $this->db->close();
476                                         break;
477                                 case 'mysql':
478                                         mysql_close($this->db);
479                                         break;
480                         }
481                 }
482         }
483
484         /**
485          * @brief Replaces ANY_VALUE() function by MIN() function,
486          *  if the database server does not support ANY_VALUE().
487          *
488          * Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
489          * ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
490          * A standard fall-back is to use MIN().
491          *
492          * @param string $sql An SQL string without the values
493          * @return string The input SQL string modified if necessary.
494          */
495         public function any_value_fallback($sql) {
496                 $server_info = $this->server_info();
497                 if (version_compare($server_info, '5.7.5', '<') ||
498                         (stripos($server_info, 'MariaDB') !== false)) {
499                         $sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
500                 }
501                 return $sql;
502         }
503
504         /**
505          * @brief Executes a prepared statement
506          *
507          * @param string $sql SQL statement
508          * @return object statement object
509          */
510         static public function p($sql) {
511                 $a = get_app();
512
513                 $stamp1 = microtime(true);
514
515                 $args = func_get_args();
516                 unset($args[0]);
517
518                 if (!self::$dbo OR !self::$dbo->connected) {
519                         return false;
520                 }
521
522                 $sql = self::$dbo->any_value_fallback($sql);
523
524                 $orig_sql = $sql;
525
526                 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
527                         $sql = "/*".$a->callstack()." */ ".$sql;
528                 }
529
530                 switch (self::$dbo->driver) {
531                         case 'pdo':
532                                 if (!$stmt = self::$dbo->db->prepare($sql)) {
533                                         $errorInfo = self::$dbo->db->errorInfo();
534                                         self::$dbo->error = $errorInfo[2];
535                                         self::$dbo->errorno = $errorInfo[1];
536                                         $retval = false;
537                                         break;
538                                 }
539
540                                 foreach ($args AS $param => $value) {
541                                         $stmt->bindParam($param, $args[$param]);
542                                 }
543
544                                 if (!$stmt->execute()) {
545                                         $errorInfo = self::$dbo->db->errorInfo();
546                                         self::$dbo->error = $errorInfo[2];
547                                         self::$dbo->errorno = $errorInfo[1];
548                                         $retval = false;
549                                 } else {
550                                         $retval = $stmt;
551                                 }
552                                 break;
553                         case 'mysqli':
554                                 $stmt = self::$dbo->db->stmt_init();
555
556                                 if (!$stmt->prepare($sql)) {
557                                         self::$dbo->error = self::$dbo->db->error;
558                                         self::$dbo->errorno = self::$dbo->db->errno;
559                                         $retval = false;
560                                         break;
561                                 }
562
563                                 $params = '';
564                                 $values = array();
565                                 foreach ($args AS $param => $value) {
566                                         if (is_int($args[$param])) {
567                                                 $params .= 'i';
568                                         } elseif (is_float($args[$param])) {
569                                                 $params .= 'd';
570                                         } elseif (is_string($args[$param])) {
571                                                 $params .= 's';
572                                         } else {
573                                                 $params .= 'b';
574                                         }
575                                         $values[] = &$args[$param];
576                                 }
577
578                                 array_unshift($values, $params);
579
580                                 call_user_func_array(array($stmt, 'bind_param'), $values);
581
582                                 if (!$stmt->execute()) {
583                                         self::$dbo->error = self::$dbo->db->error;
584                                         self::$dbo->errorno = self::$dbo->db->errno;
585                                         $retval = false;
586                                 } elseif (method_exists($stmt, 'get_result')) {
587                                         // Is mysqlnd installed?
588                                         $retval = $stmt->get_result();
589                                 } else {
590                                         $retval = $stmt;
591                                 }
592                                 break;
593                         case 'mysql':
594                                 // For the old "mysql" functions we cannot use prepared statements
595                                 foreach ($args AS $param => $value) {
596                                         if (is_int($args[$param]) OR is_float($args[$param])) {
597                                                 $replace = intval($args[$param]);
598                                         } else {
599                                                 $replace = "'".dbesc($args[$param])."'";
600                                         }
601
602                                         $pos = strpos($sql, '?');
603                                         if ($pos !== false) {
604                                                 $sql = substr_replace($sql, $replace, $pos, 1);
605                                         }
606                                 }
607
608                                 $retval = mysql_query($sql, self::$dbo->db);
609                                 if (mysql_errno(self::$dbo->db)) {
610                                         self::$dbo->error = mysql_error(self::$dbo->db);
611                                         self::$dbo->errorno = mysql_errno(self::$dbo->db);
612                                 }
613                                 break;
614                 }
615
616                 $a->save_timestamp($stamp1, 'database');
617
618                 if (x($a->config,'system') && x($a->config['system'], 'db_log')) {
619
620                         $stamp2 = microtime(true);
621                         $duration = (float)($stamp2 - $stamp1);
622
623                         if (($duration > $a->config["system"]["db_loglimit"])) {
624                                 $duration = round($duration, 3);
625                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
626                                 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
627                                                 basename($backtrace[1]["file"])."\t".
628                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
629                                                 substr($sql, 0, 2000)."\n", FILE_APPEND);
630                         }
631                 }
632                 return $retval;
633         }
634
635         /**
636          * @brief Executes a prepared statement
637          *
638          * @param string $sql SQL statement
639          * @return boolean Was the query successfull?
640          */
641         static public function e($sql) {
642                 $a = get_app();
643
644                 $stamp = microtime(true);
645
646                 $args = func_get_args();
647
648                 $stmt = call_user_func_array('self::p', $args);
649
650                 if (is_bool($stmt)) {
651                         $retval = $stmt;
652                 } elseif (is_object($stmt)) {
653                         $retval = true;
654                 } else {
655                         $retval = false;
656                 }
657
658                 self::close($stmt);
659
660                 $a->save_timestamp($stamp, "database_write");
661
662                 return $retval;
663         }
664
665         /**
666          * @brief Check if data exists
667          *
668          * @param string $sql SQL statement
669          * @return boolean Are there rows for that query?
670          */
671         static public function exists($sql) {
672                 $args = func_get_args();
673
674                 $stmt = call_user_func_array('self::p', $args);
675
676                 if (is_bool($stmt)) {
677                         $retval = $stmt;
678                 } else {
679                         $retval = is_array(self::fetch($stmt));
680                 }
681
682                 self::close($stmt);
683
684                 return $retval;
685         }
686
687         /**
688          * @brief Fetch a single row
689          *
690          * @param object $stmt statement object
691          * @return array current row
692          */
693         static public function fetch($stmt) {
694                 if (!is_object($stmt)) {
695                         return false;
696                 }
697
698                 switch (self::$dbo->driver) {
699                         case 'pdo':
700                                 return $stmt->fetch(PDO::FETCH_ASSOC);
701                         case 'mysqli':
702                                 // When mysqlnd is installed, we can use a shortcut
703                                 if (method_exists($stmt, 'fetch_array')) {
704                                         return $stmt->fetch_array(MYSQLI_ASSOC);
705                                 }
706
707                                 // This code works, but is slow
708
709                                 // Bind the result to a result array
710                                 $cols = array();
711
712                                 $cols_num = array();
713                                 for ($x = 0; $x < $stmt->field_count; $x++) {
714                                         $cols[] = &$cols_num[$x];
715                                 }
716
717                                 call_user_func_array(array($stmt, 'bind_result'), $cols);
718
719                                 $success = $stmt->fetch();
720
721                                 if (!$success) {
722                                         return false;
723                                 }
724
725                                 // The slow part:
726                                 // We need to get the field names for the array keys
727                                 // It seems that there is no better way to do this.
728                                 $result = $stmt->result_metadata();
729
730                                 $columns = array();
731                                 foreach ($cols_num AS $col) {
732                                         $field = $result->fetch_field();
733                                         $columns[$field->name] = $col;
734                                 }
735                                 return $columns;
736                         case 'mysql':
737                                 return mysql_fetch_array(self::$dbo->result, MYSQL_ASSOC);
738                 }
739         }
740
741         /**
742          * @brief Closes the current statement
743          *
744          * @param object $stmt statement object
745          * @return boolean was the close successfull?
746          */
747         static public function close($stmt) {
748                 if (!is_object($stmt)) {
749                         return false;
750                 }
751
752                 switch (self::$dbo->driver) {
753                         case 'pdo':
754                                 return $stmt->closeCursor();
755                         case 'mysqli':
756                                 return $stmt->close();
757                         case 'mysql':
758                                 return mysql_free_result($stmt);
759                 }
760         }
761 }
762
763 function printable($s) {
764         $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
765         $s = str_replace("\x00",'.',$s);
766         if (x($_SERVER,'SERVER_NAME')) {
767                 $s = escape_tags($s);
768         }
769         return $s;
770 }
771
772 // Procedural functions
773 function dbg($state) {
774         global $db;
775
776         if ($db) {
777                 $db->dbg($state);
778         }
779 }
780
781 function dbesc($str) {
782         global $db;
783
784         if ($db && $db->connected) {
785                 return($db->escape($str));
786         } else {
787                 return(str_replace("'","\\'",$str));
788         }
789 }
790
791 // Function: q($sql,$args);
792 // Description: execute SQL query with printf style args.
793 // Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
794 //                   'user', 1);
795 function q($sql) {
796         global $db;
797         $args = func_get_args();
798         unset($args[0]);
799
800         if ($db && $db->connected) {
801                 $sql = $db->any_value_fallback($sql);
802                 $stmt = @vsprintf($sql,$args); // Disabled warnings
803                 //logger("dba: q: $stmt", LOGGER_ALL);
804                 if ($stmt === false)
805                         logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
806
807                 $db->log_index($stmt);
808
809                 return $db->q($stmt);
810         }
811
812         /**
813          *
814          * This will happen occasionally trying to store the
815          * session data after abnormal program termination
816          *
817          */
818         logger('dba: no database: ' . print_r($args,true));
819         return false;
820 }
821
822 /**
823  * @brief Performs a query with "dirty reads"
824  *
825  * By doing dirty reads (reading uncommitted data) no locks are performed
826  * This function can be used to fetch data that doesn't need to be reliable.
827  *
828  * @param $args Query parameters (1 to N parameters of different types)
829  * @return array Query array
830  */
831 function qu($sql) {
832         global $db;
833
834         $args = func_get_args();
835         unset($args[0]);
836
837         if ($db && $db->connected) {
838                 $sql = $db->any_value_fallback($sql);
839                 $stmt = @vsprintf($sql,$args); // Disabled warnings
840                 if ($stmt === false)
841                         logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
842
843                 $db->log_index($stmt);
844
845                 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
846                 $retval = $db->q($stmt);
847                 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
848                 return $retval;
849         }
850
851         /**
852          *
853          * This will happen occasionally trying to store the
854          * session data after abnormal program termination
855          *
856          */
857         logger('dba: no database: ' . print_r($args,true));
858         return false;
859 }
860
861 /**
862  *
863  * Raw db query, no arguments
864  *
865  */
866 function dbq($sql) {
867         global $db;
868
869         if ($db && $db->connected) {
870                 $ret = $db->q($sql);
871         } else {
872                 $ret = false;
873         }
874         return $ret;
875 }
876
877 // Caller is responsible for ensuring that any integer arguments to
878 // dbesc_array are actually integers and not malformed strings containing
879 // SQL injection vectors. All integer array elements should be specifically
880 // cast to int to avoid trouble.
881 function dbesc_array_cb(&$item, $key) {
882         if (is_string($item))
883                 $item = dbesc($item);
884 }
885
886 function dbesc_array(&$arr) {
887         if (is_array($arr) && count($arr)) {
888                 array_walk($arr,'dbesc_array_cb');
889         }
890 }
891
892 function dba_timer() {
893         return microtime(true);
894 }