FOSSology  4.4.0
Open Source License Compliance by Open Source Software
DbManager.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014 Siemens AG
4  Authors: Steffen Weber, Andreas Würl
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
8 
9 namespace Fossology\Lib\Db;
10 
12 use Monolog\Logger;
13 
14 abstract class DbManager
15 {
17  protected $dbDriver;
19  protected $preparedStatements;
21  protected $logger;
23  protected $cumulatedTime = array();
25  protected $queryCount = array();
27  private $transactionDepth = 0;
28 
29  function __construct(Logger $logger)
30  {
31  $this->preparedStatements = array();
32  $this->logger = $logger;
33  }
34 
36  public function setDriver(Driver &$dbDriver)
37  {
38  $this->dbDriver = $dbDriver;
39  }
40 
42  public function getDriver()
43  {
44  return $this->dbDriver;
45  }
46 
47  public function begin()
48  {
49  if ($this->transactionDepth==0) {
50  $this->dbDriver->begin();
51  }
52  $this->transactionDepth++;
53  }
54 
55  public function commit()
56  {
57  $this->transactionDepth--;
58  if ($this->transactionDepth==0) {
59  $this->dbDriver->commit();
60  } else if ($this->transactionDepth < 0) {
61  throw new \Exception('too much transaction commits');
62  }
63  }
64 
65  public function rollback()
66  {
67  if ($this->transactionDepth > 0) {
68  $this->transactionDepth--;
69  $this->dbDriver->rollback();
70  } else if ($this->transactionDepth == 0) {
71  throw new \Exception('too much transaction rollbacks');
72  }
73  }
74 
80  abstract public function prepare($statementName, $sqlStatement);
81 
92  public function insertPreparedAndReturn($statementName, $sqlStatement, $params, $returning)
93  {
94  $sqlStatement .= " RETURNING $returning";
95  $statementName .= ".returning:$returning";
96  $this->prepare($statementName,$sqlStatement);
97  $res = $this->execute($statementName,$params);
98  $return = $this->fetchArray($res);
99  $this->freeResult($res);
100  return $return[$returning];
101  }
102 
109  abstract public function execute($statementName, $params = array());
110 
116  protected function checkResult($result, $sqlStatement = "")
117  {
118  if ($result !== false) {
119  return;
120  }
121  $lastError = "";
122  if ($this->dbDriver->isConnected()) {
123  $lastError = $this->dbDriver->getLastError();
124  $this->logger->critical($lastError);
125  if ($this->transactionDepth>0) {
126  $this->dbDriver->rollback();
127  }
128  } else {
129  $this->logger->critical("DB connection lost.");
130  }
131 
132  $message = "error executing: $sqlStatement\n\n$lastError";
133  throw new Exception($message);
134  }
135 
142  public function getSingleRow($sqlStatement, $params = array(), $statementName = "")
143  {
144  if (empty($statementName)) {
145  $backtrace = debug_backtrace();
146  $caller = $backtrace[1];
147  $statementName = (array_key_exists('class', $caller) ? "$caller[class]::" : '') . "$caller[function]";
148  }
149  if (!array_key_exists($statementName, $this->preparedStatements)) {
150  $this->prepare($statementName, $sqlStatement);
151  }
152  $res = $this->execute($statementName, $params);
153  $row = $this->dbDriver->fetchArray($res);
154  $this->dbDriver->freeResult($res);
155  return $row;
156  }
157 
164  public function getRows($sqlStatement, $params = array(), $statementName = "")
165  {
166  if (empty($statementName)) {
167  $backtrace = debug_backtrace();
168  $caller = $backtrace[1];
169  $statementName = (array_key_exists('class', $caller) ? "$caller[class]::" : '') . "$caller[function]";
170  }
171  if (!array_key_exists($statementName, $this->preparedStatements)) {
172  $this->prepare($statementName, $sqlStatement);
173  }
174  $res = $this->execute($statementName, $params);
175  $rows = $this->dbDriver->fetchAll($res);
176  $this->dbDriver->freeResult($res);
177  return $rows;
178  }
179 
185  public function queryOnce($sqlStatement, $sqlLog = '')
186  {
187  if (empty($sqlLog)) {
188  $sqlLog = $sqlStatement;
189  }
190  $startTime = microtime($get_as_float = true);
191  $res = $this->dbDriver->query($sqlStatement);
192  $this->checkResult($res, $sqlStatement);
193  $this->freeResult($res);
194  $execTime = microtime($get_as_float = true) - $startTime;
195  $this->logger->debug("query '$sqlLog' took " . $this->formatMilliseconds($execTime));
196  }
197 
202  public function freeResult($res)
203  {
204  return $this->dbDriver->freeResult($res);
205  }
206 
211  public function fetchArray($res)
212  {
213  return $this->dbDriver->fetchArray($res);
214  }
215 
220  public function fetchAll($res)
221  {
222  return $this->dbDriver->fetchAll($res);
223  }
224 
232  public function createMap($tableName,$keyColumn,$valueColumn,$sqlLog='')
233  {
234  if (empty($sqlLog)) {
235  $sqlLog = __METHOD__ . ".$tableName.$keyColumn,$valueColumn";
236  }
237  $this->prepare($sqlLog, "select $keyColumn,$valueColumn from $tableName");
238  $res = $this->execute($sqlLog);
239  $map = array();
240  while ($row = $this->fetchArray($res)) {
241  $map[$row[$keyColumn]] = $row[$valueColumn];
242  }
243  $this->freeResult($res);
244  return $map;
245  }
246 
247  public function flushStats()
248  {
249  foreach ($this->cumulatedTime as $statementName => $seconds) {
250  $queryCount = $this->queryCount[$statementName];
251  $this->logger->debug("executing '$statementName' took "
252  . $this->formatMilliseconds($seconds)
253  . " ($queryCount queries" . ($queryCount > 0 ? ", avg " . $this->formatMilliseconds($seconds / $queryCount) : "") . ")");
254  }
255 
256  if ($this->transactionDepth != 0) {
257  throw new \Fossology\Lib\Exception("you have not committed enough");
258  }
259  }
260 
265  protected function formatMilliseconds($seconds)
266  {
267  return sprintf("%0.3fms", 1000 * $seconds);
268  }
269 
274  protected function collectStatistics($statementName, $execTime)
275  {
276  $this->cumulatedTime[$statementName] += $execTime;
277  $this->queryCount[$statementName]++;
278  }
279 
280  public function booleanFromDb($booleanValue)
281  {
282  return $this->dbDriver->booleanFromDb($booleanValue);
283  }
284 
285  public function booleanToDb($booleanValue)
286  {
287  return $this->dbDriver->booleanToDb($booleanValue);
288  }
289 
290  private function cleanupParamsArray($params)
291  {
292  $nParams = sizeof($params);
293  for ($i = 0; $i<$nParams; $i++) {
294  if (is_bool($params[$i])) {
295  $params[$i] = $this->dbDriver->booleanToDb($params[$i]);
296  }
297  }
298  return $params;
299  }
300 
309  public function insertInto($tableName, $keys, $params, $sqlLog='', $returning='')
310  {
311  if (empty($sqlLog)) {
312  $sqlLog = __METHOD__ . ".$tableName.$keys" . (empty($returning) ? "" : md5($returning));
313  }
314  $sql = "INSERT INTO $tableName ($keys) VALUES (";
315  $nKeys = substr_count($keys,',')+1;
316  for ($i = 1; $i < $nKeys; $i++) {
317  $sql .= '$'.$i.',';
318  }
319  $sql .= '$'.$nKeys.')';
320  $params = $this->cleanupParamsArray($params);
321  if (!empty($returning)) {
322  return $this->insertPreparedAndReturn($sqlLog, $sql, $params, $returning);
323  }
324  $this->prepare($sqlLog,$sql);
325  $res = $this->execute($sqlLog,$params);
326  $this->freeResult($res);
327  }
328 
336  public function insertTableRow($tableName,$assocParams,$sqlLog='',$returning='')
337  {
338  $params = array_values($assocParams);
339  $keys = implode(',',array_keys($assocParams));
340  if (empty($sqlLog)) {
341  $sqlLog = __METHOD__ . ".$tableName.$keys" . (empty($returning) ? "" : md5($returning));
342  }
343  return $this->insertInto($tableName, $keys, $params, $sqlLog, $returning);
344  }
345 
346  public function updateTableRow($tableName, $assocParams, $idColName, $id, $sqlLog='')
347  {
348  $params = array_values($assocParams);
349  $keys = array_keys($assocParams);
350  $nKeys = sizeof($keys);
351 
352  if (empty($sqlLog)) {
353  $sqlLog = __METHOD__ . ".$tableName." . implode(",", $keys);
354  }
355 
356  $sql = "UPDATE $tableName SET";
357  for ($i = 1; $i < $nKeys; $i++) {
358  $sql .= " ".$keys[$i - 1].' = $'.$i.",";
359  }
360  $sql .= " ".$keys[$nKeys - 1].' = $'.$nKeys;
361  $sql .= " WHERE $idColName = \$".($nKeys + 1);
362 
363  $params[] = $id;
364  $params = $this->cleanupParamsArray($params);
365 
366  $this->prepare($sqlLog,$sql);
367  $res = $this->execute($sqlLog,$params);
368  $this->freeResult($res);
369  }
370 
376  public function existsTable($tableName)
377  {
378  if (! preg_match('/^[a-z0-9_]+$/i',$tableName)) {
379  throw new \Exception("invalid table name '$tableName'");
380  }
381  return $this->dbDriver->existsTable($tableName);
382  }
383 
390  public function existsColumn($tableName, $columnName)
391  {
392  if (! preg_match('/^[a-z0-9_]+$/i',$columnName)) {
393  throw new \Exception("invalid column name '$columnName'");
394  }
395  return $this->existsTable($tableName) && $this->dbDriver->existsColumn($tableName, $columnName);
396  }
397 }
execute($statementName, $params=array())
insertPreparedAndReturn($statementName, $sqlStatement, $params, $returning)
Definition: DbManager.php:92
getRows($sqlStatement, $params=array(), $statementName="")
Definition: DbManager.php:164
createMap($tableName, $keyColumn, $valueColumn, $sqlLog='')
Definition: DbManager.php:232
checkResult($result, $sqlStatement="")
Check the result for unexpected errors. If found, treat them as fatal.
Definition: DbManager.php:116
existsColumn($tableName, $columnName)
Definition: DbManager.php:390
queryOnce($sqlStatement, $sqlLog='')
Definition: DbManager.php:185
collectStatistics($statementName, $execTime)
Definition: DbManager.php:274
getSingleRow($sqlStatement, $params=array(), $statementName="")
Definition: DbManager.php:142
insertTableRow($tableName, $assocParams, $sqlLog='', $returning='')
Definition: DbManager.php:336
prepare($statementName, $sqlStatement)
setDriver(Driver &$dbDriver)
Definition: DbManager.php:36
insertInto($tableName, $keys, $params, $sqlLog='', $returning='')
Definition: DbManager.php:309
Fossology exception.
Definition: Exception.php:15