FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
DbManager.php
1 <?php
2 /*
3 Copyright (C) 2014, Siemens AG
4 Authors: Steffen Weber, Andreas Würl
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 version 2 as published by the Free Software Foundation.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19 
20 namespace Fossology\Lib\Db;
21 
23 use Monolog\Logger;
24 
25 abstract class DbManager
26 {
28  protected $dbDriver;
30  protected $preparedStatements;
32  protected $logger;
34  protected $cumulatedTime = array();
36  protected $queryCount = array();
38  private $transactionDepth = 0;
39 
40  function __construct(Logger $logger)
41  {
42  $this->preparedStatements = array();
43  $this->logger = $logger;
44  }
45 
47  public function setDriver(Driver &$dbDriver)
48  {
49  $this->dbDriver = $dbDriver;
50  }
51 
53  public function getDriver()
54  {
55  return $this->dbDriver;
56  }
57 
58  public function begin()
59  {
60  if ($this->transactionDepth==0) {
61  $this->dbDriver->begin();
62  }
63  $this->transactionDepth++;
64  }
65 
66  public function commit()
67  {
68  $this->transactionDepth--;
69  if ($this->transactionDepth==0) {
70  $this->dbDriver->commit();
71  } else if ($this->transactionDepth < 0) {
72  throw new \Exception('too much transaction commits');
73  }
74  }
75 
76  public function rollback()
77  {
78  if ($this->transactionDepth > 0) {
79  $this->transactionDepth--;
80  $this->dbDriver->rollback();
81  } else if ($this->transactionDepth == 0) {
82  throw new \Exception('too much transaction rollbacks');
83  }
84  }
85 
91  abstract public function prepare($statementName, $sqlStatement);
92 
103  public function insertPreparedAndReturn($statementName, $sqlStatement, $params, $returning)
104  {
105  $sqlStatement .= " RETURNING $returning";
106  $statementName .= ".returning:$returning";
107  $this->prepare($statementName,$sqlStatement);
108  $res = $this->execute($statementName,$params);
109  $return = $this->fetchArray($res);
110  $this->freeResult($res);
111  return $return[$returning];
112  }
113 
120  abstract public function execute($statementName, $params = array());
121 
127  protected function checkResult($result, $sqlStatement = "")
128  {
129  if ($result !== false) {
130  return;
131  }
132  $lastError = "";
133  if ($this->dbDriver->isConnected()) {
134  $lastError = $this->dbDriver->getLastError();
135  $this->logger->addCritical($lastError);
136  if ($this->transactionDepth>0) {
137  $this->dbDriver->rollback();
138  }
139  } else {
140  $this->logger->addCritical("DB connection lost.");
141  }
142 
143  $message = "error executing: $sqlStatement\n\n$lastError";
144  throw new Exception($message);
145  }
146 
153  public function getSingleRow($sqlStatement, $params = array(), $statementName = "")
154  {
155  if (empty($statementName)) {
156  $backtrace = debug_backtrace();
157  $caller = $backtrace[1];
158  $statementName = (array_key_exists('class', $caller) ? "$caller[class]::" : '') . "$caller[function]";
159  }
160  if (!array_key_exists($statementName, $this->preparedStatements)) {
161  $this->prepare($statementName, $sqlStatement);
162  }
163  $res = $this->execute($statementName, $params);
164  $row = $this->dbDriver->fetchArray($res);
165  $this->dbDriver->freeResult($res);
166  return $row;
167  }
168 
175  public function getRows($sqlStatement, $params = array(), $statementName = "")
176  {
177  if (empty($statementName)) {
178  $backtrace = debug_backtrace();
179  $caller = $backtrace[1];
180  $statementName = (array_key_exists('class', $caller) ? "$caller[class]::" : '') . "$caller[function]";
181  }
182  if (!array_key_exists($statementName, $this->preparedStatements)) {
183  $this->prepare($statementName, $sqlStatement);
184  }
185  $res = $this->execute($statementName, $params);
186  $rows = $this->dbDriver->fetchAll($res);
187  $this->dbDriver->freeResult($res);
188  return $rows;
189  }
190 
196  public function queryOnce($sqlStatement, $sqlLog = '')
197  {
198  if (empty($sqlLog)) {
199  $sqlLog = $sqlStatement;
200  }
201  $startTime = microtime($get_as_float = true);
202  $res = $this->dbDriver->query($sqlStatement);
203  $this->checkResult($res, $sqlStatement);
204  $this->freeResult($res);
205  $execTime = microtime($get_as_float = true) - $startTime;
206  $this->logger->addDebug("query '$sqlLog' took " . $this->formatMilliseconds($execTime));
207  }
208 
213  public function freeResult($res)
214  {
215  return $this->dbDriver->freeResult($res);
216  }
217 
222  public function fetchArray($res)
223  {
224  return $this->dbDriver->fetchArray($res);
225  }
226 
231  public function fetchAll($res)
232  {
233  return $this->dbDriver->fetchAll($res);
234  }
235 
243  public function createMap($tableName,$keyColumn,$valueColumn,$sqlLog='')
244  {
245  if (empty($sqlLog)) {
246  $sqlLog = __METHOD__ . ".$tableName.$keyColumn,$valueColumn";
247  }
248  $this->prepare($sqlLog, "select $keyColumn,$valueColumn from $tableName");
249  $res = $this->execute($sqlLog);
250  $map = array();
251  while ($row = $this->fetchArray($res)) {
252  $map[$row[$keyColumn]] = $row[$valueColumn];
253  }
254  $this->freeResult($res);
255  return $map;
256  }
257 
258  public function flushStats()
259  {
260  foreach ($this->cumulatedTime as $statementName => $seconds) {
261  $queryCount = $this->queryCount[$statementName];
262  $this->logger->addDebug("executing '$statementName' took "
263  . $this->formatMilliseconds($seconds)
264  . " ($queryCount queries" . ($queryCount > 0 ? ", avg " . $this->formatMilliseconds($seconds / $queryCount) : "") . ")");
265  }
266 
267  if ($this->transactionDepth != 0) {
268  throw new \Fossology\Lib\Exception("you have not committed enough");
269  }
270  }
271 
276  protected function formatMilliseconds($seconds)
277  {
278  return sprintf("%0.3fms", 1000 * $seconds);
279  }
280 
285  protected function collectStatistics($statementName, $execTime)
286  {
287  $this->cumulatedTime[$statementName] += $execTime;
288  $this->queryCount[$statementName]++;
289  }
290 
291  public function booleanFromDb($booleanValue)
292  {
293  return $this->dbDriver->booleanFromDb($booleanValue);
294  }
295 
296  public function booleanToDb($booleanValue)
297  {
298  return $this->dbDriver->booleanToDb($booleanValue);
299  }
300 
301  private function cleanupParamsArray($params)
302  {
303  $nParams = sizeof($params);
304  for ($i = 0; $i<$nParams; $i++) {
305  if (is_bool($params[$i])) {
306  $params[$i] = $this->dbDriver->booleanToDb($params[$i]);
307  }
308  }
309  return $params;
310  }
311 
318  public function insertInto($tableName, $keys, $params, $sqlLog='', $returning='')
319  {
320  if (empty($sqlLog)) {
321  $sqlLog = __METHOD__ . ".$tableName.$keys" . (empty($returning) ? "" : md5($returning));
322  }
323  $sql = "INSERT INTO $tableName ($keys) VALUES (";
324  $nKeys = substr_count($keys,',')+1;
325  for ($i = 1; $i < $nKeys; $i++) {
326  $sql .= '$'.$i.',';
327  }
328  $sql .= '$'.$nKeys.')';
329  $params = $this->cleanupParamsArray($params);
330  if (!empty($returning)) {
331  return $this->insertPreparedAndReturn($sqlLog, $sql, $params, $returning);
332  }
333  $this->prepare($sqlLog,$sql);
334  $res = $this->execute($sqlLog,$params);
335  $this->freeResult($res);
336  }
337 
344  public function insertTableRow($tableName,$assocParams,$sqlLog='',$returning='')
345  {
346  $params = array_values($assocParams);
347  $keys = implode(',',array_keys($assocParams));
348  if (empty($sqlLog)) {
349  $sqlLog = __METHOD__ . ".$tableName.$keys" . (empty($returning) ? "" : md5($returning));
350  }
351  return $this->insertInto($tableName, $keys, $params, $sqlLog, $returning);
352  }
353 
354  public function updateTableRow($tableName, $assocParams, $idColName, $id, $sqlLog='')
355  {
356  $params = array_values($assocParams);
357  $keys = array_keys($assocParams);
358  $nKeys = sizeof($keys);
359 
360  if (empty($sqlLog)) {
361  $sqlLog = __METHOD__ . ".$tableName.$keys";
362  }
363 
364  $sql = "UPDATE $tableName SET";
365  for ($i = 1; $i < $nKeys; $i++) {
366  $sql .= " ".$keys[$i - 1].' = $'.$i.",";
367  }
368  $sql .= " ".$keys[$nKeys - 1].' = $'.$nKeys;
369  $sql .= " WHERE $idColName = \$".($nKeys + 1);
370 
371  $params[] = $id;
372  $params = $this->cleanupParamsArray($params);
373 
374  $this->prepare($sqlLog,$sql);
375  $res = $this->execute($sqlLog,$params);
376  $this->freeResult($res);
377  }
378 
384  public function existsTable($tableName)
385  {
386  if (! preg_match('/^[a-z0-9_]+$/i',$tableName)) {
387  throw new \Exception("invalid table name '$tableName'");
388  }
389  return $this->dbDriver->existsTable($tableName);
390  }
391 
398  public function existsColumn($tableName, $columnName)
399  {
400  if (! preg_match('/^[a-z0-9_]+$/i',$columnName)) {
401  throw new \Exception("invalid column name '$columnName'");
402  }
403  return $this->existsTable($tableName) && $this->dbDriver->existsColumn($tableName, $columnName);
404  }
405 }
insertPreparedAndReturn($statementName, $sqlStatement, $params, $returning)
Definition: DbManager.php:103
createMap($tableName, $keyColumn, $valueColumn, $sqlLog='')
Definition: DbManager.php:243
prepare($statementName, $sqlStatement)
setDriver(Driver &$dbDriver)
Definition: DbManager.php:47
insertInto($tableName, $keys, $params, $sqlLog='', $returning='')
Definition: DbManager.php:318
insertTableRow($tableName, $assocParams, $sqlLog='', $returning='')
Definition: DbManager.php:344
getSingleRow($sqlStatement, $params=array(), $statementName="")
Definition: DbManager.php:153
existsColumn($tableName, $columnName)
Definition: DbManager.php:398
queryOnce($sqlStatement, $sqlLog= '')
Definition: DbManager.php:196
execute($statementName, $params=array())
Fossology exception.
Definition: Exception.php:25
checkResult($result, $sqlStatement="")
Check the result for unexpected errors. If found, treat them as fatal.
Definition: DbManager.php:127
getRows($sqlStatement, $params=array(), $statementName="")
Definition: DbManager.php:175
collectStatistics($statementName, $execTime)
Definition: DbManager.php:285