1<?php 2// dbl.php -- database interface layer 3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE. 4 5class Dbl_Result { 6 public $affected_rows; 7 public $insert_id; 8 public $warning_count; 9 10 function __construct(mysqli $dblink) { 11 $this->affected_rows = $dblink->affected_rows; 12 $this->insert_id = $dblink->insert_id; 13 $this->warning_count = $dblink->warning_count; 14 } 15} 16 17class Dbl_MultiResult { 18 private $dblink; 19 private $flags; 20 private $qstr; 21 private $more; 22 23 function __construct(mysqli $dblink, $flags, $qstr, $result) { 24 $this->dblink = $dblink; 25 $this->flags = $flags; 26 $this->qstr = $qstr; 27 $this->more = $result; 28 } 29 function next() { 30 // XXX does processing stop at first error? 31 if ($this->more === null) 32 $this->more = $this->dblink->more_results() ? $this->dblink->next_result() : -1; 33 if ($this->more === -1) 34 return false; 35 else if ($this->more) { 36 $result = $this->dblink->store_result(); 37 $this->more = null; 38 } else 39 $result = false; 40 return Dbl::do_result($this->dblink, $this->flags, $this->qstr, $result); 41 } 42 function free_all() { 43 while (($result = $this->next())) 44 Dbl::free($result); 45 } 46} 47 48class Dbl { 49 const F_RAW = 1; 50 const F_APPLY = 2; 51 const F_LOG = 4; 52 const F_ERROR = 8; 53 const F_ALLOWERROR = 16; 54 const F_MULTI = 32; 55 const F_ECHO = 64; 56 const F_NOEXEC = 128; 57 58 static public $nerrors = 0; 59 static public $default_dblink; 60 static private $error_handler = "Dbl::default_error_handler"; 61 static private $query_log = false; 62 static private $query_log_key = false; 63 static private $query_log_file = null; 64 static public $check_warnings = true; 65 static public $landmark_sanitizer = "/^Dbl::/"; 66 67 static function has_error() { 68 return self::$nerrors > 0; 69 } 70 71 static function make_dsn($opt) { 72 if (isset($opt["dsn"])) { 73 if (is_string($opt["dsn"])) 74 return $opt["dsn"]; 75 } else { 76 list($user, $password, $host, $name) = 77 [get($opt, "dbUser"), get($opt, "dbPassword"), get($opt, "dbHost"), get($opt, "dbName")]; 78 $user = ($user !== null ? $user : $name); 79 $password = ($password !== null ? $password : $name); 80 $host = ($host !== null ? $host : "localhost"); 81 if (is_string($user) && is_string($password) && is_string($host) && is_string($name)) 82 return "mysql://" . urlencode($user) . ":" . urlencode($password) . "@" . urlencode($host) . "/" . urlencode($name); 83 } 84 return null; 85 } 86 87 static function sanitize_dsn($dsn) { 88 return preg_replace('{\A(\w+://[^/:]*:)[^\@/]+([\@/])}', '$1PASSWORD$2', $dsn); 89 } 90 91 static function connect_dsn($dsn) { 92 global $Opt; 93 94 $dbhost = $dbuser = $dbpass = $dbname = $dbport = null; 95 if ($dsn && preg_match('|^mysql://([^:@/]*)/(.*)|', $dsn, $m)) { 96 $dbhost = urldecode($m[1]); 97 $dbname = urldecode($m[2]); 98 } else if ($dsn && preg_match('|^mysql://([^:@/]*)@([^/]*)/(.*)|', $dsn, $m)) { 99 $dbhost = urldecode($m[2]); 100 $dbuser = urldecode($m[1]); 101 $dbname = urldecode($m[3]); 102 } else if ($dsn && preg_match('|^mysql://([^:@/]*):([^@/]*)@([^/]*)/(.*)|', $dsn, $m)) { 103 $dbhost = urldecode($m[3]); 104 $dbuser = urldecode($m[1]); 105 $dbpass = urldecode($m[2]); 106 $dbname = urldecode($m[4]); 107 } 108 if (!$dbname || $dbname === "mysql" || substr($dbname, -7) === "_schema") 109 return array(null, null); 110 111 $dbsock = get($Opt, "dbSocket"); 112 if ($dbsock && $dbport === null) 113 $dbport = ini_get("mysqli.default_port"); 114 if ($dbpass === null) 115 $dbpass = ini_get("mysqli.default_pw"); 116 if ($dbuser === null) 117 $dbuser = ini_get("mysqli.default_user"); 118 if ($dbhost === null) 119 $dbhost = ini_get("mysqli.default_host"); 120 121 if ($dbsock) 122 $dblink = new mysqli($dbhost, $dbuser, $dbpass, "", $dbport, $dbsock); 123 else if ($dbport !== null) 124 $dblink = new mysqli($dbhost, $dbuser, $dbpass, "", $dbport); 125 else 126 $dblink = new mysqli($dbhost, $dbuser, $dbpass); 127 128 if ($dblink && !mysqli_connect_errno() && $dblink->select_db($dbname)) { 129 // We send binary strings to MySQL, so we don't want warnings 130 // about non-UTF-8 data 131 $dblink->set_charset("binary"); 132 // The necessity of the following line is explosively terrible 133 // (the default is 1024/!?))(U#*@$%&!U 134 $dblink->query("set group_concat_max_len=4294967295"); 135 } else if ($dblink) { 136 $dblink->close(); 137 $dblink = null; 138 } 139 return array($dblink, $dbname); 140 } 141 142 static function set_default_dblink($dblink) { 143 self::$default_dblink = $dblink; 144 } 145 146 static function set_error_handler($callable) { 147 self::$error_handler = $callable ? : "Dbl::default_error_handler"; 148 } 149 150 static function landmark() { 151 return caller_landmark(1, self::$landmark_sanitizer); 152 } 153 154 static function default_error_handler($dblink, $query) { 155 trigger_error(self::landmark() . ": database error: $dblink->error in $query"); 156 } 157 158 static private function query_args($args, $flags, $log_location) { 159 $argpos = 0; 160 $dblink = self::$default_dblink; 161 if (is_object($args[0])) { 162 $argpos = 1; 163 $dblink = $args[0]; 164 } else if ($args[0] === null && count($args) > 1) 165 $argpos = 1; 166 if ((($flags & self::F_RAW) && count($args) != $argpos + 1) 167 || (($flags & self::F_APPLY) && count($args) > $argpos + 2)) 168 trigger_error(self::landmark() . ": wrong number of arguments"); 169 else if (($flags & self::F_APPLY) && isset($args[$argpos + 1]) 170 && !is_array($args[$argpos + 1])) 171 trigger_error(self::landmark() . ": argument is not array"); 172 $q = $args[$argpos]; 173 if (($flags & self::F_MULTI) && is_array($q)) 174 $q = join(";", $q); 175 if ($log_location && self::$query_log !== false) { 176 self::$query_log_key = $qx = simplify_whitespace($q); 177 if (isset(self::$query_log[$qx])) 178 ++self::$query_log[$qx][1]; 179 else 180 self::$query_log[$qx] = [0, 1, self::landmark()]; 181 } 182 if (count($args) === $argpos + 1) 183 return array($dblink, $q, array()); 184 else if ($flags & self::F_APPLY) 185 return array($dblink, $q, $args[$argpos + 1]); 186 else 187 return array($dblink, $q, array_slice($args, $argpos + 1)); 188 } 189 190 static private function format_query_args($dblink, $qstr, $argv) { 191 $original_qstr = $qstr; 192 $strpos = $argpos = 0; 193 $usedargs = []; 194 $simpleargs = true; 195 while (($strpos = strpos($qstr, "?", $strpos)) !== false) { 196 // argument name 197 $nextpos = $strpos + 1; 198 $nextch = substr($qstr, $nextpos, 1); 199 if ($nextch === "?") { 200 $qstr = substr($qstr, 0, $strpos + 1) . substr($qstr, $strpos + 2); 201 $strpos = $strpos + 1; 202 continue; 203 } else if ($nextch === "{" 204 && ($rbracepos = strpos($qstr, "}", $nextpos + 1)) !== false) { 205 $thisarg = substr($qstr, $nextpos + 1, $rbracepos - $nextpos - 1); 206 if ($thisarg === (string) (int) $thisarg) 207 --$thisarg; 208 $nextpos = $rbracepos + 1; 209 $nextch = substr($qstr, $nextpos, 1); 210 $simpleargs = false; 211 } else { 212 do { 213 $thisarg = $argpos; 214 ++$argpos; 215 } while (isset($usedargs[$thisarg])); 216 } 217 if (!array_key_exists($thisarg, $argv)) 218 trigger_error(self::landmark() . ": query '$original_qstr' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " not set"); 219 $usedargs[$thisarg] = true; 220 // argument format 221 $arg = get($argv, $thisarg); 222 if ($nextch === "e" || $nextch === "E") { 223 if ($arg === null) 224 $arg = ($nextch === "e" ? " IS NULL" : " IS NOT NULL"); 225 else if (is_int($arg) || is_float($arg)) 226 $arg = ($nextch === "e" ? "=" : "!=") . $arg; 227 else 228 $arg = ($nextch === "e" ? "='" : "!='") . $dblink->real_escape_string($arg) . "'"; 229 ++$nextpos; 230 } else if ($nextch === "a" || $nextch === "A") { 231 if ($arg === null) 232 $arg = array(); 233 else if (is_int($arg) || is_float($arg) || is_string($arg)) 234 $arg = array($arg); 235 foreach ($arg as $x) 236 if (!is_int($x) && !is_float($x)) { 237 reset($arg); 238 foreach ($arg as &$y) 239 $y = "'" . $dblink->real_escape_string($y) . "'"; 240 unset($y); 241 break; 242 } 243 if (empty($arg)) { 244 // We want `foo IN ()` and `foo NOT IN ()`. 245 // That is, we want `false` and `true`. We compromise. The 246 // statement `foo=NULL` is always NULL -- which is falsy 247 // -- even if `foo IS NULL`. The statement `foo IS NOT 248 // NULL` is true unless `foo IS NULL`. 249 $arg = ($nextch === "a" ? "=NULL" : " IS NOT NULL"); 250 } else if (count($arg) === 1) { 251 reset($arg); 252 $arg = ($nextch === "a" ? "=" : "!=") . current($arg); 253 } else 254 $arg = ($nextch === "a" ? " IN (" : " NOT IN (") . join(", ", $arg) . ")"; 255 ++$nextpos; 256 } else if ($nextch === "s") { 257 $arg = $dblink->real_escape_string($arg); 258 ++$nextpos; 259 } else if ($nextch === "l") { 260 $arg = sqlq_for_like($arg); 261 ++$nextpos; 262 if (substr($qstr, $nextpos + 1, 1) === "s") 263 ++$nextpos; 264 else 265 $arg = "'" . $arg . "'"; 266 } else if ($nextch === "v") { 267 ++$nextpos; 268 if (!is_array($arg) || empty($arg)) { 269 trigger_error(self::landmark() . ": query '$original_qstr' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " should be nonempty array"); 270 $arg = "NULL"; 271 } else { 272 $alln = -1; 273 $vs = []; 274 foreach ($arg as $x) { 275 if (!is_array($x)) 276 $x = [$x]; 277 $n = count($x); 278 if ($alln === -1) 279 $alln = $n; 280 if ($alln !== $n && $alln !== -2) { 281 trigger_error(self::landmark() . ": query '$original_qstr' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " has components of different lengths"); 282 $alln = -2; 283 } 284 foreach ($x as &$y) 285 if ($y === null) 286 $y = "NULL"; 287 else if (!is_int($y) && !is_float($y)) 288 $y = "'" . $dblink->real_escape_string($y) . "'"; 289 unset($y); 290 $vs[] = "(" . join(",", $x) . ")"; 291 } 292 $arg = join(", ", $vs); 293 } 294 } else { 295 if ($arg === null) 296 $arg = "NULL"; 297 else if (!is_int($arg) && !is_float($arg)) 298 $arg = "'" . $dblink->real_escape_string($arg) . "'"; 299 } 300 // combine 301 $suffix = substr($qstr, $nextpos); 302 $qstr = substr($qstr, 0, $strpos) . $arg . $suffix; 303 $strpos = strlen($qstr) - strlen($suffix); 304 } 305 if ($simpleargs && $argpos !== count($argv)) 306 trigger_error(self::landmark() . ": query '$original_qstr' unused arguments"); 307 return $qstr; 308 } 309 310 static function format_query(/* [$dblink,] $qstr, ... */) { 311 list($dblink, $qstr, $argv) = self::query_args(func_get_args(), 0, false); 312 return self::format_query_args($dblink, $qstr, $argv); 313 } 314 315 static function format_query_apply(/* [$dblink,] $qstr, [$argv] */) { 316 list($dblink, $qstr, $argv) = self::query_args(func_get_args(), self::F_APPLY, false); 317 return self::format_query_args($dblink, $qstr, $argv); 318 } 319 320 static private function call_query($dblink, $flags, $qfunc, $qstr) { 321 if ($flags & self::F_ECHO) 322 error_log($qstr); 323 if ($flags & self::F_NOEXEC) 324 return null; 325 if (self::$query_log_key) { 326 $time = microtime(true); 327 $result = $dblink->$qfunc($qstr); 328 self::$query_log[self::$query_log_key][0] += microtime(true) - $time; 329 self::$query_log_key = false; 330 } else 331 $result = $dblink->$qfunc($qstr); 332 return $result; 333 } 334 335 static private function do_query_with($dblink, $qstr, $argv, $flags) { 336 if (!($flags & self::F_RAW)) 337 $qstr = self::format_query_args($dblink, $qstr, $argv); 338 if (!$qstr) { 339 error_log(self::landmark() . ": empty query"); 340 return false; 341 } 342 return self::do_result($dblink, $flags, $qstr, self::call_query($dblink, $flags, "query", $qstr)); 343 } 344 345 static private function do_query($args, $flags) { 346 list($dblink, $qstr, $argv) = self::query_args($args, $flags, true); 347 return self::do_query_with($dblink, $qstr, $argv, $flags); 348 } 349 350 static function do_query_on($dblink, $args, $flags) { 351 list($ignored_dblink, $qstr, $argv) = self::query_args($args, $flags, true); 352 return self::do_query_with($dblink, $qstr, $argv, $flags); 353 } 354 355 static public function do_result($dblink, $flags, $qstr, $result) { 356 if ($result === false && $dblink->errno) { 357 if (!($flags & self::F_ALLOWERROR)) 358 ++self::$nerrors; 359 if ($flags & self::F_ERROR) 360 call_user_func(self::$error_handler, $dblink, $qstr); 361 else if ($flags & self::F_LOG) 362 error_log(self::landmark() . ": database error: " . $dblink->error . " in $qstr"); 363 } else if ($result === false || $result === true) 364 $result = new Dbl_Result($dblink); 365 if (self::$check_warnings && !($flags & self::F_ALLOWERROR) 366 && $dblink->warning_count) { 367 $wresult = $dblink->query("show warnings"); 368 while ($wresult && ($wrow = $wresult->fetch_row())) 369 error_log(self::landmark() . ": database warning: $wrow[0] ($wrow[1]) $wrow[2]"); 370 $wresult && $wresult->close(); 371 } 372 return $result; 373 } 374 375 static private function do_multi_query($args, $flags) { 376 list($dblink, $qstr, $argv) = self::query_args($args, $flags, true); 377 if (!($flags & self::F_RAW)) 378 $qstr = self::format_query_args($dblink, $qstr, $argv); 379 return new Dbl_MultiResult($dblink, $flags, $qstr, self::call_query($dblink, $flags, "multi_query", $qstr)); 380 } 381 382 static function query(/* [$dblink,] $qstr, ... */) { 383 return self::do_query(func_get_args(), 0); 384 } 385 386 static function query_raw(/* [$dblink,] $qstr */) { 387 return self::do_query(func_get_args(), self::F_RAW); 388 } 389 390 static function query_apply(/* [$dblink,] $qstr, [$argv] */) { 391 return self::do_query(func_get_args(), self::F_APPLY); 392 } 393 394 static function q(/* [$dblink,] $qstr, ... */) { 395 return self::do_query(func_get_args(), 0); 396 } 397 398 static function q_raw(/* [$dblink,] $qstr */) { 399 return self::do_query(func_get_args(), self::F_RAW); 400 } 401 402 static function q_apply(/* [$dblink,] $qstr, [$argv] */) { 403 return self::do_query(func_get_args(), self::F_APPLY); 404 } 405 406 static function qx(/* [$dblink,] $qstr, ... */) { 407 return self::do_query(func_get_args(), self::F_ALLOWERROR); 408 } 409 410 static function qx_raw(/* [$dblink,] $qstr */) { 411 return self::do_query(func_get_args(), self::F_RAW | self::F_ALLOWERROR); 412 } 413 414 static function qx_apply(/* [$dblink,] $qstr, [$argv] */) { 415 return self::do_query(func_get_args(), self::F_APPLY | self::F_ALLOWERROR); 416 } 417 418 static function ql(/* [$dblink,] $qstr, ... */) { 419 return self::do_query(func_get_args(), self::F_LOG); 420 } 421 422 static function ql_raw(/* [$dblink,] $qstr */) { 423 return self::do_query(func_get_args(), self::F_RAW | self::F_LOG); 424 } 425 426 static function ql_apply(/* [$dblink,] $qstr, [$argv] */) { 427 return self::do_query(func_get_args(), self::F_APPLY | self::F_LOG); 428 } 429 430 static function qe(/* [$dblink,] $qstr, ... */) { 431 return self::do_query(func_get_args(), self::F_ERROR); 432 } 433 434 static function qe_raw(/* [$dblink,] $qstr */) { 435 return self::do_query(func_get_args(), self::F_RAW | self::F_ERROR); 436 } 437 438 static function qe_apply(/* [$dblink,] $qstr, [$argv] */) { 439 return self::do_query(func_get_args(), self::F_APPLY | self::F_ERROR); 440 } 441 442 static function multi_q(/* [$dblink,] $qstr, ... */) { 443 return self::do_multi_query(func_get_args(), self::F_MULTI); 444 } 445 446 static function multi_q_raw(/* [$dblink,] $qstr */) { 447 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_RAW); 448 } 449 450 static function multi_q_apply(/* [$dblink,] $qstr, [$argv] */) { 451 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_APPLY); 452 } 453 454 static function multi_ql(/* [$dblink,] $qstr, ... */) { 455 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_LOG); 456 } 457 458 static function multi_ql_raw(/* [$dblink,] $qstr */) { 459 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_RAW | self::F_LOG); 460 } 461 462 static function multi_ql_apply(/* [$dblink,] $qstr, [$argv] */) { 463 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_APPLY | self::F_LOG); 464 } 465 466 static function multi_qe(/* [$dblink,] $qstr, ... */) { 467 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_ERROR); 468 } 469 470 static function multi_qe_raw(/* [$dblink,] $qstr */) { 471 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_RAW | self::F_ERROR); 472 } 473 474 static function multi_qe_apply(/* [$dblink,] $qstr, [$argv] */) { 475 return self::do_multi_query(func_get_args(), self::F_MULTI | self::F_APPLY | self::F_ERROR); 476 } 477 478 static function make_multi_query_stager($dblink, $flags) { 479 $qs = $qvs = []; 480 return function ($q, $qv = []) use ($dblink, $flags, &$qs, &$qvs) { 481 if ($q && $q !== true) { 482 $qs[] = $q; 483 $qvs = array_merge($qvs, $qv); 484 } 485 if ((!$q || $q === true || count($qs) >= 50 || count($qv) >= 1000) 486 && !empty($qs)) { 487 $mresult = Dbl::do_multi_query([$dblink, join("; ", $qs), $qvs], self::F_MULTI | self::F_APPLY | $flags); 488 $mresult->free_all(); 489 $qs = $qvs = []; 490 } 491 }; 492 } 493 494 static function make_multi_ql_stager($dblink = null) { 495 return self::make_multi_query_stager($dblink ? : self::$default_dblink, self::F_LOG); 496 } 497 498 static function make_multi_qe_stager($dblink = null) { 499 return self::make_multi_query_stager($dblink ? : self::$default_dblink, self::F_ERROR); 500 } 501 502 static function free($result) { 503 if ($result && $result instanceof mysqli_result) 504 $result->close(); 505 } 506 507 // array of all first columns 508 static private function do_make_result($args, $flags = self::F_ERROR) { 509 if (count($args) == 1 && !is_string($args[0])) 510 return $args[0]; 511 else 512 return self::do_query($args, $flags); 513 } 514 515 static function fetch_value(/* $result | [$dblink,] $query, ... */) { 516 $result = self::do_make_result(func_get_args()); 517 $x = $result ? $result->fetch_row() : null; 518 $result && $result->close(); 519 return $x ? $x[0] : null; 520 } 521 522 static function fetch_ivalue(/* $result | [$dblink,] $query, ... */) { 523 $result = self::do_make_result(func_get_args()); 524 $x = $result ? $result->fetch_row() : null; 525 $result && $result->close(); 526 return $x ? (int) $x[0] : null; 527 } 528 529 static function fetch_rows(/* $result | [$dblink,] $query, ... */) { 530 $result = self::do_make_result(func_get_args()); 531 $x = []; 532 while (($row = ($result ? $result->fetch_row() : null))) 533 $x[] = $row; 534 $result && $result->close(); 535 return $x; 536 } 537 538 static function fetch_objects(/* $result | [$dblink,] $query, ... */) { 539 $result = self::do_make_result(func_get_args()); 540 $x = []; 541 while (($row = ($result ? $result->fetch_object() : null))) 542 $x[] = $row; 543 $result && $result->close(); 544 return $x; 545 } 546 547 static function fetch_first_row(/* $result | [$dblink,] $query, ... */) { 548 $result = self::do_make_result(func_get_args()); 549 $x = $result ? $result->fetch_row() : null; 550 $result && $result->close(); 551 return $x; 552 } 553 554 static function fetch_first_object(/* $result | [$dblink,] $query, ... */) { 555 $result = self::do_make_result(func_get_args()); 556 $x = $result ? $result->fetch_object() : null; 557 $result && $result->close(); 558 return $x; 559 } 560 561 static function fetch_first_columns(/* $result | [$dblink,] $query, ... */) { 562 $result = self::do_make_result(func_get_args()); 563 $x = array(); 564 while ($result && ($row = $result->fetch_row())) 565 $x[] = $row[0]; 566 $result && $result->close(); 567 return $x; 568 } 569 570 static function fetch_map(/* $result | [$dblink,] $query, ... */) { 571 $result = self::do_make_result(func_get_args()); 572 $x = array(); 573 while ($result && ($row = $result->fetch_row())) 574 $x[$row[0]] = count($row) == 2 ? $row[1] : array_slice($row, 1); 575 $result && $result->close(); 576 return $x; 577 } 578 579 static function fetch_iimap(/* $result | [$dblink,] $query, ... */) { 580 $result = self::do_make_result(func_get_args()); 581 $x = array(); 582 while ($result && ($row = $result->fetch_row())) { 583 assert(count($row) == 2); 584 $x[(int) $row[0]] = ($row[1] === null ? null : (int) $row[1]); 585 } 586 $result && $result->close(); 587 return $x; 588 } 589 590 static function compare_and_swap($dblink, $value_query, $value_query_args, 591 $callback, $update_query, $update_query_args) { 592 while (1) { 593 $result = self::qe_apply($dblink, $value_query, $value_query_args); 594 $value = self::fetch_value($result); 595 $new_value = call_user_func($callback, $value); 596 if ($new_value === $value) 597 return $new_value; 598 $update_query_args["expected"] = $value; 599 $update_query_args["desired"] = $new_value; 600 $result = self::qe_apply($dblink, $update_query, $update_query_args); 601 if ($result->affected_rows) 602 return $new_value; 603 } 604 } 605 606 static function log_queries($limit, $file = false) { 607 if (is_float($limit)) 608 $limit = $limit >= 1 || ($limit > 0 && mt_rand() < $limit * mt_getrandmax()); 609 if (!$limit) 610 self::$query_log = false; 611 else if (self::$query_log === false) { 612 register_shutdown_function("Dbl::shutdown"); 613 self::$query_log = []; 614 self::$query_log_file = $file; 615 } 616 } 617 618 static function shutdown() { 619 if (self::$query_log) { 620 uasort(self::$query_log, function ($a, $b) { 621 return $b[0] < $a[0] ? -1 : $b[0] > $a[0]; 622 }); 623 $self = Navigation::self(); 624 $i = 1; 625 $n = count(self::$query_log); 626 $t = [0, 0]; 627 $qlog = ""; 628 foreach (self::$query_log as $where => $what) { 629 $a = [$what[0], $what[1], $what[2], $where]; 630 $qlog .= "query_log: $self #$i/$n: " . json_encode($a) . "\n"; 631 ++$i; 632 $t[0] += $what[0]; 633 $t[1] += $what[1]; 634 } 635 $qlog .= "query_log: total: " . json_encode($t) . "\n"; 636 if (self::$query_log_file) 637 @file_put_contents(self::$query_log_file, $qlog, FILE_APPEND); 638 else 639 error_log($qlog); 640 } 641 self::$query_log = false; 642 } 643 644 static function utf8(/* [$dblink,] $qstr */) { 645 $args = func_get_args(); 646 $dblink = count($args) > 1 ? $args[0] : self::$default_dblink; 647 $utf8 = $dblink->server_version >= 50503 ? "utf8mb4" : "utf8"; 648 $qstr = count($args) > 1 ? $args[1] : $args[0]; 649 return "_" . $utf8 . $qstr; 650 } 651 652 static function utf8ci(/* [$dblink,] $qstr */) { 653 $args = func_get_args(); 654 $dblink = count($args) > 1 ? $args[0] : self::$default_dblink; 655 $utf8 = $dblink->server_version >= 50503 ? "utf8mb4" : "utf8"; 656 $qstr = count($args) > 1 ? $args[1] : $args[0]; 657 return "_" . $utf8 . $qstr . " collate " . $utf8 . "_general_ci"; 658 } 659} 660 661// number of rows returned by a select query, or 'false' if result is an error 662function edb_nrows($result) { 663 return $result ? $result->num_rows : false; 664} 665 666// next row as an array, or 'false' if no more rows or result is an error 667function edb_row($result) { 668 return $result ? $result->fetch_row() : false; 669} 670 671// array of all rows as arrays 672function edb_rows($result) { 673 $x = array(); 674 while ($result && ($row = $result->fetch_row())) 675 $x[] = $row; 676 Dbl::free($result); 677 return $x; 678} 679 680// array of all first columns as arrays 681function edb_first_columns($result) { 682 $x = array(); 683 while ($result && ($row = $result->fetch_row())) 684 $x[] = $row[0]; 685 Dbl::free($result); 686 return $x; 687} 688 689// map of all rows 690function edb_map($result) { 691 $x = array(); 692 while ($result && ($row = $result->fetch_row())) 693 $x[$row[0]] = (count($row) == 2 ? $row[1] : array_slice($row, 1)); 694 Dbl::free($result); 695 return $x; 696} 697 698// next row as an object, or 'false' if no more rows or result is an error 699function edb_orow($result) { 700 return $result ? $result->fetch_object() : false; 701} 702 703// array of all rows as objects 704function edb_orows($result) { 705 $x = array(); 706 while ($result && ($row = $result->fetch_object())) 707 $x[] = $row; 708 Dbl::free($result); 709 return $x; 710} 711 712// quoting for SQL 713function sqlq($value) { 714 return Dbl::$default_dblink->escape_string($value); 715} 716 717function sqlq_for_like($value) { 718 return preg_replace("/(?=[%_\\\\'\"\\x00\\n\\r\\x1a])/", "\\", $value); 719} 720 721function sql_in_numeric_set($set) { 722 if (empty($set)) 723 return "=-1"; 724 else if (count($set) == 1) 725 return "=" . $set[0]; 726 else 727 return " in (" . join(", ", $set) . ")"; 728} 729 730function sql_not_in_numeric_set($set) { 731 $sql = sql_in_numeric_set($set); 732 return ($sql[0] === "=" ? "!" : " not") . $sql; 733} 734