CWIS Developer Documentation
Database.php
Go to the documentation of this file.
1 <?PHP
2 
3 #
4 # Axis--Database.php
5 # A Simple SQL Database Abstraction Object
6 #
7 # Copyright 1999-2002 Axis Data
8 # This code is free software that can be used or redistributed under the
9 # terms of Version 2 of the GNU General Public License, as published by the
10 # Free Software Foundation (http://www.fsf.org).
11 #
12 # Author: Edward Almasy (almasy@axisdata.com)
13 #
14 # Part of the AxisPHP library v1.2.5
15 # For more information see http://www.axisdata.com/AxisPHP/
16 #
17 
22 class Database
23 {
24 
25  # ---- PUBLIC INTERFACE --------------------------------------------------
26  /*@(*/
28 
43  public function __construct(
44  $UserName = NULL, $Password = NULL, $DatabaseName = NULL, $HostName = NULL)
45  {
46  # save DB access parameter values
47  $this->DBUserName = $UserName ? $UserName : self::$GlobalDBUserName;
48  $this->DBPassword = $Password ? $Password : self::$GlobalDBPassword;
49  $this->DBHostName = $HostName ? $HostName :
50  (isset(self::$GlobalDBHostName) ? self::$GlobalDBHostName
51  : "localhost");
52  $this->DBName = $DatabaseName ? $DatabaseName : self::$GlobalDBName;
53 
54  # set memory threshold for cache clearing
55  if (!isset(self::$CacheMemoryThreshold))
56  {
57  self::$CacheMemoryThreshold = self::GetPhpMemoryLimit() / 4;
58  }
59 
60  # if we don't already have a connection or DB access parameters were supplied
61  $HandleIndex = $this->DBHostName.":".$this->DBName;
62  if (!array_key_exists($HandleIndex, self::$ConnectionHandles)
63  || $UserName || $Password || $DatabaseName || $HostName)
64  {
65  # open connection to DB server and select database
66  $this->Handle = self::ConnectAndSelectDB($this->DBHostName,
67  $this->DBUserName, $this->DBPassword, $this->DBName);
68  self::$ConnectionHandles[$HandleIndex] = $this->Handle;
69  }
70  else
71  {
72  # set local connection handle
73  $this->Handle = self::$ConnectionHandles[$HandleIndex];
74  }
75  }
76 
81  public function __sleep()
82  {
83  return array("DBUserName", "DBPassword", "DBHostName", "DBName");
84  }
90  public function __wakeup()
91  {
92  # if we don't already have a database server connection
93  $HandleIndex = $this->DBHostName.":".$this->DBName;
94  if (!array_key_exists($HandleIndex, self::$ConnectionHandles))
95  {
96  # open connection to DB server and select database
97  try
98  {
99  $this->Handle = self::ConnectAndSelectDB($this->DBHostName,
100  $this->DBUserName, $this->DBPassword, $this->DBName);
101  }
102  catch (Exception $Exception)
103  {
104  if (isset(self::$GlobalDBUserName)
105  && isset(self::$GlobalDBPassword)
106  && isset(self::$GlobalDBName))
107  {
108  $this->DBUserName = self::$GlobalDBUserName;
109  $this->DBPassword = self::$GlobalDBPassword;
110  $this->DBName = self::$GlobalDBName;
111  $this->DBHostName = isset(self::$GlobalDBHostName)
112  ? self::$GlobalDBHostName : "localhost";
113  $this->Handle = self::ConnectAndSelectDB($this->DBHostName,
114  $this->DBUserName, $this->DBPassword, $this->DBName);
115  }
116  else
117  {
118  throw $Exception;
119  }
120  }
121  self::$ConnectionHandles[$HandleIndex] = $this->Handle;
122  }
123  else
124  {
125  # set local connection handle
126  $this->Handle = self::$ConnectionHandles[$HandleIndex];
127  }
128  }
138  public static function SetGlobalServerInfo(
139  $UserName, $Password, $HostName = "localhost")
140  {
141  # save default DB access parameters
142  self::$GlobalDBUserName = $UserName;
143  self::$GlobalDBPassword = $Password;
144  self::$GlobalDBHostName = $HostName;
145 
146  # clear any existing DB connection handles
147  self::$ConnectionHandles = array();
148  }
149 
154  public static function SetGlobalDatabaseName($DatabaseName)
155  {
156  # save new default DB name
157  self::$GlobalDBName = $DatabaseName;
158 
159  # clear any existing DB connection handles
160  self::$ConnectionHandles = array();
161  }
162 
167  public function SetDefaultStorageEngine($Engine)
168  {
169  # choose config variable to use based on server version number
170  $ConfigVar = version_compare($this->GetServerVersion(), "5.5", "<")
171  ? "storage_engine" : "default_storage_engine";
172 
173  # set storage engine in database
174  $this->Query("SET ".$ConfigVar." = ".$Engine);
175  }
176 
183  public function GetServerVersion($FullVersion=FALSE)
184  {
185  # retrieve version string
186  $Version = $this->Query("SELECT VERSION() AS ServerVer", "ServerVer");
187 
188  if (!$FullVersion)
189  {
190  # strip off any build/config suffix
191  $Pieces = explode("-", $Version);
192  $Version = array_shift($Pieces);
193  }
194 
195  # return version number to caller
196  return $Version;
197  }
198 
207  public function GetClientVersion()
208  {
209  return mysqli_get_client_info();
210  }
211 
217  public function GetHostInfo()
218  {
219  return mysqli_get_host_info($this->Handle);
220  }
221 
227  public function DBHostName()
228  {
229  return $this->DBHostName;
230  }
231 
237  public function DBName()
238  {
239  return $this->DBName;
240  }
241 
247  public function DBUserName()
248  {
249  return $this->DBUserName;
250  }
251 
259  public static function Caching($NewSetting = NULL)
260  {
261  # if cache setting has changed
262  if (($NewSetting !== NULL) && ($NewSetting != self::$CachingFlag))
263  {
264  # save new setting
265  self::$CachingFlag = $NewSetting;
266 
267  # clear any existing cached results
268  self::$QueryResultCache = array();
269  }
270 
271  # return current setting to caller
272  return self::$CachingFlag;
273  }
274 
285  public static function AdvancedCaching($NewSetting = NULL)
286  {
287  if ($NewSetting !== NULL)
288  {
289  self::$AdvancedCachingFlag = $NewSetting;
290  }
291  return self::$AdvancedCachingFlag;
292  }
293 
313  public function SetQueryErrorsToIgnore($ErrorsToIgnore, $NormalizeWhitespace = TRUE)
314  {
315  if ($NormalizeWhitespace && ($ErrorsToIgnore !== NULL))
316  {
317  $RevisedErrorsToIgnore = array();
318  foreach ($ErrorsToIgnore as $SqlPattern => $ErrMsgPattern)
319  {
320  $SqlPattern = preg_replace("/\\s+/", "\\s+", $SqlPattern);
321  $RevisedErrorsToIgnore[$SqlPattern] = $ErrMsgPattern;
322  }
323  $ErrorsToIgnore = $RevisedErrorsToIgnore;
324  }
325  $this->ErrorsToIgnore = $ErrorsToIgnore;
326  }
327 
333  public function IgnoredError()
334  {
335  return $this->ErrorIgnored;
336  }
337 
338  /*@)*/ /* Setup/Initialization */ /*@(*/
340 
351  public function Query($QueryString, $FieldName = "")
352  {
353  # clear flag that indicates whether query error was ignored
354  $this->ErrorIgnored = FALSE;
355 
356  # if caching is enabled
357  if (self::$CachingFlag)
358  {
359  # if SQL statement is read-only
360  if ($this->IsReadOnlyStatement($QueryString))
361  {
362  # if we have statement in cache
363  if (isset(self::$QueryResultCache[$QueryString]["NumRows"]))
364  {
365  if (self::$QueryDebugOutputFlag)
366  { print("DB-C: $QueryString<br>\n"); }
367 
368  # make sure query result looks okay
369  $this->QueryHandle = TRUE;
370 
371  # increment cache hit counter
372  self::$CachedQueryCounter++;
373 
374  # make local copy of results
375  $this->QueryResults = self::$QueryResultCache[$QueryString];
376  $this->NumRows = self::$QueryResultCache[$QueryString]["NumRows"];
377 
378  # set flag to indicate that results should be retrieved from cache
379  $this->GetResultsFromCache = TRUE;
380  }
381  else
382  {
383  # execute SQL statement
384  $this->QueryHandle = $this->RunQuery($QueryString);
385  if (!$this->QueryHandle instanceof mysqli_result) { return FALSE; }
386 
387  # save number of rows in result
388  $this->NumRows = mysqli_num_rows($this->QueryHandle);
389 
390  # if too many rows to cache
391  if ($this->NumRows >= self::$CacheRowsThreshold)
392  {
393  # set flag to indicate that query results should not
394  # be retrieved from cache
395  $this->GetResultsFromCache = FALSE;
396  }
397  else
398  {
399  # if we are low on memory
400  if (self::GetFreeMemory() < self::$CacheMemoryThreshold)
401  {
402  # clear out all but last few rows from cache
403  self::$QueryResultCache = array_slice(
404  self::$QueryResultCache,
405  (0 - self::$CacheRowsToLeave));
406  }
407 
408  # if advanced caching is enabled
409  if (self::$AdvancedCachingFlag)
410  {
411  # save tables accessed by query
412  self::$QueryResultCache[$QueryString]["TablesAccessed"] =
413  $this->TablesAccessed($QueryString);
414  }
415 
416  # if rows found
417  if ($this->NumRows > 0)
418  {
419  # load query results
420  for ($Row = 0; $Row < $this->NumRows; $Row++)
421  {
422  $this->QueryResults[$Row] =
423  mysqli_fetch_assoc($this->QueryHandle);
424  }
425 
426  # cache query results
427  self::$QueryResultCache[$QueryString] = $this->QueryResults;
428  }
429  else
430  {
431  # clear local query results
432  unset($this->QueryResults);
433  }
434 
435  # cache number of rows
436  self::$QueryResultCache[$QueryString]["NumRows"] = $this->NumRows;
437 
438  # set flag to indicate that query results should be
439  # retrieved from cache
440  $this->GetResultsFromCache = TRUE;
441  }
442  }
443  }
444  else
445  {
446  # if advanced caching is enabled
447  if (self::$AdvancedCachingFlag)
448  {
449  # if table modified by statement is known
450  $TableModified = $this->TableModified($QueryString);
451  if ($TableModified)
452  {
453  # for each cached query
454  foreach (self::$QueryResultCache
455  as $CachedQueryString => $CachedQueryResult)
456  {
457  # if we know what tables were accessed
458  if ($CachedQueryResult["TablesAccessed"])
459  {
460  # if tables accessed include the one we may modify
461  if (in_array($TableModified,
462  $CachedQueryResult["TablesAccessed"]))
463  {
464  # clear cached query results
465  unset(self::$QueryResultCache[$CachedQueryString]);
466  }
467  }
468  else
469  {
470  # clear cached query results
471  unset(self::$QueryResultCache[$CachedQueryString]);
472  }
473  }
474  }
475  else
476  {
477  # clear entire query result cache
478  self::$QueryResultCache = array();
479  }
480  }
481  else
482  {
483  # clear entire query result cache
484  self::$QueryResultCache = array();
485  }
486 
487  # execute SQL statement
488  $this->QueryHandle = $this->RunQuery($QueryString);
489  if ($this->QueryHandle === FALSE) { return FALSE; }
490 
491  # set flag to indicate that query results should not be
492  # retrieved from cache
493  $this->GetResultsFromCache = FALSE;
494  }
495 
496  # reset row counter
497  $this->RowCounter = 0;
498 
499  # increment query counter
500  self::$QueryCounter++;
501  }
502  else
503  {
504  # execute SQL statement
505  $this->QueryHandle = $this->RunQuery($QueryString);
506  if ($this->QueryHandle === FALSE) { return FALSE; }
507  }
508 
509  if (($FieldName != "") && ($this->QueryHandle !== FALSE))
510  {
511  return $this->FetchField($FieldName);
512  }
513  else
514  {
515  return $this->QueryHandle;
516  }
517  }
518 
531  public function ExecuteQueriesFromFile($FileName)
532  {
533  # open file
534  $FHandle = fopen($FileName, "r");
535 
536  # if file open succeeded
537  if ($FHandle !== FALSE)
538  {
539  # while lines left in file
540  $Query = "";
541  $QueryCount = 0;
542  while (!feof($FHandle))
543  {
544  # read in line from file
545  $Line = fgets($FHandle, 32767);
546 
547  # trim whitespace from line
548  $Line = trim($Line);
549 
550  # if line is not empty and not a comment
551  if (!preg_match("/^#/", $Line)
552  && !preg_match("/^--/", $Line)
553  && strlen($Line))
554  {
555  # add line to current query
556  $Query .= " ".$Line;
557 
558  # if line completes a query
559  if (preg_match("/;$/", $Line))
560  {
561  # run query
562  $QueryCount++;
563  $Result = $this->Query($Query);
564  $Query = "";
565 
566  # if query resulted in an error that is not ignorable
567  if ($Result === FALSE)
568  {
569  # stop processing queries and set error code
570  $QueryCount = NULL;
571  break;
572  }
573  }
574  }
575  }
576 
577  # close file
578  fclose($FHandle);
579  }
580 
581  # return number of executed queries to caller
582  return $QueryCount;
583  }
584 
590  public function QueryErrMsg()
591  {
592  return $this->ErrMsg;
593  }
594 
600  public function QueryErrNo()
601  {
602  return $this->ErrNo;
603  }
604 
611  public static function DisplayQueryErrors($NewValue = NULL)
612  {
613  if ($NewValue !== NULL) { self::$DisplayErrors = $NewValue; }
614  return self::$DisplayErrors;
615  }
616 
621  public function NumRowsSelected()
622  {
623  # if caching is enabled and query was cached
624  if (self::$CachingFlag && $this->GetResultsFromCache)
625  {
626  # return cached number of rows to caller
627  return $this->NumRows;
628  }
629  else
630  {
631  # call to this method after an unsuccessful query
632  if (!$this->QueryHandle instanceof mysqli_result)
633  {
634  return 0;
635  }
636 
637  # retrieve number of rows and return to caller
638  return mysqli_num_rows($this->QueryHandle);
639  }
640  }
641 
647  public function NumRowsAffected()
648  {
649  # call to this method after an unsuccessful query
650  if ($this->QueryHandle === FALSE)
651  {
652  return 0;
653  }
654 
655  # retrieve number of rows and return to caller
656  return mysqli_affected_rows($this->Handle);
657  }
658 
664  public function FetchRow()
665  {
666  # if caching is enabled and query was cached
667  if (self::$CachingFlag && $this->GetResultsFromCache)
668  {
669  # if rows left to return
670  if ($this->RowCounter < $this->NumRows)
671  {
672  # retrieve row from cache
673  $Result = $this->QueryResults[$this->RowCounter];
674 
675  # increment row counter
676  $this->RowCounter++;
677  }
678  else
679  {
680  # return nothing
681  $Result = FALSE;
682  }
683  }
684  else
685  {
686  # call to this method after successful query
687  if ($this->QueryHandle instanceof mysqli_result)
688  {
689  $Result = mysqli_fetch_assoc($this->QueryHandle);
690  if ($Result === NULL) { $Result = FALSE; }
691  }
692 
693  # call to this method after unsuccessful query
694  else
695  {
696  $Result = FALSE;
697  }
698  }
699 
700  # return row to caller
701  return $Result;
702  }
703 
710  public function FetchRows($NumberOfRows = NULL)
711  {
712  # assume no rows will be returned
713  $Result = array();
714 
715  # for each available row
716  $RowsFetched = 0;
717  while ((($RowsFetched < $NumberOfRows) || ($NumberOfRows == NULL))
718  && ($Row = $this->FetchRow()))
719  {
720  # add row to results
721  $Result[] = $Row;
722  $RowsFetched++;
723  }
724 
725  # return array of rows to caller
726  return $Result;
727  }
728 
745  public function FetchColumn($FieldName, $IndexFieldName = NULL)
746  {
747  $Array = array();
748  while ($Record = $this->FetchRow())
749  {
750  if ($IndexFieldName != NULL)
751  {
752  $Array[$Record[$IndexFieldName]] = $Record[$FieldName];
753  }
754  else
755  {
756  $Array[] = $Record[$FieldName];
757  }
758  }
759  return $Array;
760  }
761 
770  public function FetchField($FieldName)
771  {
772  $Record = $this->FetchRow();
773  return isset($Record[$FieldName]) ? $Record[$FieldName] : NULL;
774  }
775 
782  public function LastInsertId()
783  {
784  return (int)$this->Query(
785  "SELECT LAST_INSERT_ID() AS InsertId",
786  "InsertId");
787  }
788 
799  public function GetNextInsertId($TableName)
800  {
801  if (!$this->TableExists($TableName))
802  {
803  throw new Exception(
804  "Table ".$TableName." does not exist");
805  }
806 
807  return (int)$this->Query(
808  "SELECT `AUTO_INCREMENT` AS Id FROM INFORMATION_SCHEMA.TABLES "
809  ."WHERE TABLE_SCHEMA='".addslashes($this->DBName())."' "
810  ."AND TABLE_NAME = '".addslashes($TableName)."'", "Id");
811  }
812 
827  public function UpdateValue(
828  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
829  {
830  # expand condition if supplied
831  if ($Condition != NULL) { $Condition = " WHERE ".$Condition; }
832 
833  # if cached values not already loaded
834  if (!isset($CachedRecord))
835  {
836  # read cached record from database
837  $this->Query("SELECT * FROM `".$TableName."` ".$Condition);
838  $CachedRecord = $this->FetchRow();
839 
840  # error out if requested column does not exist in specified table
841  if ($NewValue === DB_NOVALUE)
842  {
843  $RowsFound = ($this->NumRowsSelected() > 0);
844  if (($RowsFound && !array_key_exists($FieldName, $CachedRecord))
845  || (!$RowsFound && !$this->FieldExists($TableName, $FieldName)))
846  {
847  throw new Exception("Column '".$FieldName
848  ."' not found in table '".$TableName."'.");
849  }
850  }
851  }
852 
853  # if new value supplied
854  if ($NewValue !== DB_NOVALUE)
855  {
856  # error out if we are trying to update a nonexistent record or field
857  if (!count($CachedRecord))
858  {
859  throw new Exception("No records found when attempting to update"
860  ." column '".$FieldName."' in table '".$TableName."'"
861  .(($Condition != NULL)
862  ? " using condition '".$Condition."'" : "")
863  .".");
864  }
865  else
866  {
867  if (!array_key_exists($FieldName, $CachedRecord))
868  {
869  throw new Exception("Attempting to update column '".$FieldName
870  ."', which does not exist in table '".$TableName."'.");
871  }
872  }
873 
874  # update value in database
875  $this->Query("UPDATE `".$TableName."` SET `".$FieldName."` = "
876  .(($NewValue === NULL) ? "NULL" : "'"
877  .mysqli_real_escape_string($this->Handle, $NewValue)."'")
878  .$Condition);
879 
880  # update value in cached record
881  $CachedRecord[$FieldName] = $NewValue;
882  }
883 
884  # return value from cached record to caller
885  return isset($CachedRecord[$FieldName])
886  ? $CachedRecord[$FieldName] : NULL;
887  }
888 
905  public function UpdateIntValue(
906  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
907  {
908  return $this->UpdateValue($TableName, $FieldName,
909  (($NewValue === DB_NOVALUE) ? DB_NOVALUE : (int)$NewValue),
910  $Condition, $CachedRecord);
911  }
912 
929  public function UpdateFloatValue(
930  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
931  {
932  return $this->UpdateValue($TableName, $FieldName,
933  (($NewValue === DB_NOVALUE) ? DB_NOVALUE : (float)$NewValue),
934  $Condition, $CachedRecord);
935  }
936 
948  public function CopyValues($TableName, $IdColumn, $SrcId, $DstId,
949  $ColumnsToExclude = array())
950  {
951  # retrieve names of all columns in table
952  $AllColumns = $this->GetColumns($TableName);
953 
954  # remove columns to be excluded from copy
955  $ColumnsToExclude[] = $IdColumn;
956  $ColumnsToCopy = array_diff($AllColumns, $ColumnsToExclude);
957 
958  # normalize destination IDs
959  $DstIds = is_array($DstId) ? $DstId : array($DstId);
960  $DstIds = array_diff($DstIds, array($SrcId));
961 
962  # if there are columns to copy and we have destinations
963  if (count($ColumnsToCopy) && count($DstIds))
964  {
965  # construct and execute query to perform copy
966  $Query = "UPDATE `".$TableName."` AS Target"
967  ." LEFT JOIN `".$TableName."` AS Source"
968  ." ON Source.`".$IdColumn."` = '".addslashes($SrcId)."'";
969  $QuerySets = array();
970  foreach ($ColumnsToCopy as $ColumnName)
971  {
972  $QuerySets[] = "Target.`".$ColumnName."` = Source.`".$ColumnName."`";
973  }
974  $Query .= " SET ".implode(", ", $QuerySets);
975  $QueryConditions = array();
976  foreach ($DstIds as $Id)
977  {
978  $QueryConditions[] = "Target.`".$IdColumn."` = '".addslashes($DstId)."'";
979  }
980  $Query .= " WHERE ".implode(" OR ", $QueryConditions);
981  $this->Query($Query);
982  }
983  }
984 
998  public function InsertArray($Table, $ValueField, $Values,
999  $KeyField = NULL, $AvgDataLength = 20)
1000  {
1001  # pick some ballpark values
1002  $ChunkSizeAssumedSafe = 100;
1003  $QueryLengthAssumedSafe = 10486576; # (1 MB)
1004 
1005  # exit without doing anything if there are no values
1006  $ValueCount = count($Values);
1007  if ($ValueCount == 0)
1008  {
1009  return;
1010  }
1011 
1012  # determine size of array chunk per INSERT statement
1013  $NonValueCharCount = 100;
1014  if ($ValueCount > $ChunkSizeAssumedSafe)
1015  {
1016  $MaxQueryLen = $this->GetMaxQueryLength();
1017  $ValueSegmentLen = $AvgDataLength + 6;
1018  if ($KeyField !== NULL)
1019  {
1020  $ValueSegmentLen = $ValueSegmentLen * 2;
1021  }
1022  $ValueChunkSize = $MaxQueryLen / $ValueSegmentLen;
1023  }
1024  else
1025  {
1026  $ValueChunkSize = $ChunkSizeAssumedSafe;
1027  }
1028 
1029  # for each chunk of values
1030  foreach (array_chunk($Values, $ValueChunkSize, TRUE) as $ValueChunk)
1031  {
1032  # begin building query
1033  $Query = "INSERT INTO `".$Table."` (`".$ValueField."`";
1034 
1035  # if key field was specified
1036  if ($KeyField !== NULL)
1037  {
1038  # add key field to query
1039  $Query .= ", `".$KeyField."`";
1040 
1041  # assemble value segment with keys
1042  $ValueSegFunc = function($Carry, $Key) use ($ValueChunk)
1043  {
1044  $Carry .= "('".addslashes($ValueChunk[$Key])."','"
1045  .addslashes($Key)."'),";
1046  return $Carry;
1047  };
1048  $ValueSegment = array_reduce(array_keys($ValueChunk), $ValueSegFunc);
1049  }
1050  else
1051  {
1052  # assemble value segment
1053  $ValueSegFunc = function($Carry, $Value)
1054  {
1055  $Carry .= "('".addslashes($Value)."'),";
1056  return $Carry;
1057  };
1058  $ValueSegment = array_reduce($ValueChunk, $ValueSegFunc);
1059  }
1060 
1061  # trim extraneous comma off of value segment
1062  $ValueSegment = substr($ValueSegment, 0, -1);
1063 
1064  # add value segment to query
1065  $Query .= ") VALUES ".$ValueSegment;
1066 
1067  # double check to make sure query isn't too long
1068  $QueryLen = strlen($Query);
1069  if ($QueryLen > $QueryLengthAssumedSafe)
1070  {
1071  if (!isset($MaxQueryLen))
1072  {
1073  $MaxQueryLen = $this->GetMaxQueryLength();
1074  }
1075  if ($QueryLen > $MaxQueryLen)
1076  {
1077  throw new Exception("Maximum query length ("
1078  .$MaxQueryLen.") exceeded (".$QueryLen.").");
1079  }
1080  }
1081 
1082  # run query
1083  $this->Query($Query);
1084  }
1085  }
1086 
1087  /*@)*/ /* Data Manipulation */ /*@(*/
1089 
1098  public function EscapeString($String)
1099  {
1100  return mysqli_real_escape_string($this->Handle, $String);
1101  }
1102 
1109  public function LogComment($String)
1110  {
1111  $this->Query("-- ".$String);
1112  }
1113 
1119  public function TableExists($TableName)
1120  {
1121  $this->Query("SHOW TABLES LIKE '".addslashes($TableName)."'");
1122  return $this->NumRowsSelected() ? TRUE : FALSE;
1123  }
1124 
1131  public function FieldExists($TableName, $FieldName)
1132  {
1133  $this->Query("DESC ".$TableName);
1134  while ($CurrentFieldName = $this->FetchField("Field"))
1135  {
1136  if ($CurrentFieldName == $FieldName) { return TRUE; }
1137  }
1138  return FALSE;
1139  }
1140 
1147  public function GetFieldType($TableName, $FieldName)
1148  {
1149  $this->Query("DESC ".$TableName);
1150  $AllTypes = $this->FetchColumn("Type", "Field");
1151  return $AllTypes[$FieldName];
1152  }
1153 
1159  public function GetColumns($TableName)
1160  {
1161  $this->Query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS"
1162  ." WHERE TABLE_SCHEMA = '".addslashes($this->DBName)
1163  ."' AND TABLE_NAME = '".addslashes($TableName)."'");
1164  return $this->FetchColumn("COLUMN_NAME");
1165  }
1166 
1171  public function GetMaxQueryLength()
1172  {
1173  return $this->Query("SHOW VARIABLES LIKE 'max_allowed_packet'",
1174  "Value");
1175  }
1176 
1182  public static function QueryDebugOutput($NewSetting)
1183  {
1184  self::$QueryDebugOutputFlag = $NewSetting;
1185  }
1186 
1192  public static function NumQueries()
1193  {
1194  return self::$QueryCounter;
1195  }
1196 
1203  public static function NumCacheHits()
1204  {
1205  return self::$CachedQueryCounter;
1206  }
1207 
1213  public static function CacheHitRate()
1214  {
1215  if (self::$QueryCounter)
1216  {
1217  return (self::$CachedQueryCounter / self::$QueryCounter) * 100;
1218  }
1219  else
1220  {
1221  return 0;
1222  }
1223  }
1224 
1225  /*@)*/ /* Miscellaneous */
1226 
1227  # ---- PRIVATE INTERFACE -------------------------------------------------
1228 
1229  protected $DBUserName;
1230  protected $DBPassword;
1231  protected $DBHostName;
1232  protected $DBName;
1233 
1234  private $Handle;
1235  private $QueryHandle;
1236  private $QueryResults;
1237  private $RowCounter;
1238  private $NumRows;
1239  private $GetResultsFromCache;
1240  private $ErrorIgnored = FALSE;
1241  private $ErrorsToIgnore = NULL;
1242  private $ErrMsg = NULL;
1243  private $ErrNo = NULL;
1244 
1245  private static $DisplayErrors = FALSE;
1246 
1247  private static $GlobalDBUserName;
1248  private static $GlobalDBPassword;
1249  private static $GlobalDBHostName;
1250  private static $GlobalDBName;
1251 
1252  # debug output flag
1253  private static $QueryDebugOutputFlag = FALSE;
1254  # flag for whether caching is turned on
1255  private static $CachingFlag = TRUE;
1256  # query result advanced caching flag
1257  private static $AdvancedCachingFlag = FALSE;
1258  # global cache for query results
1259  private static $QueryResultCache = array();
1260  # stats counters
1261  private static $QueryCounter = 0;
1262  private static $CachedQueryCounter = 0;
1263  # database connection link handles
1264  private static $ConnectionHandles = array();
1265  # do not cache queries that return more than this number of rows
1266  private static $CacheRowsThreshold = 250;
1267  # prune the query cache if there is less than this amount of memory free
1268  private static $CacheMemoryThreshold;
1269  # number of rows to leave in cache when pruning
1270  private static $CacheRowsToLeave = 10;
1271  # number of retry attempts to make to connect to database
1272  private static $ConnectRetryAttempts = 3;
1273  # number of seconds to wait between connection retry attempts
1274  private static $ConnectRetryInterval = 5;
1275 
1276  # server connection error codes
1277  const CR_CONNECTION_ERROR = 2002; # Can't connect to local MySQL server
1278  # through socket '%s' (%d)
1279  const CR_CONN_HOST_ERROR = 2003; # Can't connect to MySQL server on '%s' (%d)
1280  const CR_SERVER_GONE_ERROR = 2006; # MySQL server has gone away
1281  const CR_SERVER_LOST = 2013; # Lost connection to MySQL server during query
1282 
1283  # limits on int variables
1284  # https://dev.mysql.com/doc/refman/5.7/en/integer-types.html
1285  const TINYINT_MAX_VALUE = 127;
1286  const SMALLINT_MAX_VALUE = 32767;
1287  const MEDIUMINT_MAX_VALUE = 8388607;
1288  const INT_MAX_VALUE = 2147483647;
1289  const BIGINT_MAX_VALUE = 9223372036854775807;
1290 
1291  # connection error codes that may be recoverable
1292  private static $RecoverableConnectionErrors = array(
1293  self::CR_CONNECTION_ERROR,
1294  );
1295 
1306  private static function ConnectAndSelectDB(
1308  {
1309  $ConnectAttemptsLeft = self::$ConnectRetryAttempts + 1;
1310  do
1311  {
1312  # if this is not our first connection attempt
1313  if (isset($Handle))
1314  {
1315  # wait for the retry interval
1316  sleep(self::$ConnectRetryInterval);
1317  }
1318 
1319  # attempt to connect to server
1320  $Handle = @mysqli_connect($DBHostName, $DBUserName, $DBPassword);
1321  $ConnectAttemptsLeft--;
1322  }
1323  # repeat if we do not have a connection and there are retry attempts
1324  # left and the connection error code indicates a retry may succeed
1325  // @codingStandardsIgnoreStart
1326  // (because phpcs apparently doesn't know how to handle do-while loops)
1327  while (!$Handle && $ConnectAttemptsLeft
1328  && in_array(mysqli_connect_errno(),
1329  self::$RecoverableConnectionErrors));
1330  // @codingStandardsIgnoreEnd
1331 
1332  # throw exception if connection attempts failed
1333  if (!$Handle)
1334  {
1335  throw new Exception("Could not connect to database: "
1336  .mysqli_connect_error()." (errno: ".mysqli_connect_errno().")");
1337  }
1338 
1339  # select DB
1340  $Result = mysqli_select_db($Handle, $DBName);
1341  if ($Result !== TRUE)
1342  {
1343  throw new Exception("Could not select database: "
1344  .mysqli_error($Handle)." (errno: ".mysqli_errno($Handle).")");
1345  }
1346 
1347  # return new connection to caller
1348  return $Handle;
1349  }
1350 
1356  private function IsReadOnlyStatement($QueryString)
1357  {
1358  return preg_match("/^[ ]*SELECT /i", $QueryString) ? TRUE : FALSE;
1359  }
1360 
1367  private function TableModified($QueryString)
1368  {
1369  # assume we're not going to be able to determine table
1370  $TableName = FALSE;
1371 
1372  # split query into pieces
1373  $QueryString = trim($QueryString);
1374  $Words = preg_split("/\s+/", $QueryString);
1375 
1376  # if INSERT statement
1377  $WordIndex = 1;
1378  if (strtoupper($Words[0]) == "INSERT")
1379  {
1380  # skip over modifying keywords
1381  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
1382  || (strtoupper($Words[$WordIndex]) == "DELAYED")
1383  || (strtoupper($Words[$WordIndex]) == "IGNORE")
1384  || (strtoupper($Words[$WordIndex]) == "INTO"))
1385  {
1386  $WordIndex++;
1387  }
1388 
1389  # next word is table name
1390  $TableName = $Words[$WordIndex];
1391  }
1392  # else if UPDATE statement
1393  elseif (strtoupper($Words[0]) == "UPDATE")
1394  {
1395  # skip over modifying keywords
1396  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
1397  || (strtoupper($Words[$WordIndex]) == "IGNORE"))
1398  {
1399  $WordIndex++;
1400  }
1401 
1402  # if word following next word is SET
1403  if (strtoupper($Words[$WordIndex + 1]) == "SET")
1404  {
1405  # next word is table name
1406  $TableName = $Words[$WordIndex];
1407  }
1408  }
1409  # else if DELETE statement
1410  elseif (strtoupper($Words[0]) == "DELETE")
1411  {
1412  # skip over modifying keywords
1413  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
1414  || (strtoupper($Words[$WordIndex]) == "IGNORE")
1415  || (strtoupper($Words[$WordIndex]) == "QUICK"))
1416  {
1417  $WordIndex++;
1418  }
1419 
1420  # if next term is FROM
1421  if (strtoupper($Words[$WordIndex]) == "FROM")
1422  {
1423  # next word is table name
1424  $WordIndex++;
1425  $TableName = $Words[$WordIndex];
1426  }
1427  }
1428 
1429  # discard table name if it looks at all suspicious
1430  if ($TableName)
1431  {
1432  if (!preg_match("/[a-zA-Z0-9]+/", $TableName))
1433  {
1434  $TableName = FALSE;
1435  }
1436  }
1437 
1438  # return table name (or lack thereof) to caller
1439  return $TableName;
1440  }
1441 
1448  private function TablesAccessed($QueryString)
1449  {
1450  # assume we're not going to be able to determine tables
1451  $TableNames = FALSE;
1452 
1453  # split query into pieces
1454  $QueryString = trim($QueryString);
1455  $Words = preg_split("/\s+/", $QueryString);
1456  $UQueryString = strtoupper($QueryString);
1457  $UWords = preg_split("/\s+/", $UQueryString);
1458 
1459  # if SELECT statement
1460  if ($UWords[0] == "SELECT")
1461  {
1462  # keep going until we hit FROM or last word
1463  $WordIndex = 1;
1464  while (($UWords[$WordIndex] != "FROM")
1465  && strlen($UWords[$WordIndex]))
1466  {
1467  $WordIndex++;
1468  }
1469 
1470  # if we hit FROM
1471  if ($UWords[$WordIndex] == "FROM")
1472  {
1473  # for each word after FROM
1474  $WordIndex++;
1475  while (strlen($UWords[$WordIndex]))
1476  {
1477  # if current word ends with comma
1478  if (preg_match("/,$/", $Words[$WordIndex]))
1479  {
1480  # strip off comma and add word to table name list
1481  $TableNames[] = substr($Words[$WordIndex], 0, -1);
1482  }
1483  else
1484  {
1485  # add word to table name list
1486  $TableNames[] = $Words[$WordIndex];
1487 
1488  # if next word is not comma
1489  $WordIndex++;
1490  if ($Words[$WordIndex] != ",")
1491  {
1492  # if word begins with comma
1493  if (preg_match("/^,/", $Words[$WordIndex]))
1494  {
1495  # strip off comma (NOTE: modifies $Words array!)
1496  $Words[$WordIndex] = substr($Words[$WordIndex], 1);
1497 
1498  # decrement index so we start with this word next pass
1499  $WordIndex--;
1500  }
1501  else
1502  {
1503  # stop scanning words (non-basic JOINs not yet handled)
1504  break;
1505  }
1506  }
1507  }
1508 
1509  # move to next word
1510  $WordIndex++;
1511  }
1512  }
1513  }
1514 
1515  # discard table names if they look at all suspicious
1516  if ($TableNames)
1517  {
1518  foreach ($TableNames as $Name)
1519  {
1520  if (!preg_match("/^[a-zA-Z0-9]+$/", $Name))
1521  {
1522  $TableNames = FALSE;
1523  break;
1524  }
1525  }
1526  }
1527 
1528  # return table name (or lack thereof) to caller
1529  return $TableNames;
1530  }
1531 
1538  private function RunQuery($QueryString)
1539  {
1540  # log query start time if debugging output is enabled
1541  if (self::$QueryDebugOutputFlag) { $QueryStartTime = microtime(TRUE); }
1542 
1543  # run query against database
1544  $this->QueryHandle = mysqli_query($this->Handle, $QueryString) ;
1545 
1546  # print query and execution time if debugging output is enabled
1547  if (self::$QueryDebugOutputFlag)
1548  {
1549  print "DB: ".$QueryString." ["
1550  .sprintf("%.2f", microtime(TRUE) - $QueryStartTime)
1551  ."s]"."<br>\n";
1552  }
1553 
1554  # if query failed and there are errors that we can ignore
1555  if (($this->QueryHandle === FALSE) && $this->ErrorsToIgnore)
1556  {
1557  # for each pattern for an error that we can ignore
1558  foreach ($this->ErrorsToIgnore as $SqlPattern => $ErrMsgPattern)
1559  {
1560  # if error matches pattern
1561  $ErrorMsg = mysqli_error($this->Handle);
1562  if (preg_match($SqlPattern, $QueryString)
1563  && preg_match($ErrMsgPattern, $ErrorMsg))
1564  {
1565  # set return value to indicate error was ignored
1566  $this->QueryHandle = TRUE;
1567 
1568  # set internal flag to indicate that an error was ignored
1569  $this->ErrorIgnored = $ErrorMsg;
1570 
1571  # stop looking at patterns
1572  break;
1573  }
1574  }
1575  }
1576 
1577  # if query failed
1578  if ($this->QueryHandle === FALSE)
1579  {
1580  # clear stored value for number of rows retrieved
1581  $this->NumRows = 0;
1582 
1583  # retrieve error info
1584  $this->ErrMsg = mysqli_error($this->Handle);
1585  $this->ErrNo = mysqli_errno($this->Handle);
1586 
1587  # if we are supposed to be displaying errors
1588  if (self::$DisplayErrors)
1589  {
1590  # print error info
1591  print("<b>SQL Error:</b> <i>".$this->ErrMsg
1592  ."</i> (".$this->ErrNo.")<br/>\n");
1593  print("<b>SQL Statement:</b> <i>"
1594  .htmlspecialchars($QueryString)."</i><br/>\n");
1595 
1596  # retrieve execution trace that got us to this point
1597  $Trace = debug_backtrace();
1598 
1599  # remove current context from trace
1600  array_shift($Trace);
1601 
1602  # make sure file name and line number are available
1603  foreach ($Trace as $Index => $Loc)
1604  {
1605  if (!array_key_exists("file", $Loc))
1606  {
1607  $Trace[$Index]["file"] = "UNKNOWN";
1608  }
1609  if (!array_key_exists("line", $Loc))
1610  {
1611  $Trace[$Index]["line"] = "??";
1612  }
1613  }
1614 
1615  # determine length of leading path common to all file names in trace
1616  $LocString = "";
1617  $OurFile = __FILE__;
1618  $PrefixLen = 9999;
1619  foreach ($Trace as $Loc)
1620  {
1621  if ($Loc["file"] != "UNKNOWN")
1622  {
1623  $Index = 0;
1624  $FNameLength = strlen($Loc["file"]);
1625  while ($Index < $FNameLength &&
1626  $Loc["file"][$Index] == $OurFile[$Index])
1627  { $Index++; }
1628  $PrefixLen = min($PrefixLen, $Index);
1629  }
1630  }
1631 
1632  foreach ($Trace as $Loc)
1633  {
1634  $Sep = "";
1635  $ArgString = "";
1636  foreach ($Loc["args"] as $Arg)
1637  {
1638  $ArgString .= $Sep;
1639  switch (gettype($Arg))
1640  {
1641  case "boolean":
1642  $ArgString .= $Arg ? "TRUE" : "FALSE";
1643  break;
1644 
1645  case "integer":
1646  case "double":
1647  $ArgString .= $Arg;
1648  break;
1649 
1650  case "string":
1651  $ArgString .= '"<i>'.htmlspecialchars(substr($Arg, 0, 40))
1652  .((strlen($Arg) > 40) ? "..." : "").'</i>"';
1653  break;
1654 
1655  case "array":
1656  case "resource":
1657  case "NULL":
1658  $ArgString .= strtoupper(gettype($Arg));
1659  break;
1660 
1661  case "object":
1662  $ArgString .= get_class($Arg);
1663  break;
1664 
1665  case "unknown type":
1666  $ArgString .= "UNKNOWN";
1667  break;
1668  }
1669  $Sep = ",";
1670  }
1671  $Loc["file"] = substr($Loc["file"], $PrefixLen);
1672  $LocString .= "&nbsp;&nbsp;";
1673  if (array_key_exists("class", $Loc))
1674  { $LocString .= $Loc["class"]."::"; }
1675  $LocString .= $Loc["function"]."(".$ArgString.")"
1676  ." - ".$Loc["file"].":".$Loc["line"]
1677  ."<br>\n";
1678  }
1679  print("<b>Trace:</b><br>\n".$LocString);
1680  }
1681  }
1682  return $this->QueryHandle;
1683  }
1684 
1689  static private function GetPhpMemoryLimit()
1690  {
1691  $Str = strtoupper(ini_get("memory_limit"));
1692  if (substr($Str, -1) == "B") { $Str = substr($Str, 0, strlen($Str) - 1); }
1693  switch (substr($Str, -1))
1694  {
1695  case "K":
1696  $MemoryLimit = (int)$Str * 1024;
1697  break;
1698 
1699  case "M":
1700  $MemoryLimit = (int)$Str * 1048576;
1701  break;
1702 
1703  case "G":
1704  $MemoryLimit = (int)$Str * 1073741824;
1705  break;
1706 
1707  default:
1708  $MemoryLimit = (int)$Str;
1709  break;
1710  }
1711  return $MemoryLimit;
1712  }
1713 
1718  static private function GetFreeMemory()
1719  {
1720  return self::GetPhpMemoryLimit() - memory_get_usage();
1721  }
1722 }
1723 
1724 # define return values (numerical values correspond to MySQL error codes)
1725 // @codingStandardsIgnoreStart (to silence warning about multiple spaces)
1726 define("DB_OKAY", 0);
1727 define("DB_ERROR", 1);
1728 define("DB_ACCESSDENIED", 2);
1729 define("DB_UNKNOWNDB", 3);
1730 define("DB_UNKNOWNTABLE", 4);
1731 define("DB_SYNTAXERROR", 5);
1732 define("DB_DBALREADYEXISTS", 6);
1733 define("DB_DBDOESNOTEXIST", 7);
1734 define("DB_DISKFULL", 8);
1735 // @codingStandardsIgnoreEnd
1736 
1737 # define value to designate omitted arguments (so DB values can be set to NULL)
1738 define("DB_NOVALUE", "!-_-_-DB_NOVALUE-_-_-!");
1739 
1740 # MySQL error code mapping
1742  1045 => DB_ACCESSDENIED,
1743  1049 => DB_UNKNOWNDB,
1744  1046 => DB_UNKNOWNTABLE,
1745  1064 => DB_SYNTAXERROR,
1746  1007 => DB_DBALREADYEXISTS, # ? (not sure)
1747  1008 => DB_DBDOESNOTEXIST, # ? (not sure)
1748  1021 => DB_DISKFULL, # ? (not sure)
1749  );
1750 
1751 # date() format for SQL dates
1752 define("DATE_SQL", "Y-m-d H:i:s");
QueryErrMsg()
Get most recent error message text set by Query().
Definition: Database.php:590
static Caching($NewSetting=NULL)
Get or set whether query result caching is currently enabled.
Definition: Database.php:259
__sleep()
Definition: Database.php:81
GetServerVersion($FullVersion=FALSE)
Get database server version number.
Definition: Database.php:183
static SetGlobalDatabaseName($DatabaseName)
Set default database name.
Definition: Database.php:154
const CR_CONNECTION_ERROR
Definition: Database.php:1277
const DB_ACCESSDENIED
Definition: Database.php:1728
SetDefaultStorageEngine($Engine)
Set default database storage engine.
Definition: Database.php:167
ExecuteQueriesFromFile($FileName)
Execute queries from specified file.
Definition: Database.php:531
UpdateIntValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set an integer value in the database.
Definition: Database.php:905
SQL database abstraction object with smart query caching.
Definition: Database.php:22
SetQueryErrorsToIgnore($ErrorsToIgnore, $NormalizeWhitespace=TRUE)
Set query errors to ignore.
Definition: Database.php:313
const CR_SERVER_GONE_ERROR
Definition: Database.php:1280
DBUserName()
Get name used to connect with database server.
Definition: Database.php:247
GetNextInsertId($TableName)
For tables that have an AUTO_INCREMENT column, get the next value that will be assigned.
Definition: Database.php:799
EscapeString($String)
Escape a string that may contain null bytes.
Definition: Database.php:1098
FetchRow()
Get next database row retrieved by most recent query.
Definition: Database.php:664
LastInsertId()
Get ID of row added by the last SQL "INSERT" statement.
Definition: Database.php:782
const CR_SERVER_LOST
Definition: Database.php:1281
const DB_SYNTAXERROR
Definition: Database.php:1731
__construct($UserName=NULL, $Password=NULL, $DatabaseName=NULL, $HostName=NULL)
Object constructor.
Definition: Database.php:43
TableExists($TableName)
Get whether specified table exists.
Definition: Database.php:1119
const DB_DISKFULL
Definition: Database.php:1734
const INT_MAX_VALUE
Definition: Database.php:1288
static SetGlobalServerInfo($UserName, $Password, $HostName="localhost")
Set default login and host info for database server.
Definition: Database.php:138
$APDBErrorCodeMappings
Definition: Database.php:1741
GetClientVersion()
Get version number of the client libraries being used to connect to the database server (Currently th...
Definition: Database.php:207
GetFieldType($TableName, $FieldName)
Get field (column) type.
Definition: Database.php:1147
const DB_NOVALUE
Definition: Database.php:1738
const MEDIUMINT_MAX_VALUE
Definition: Database.php:1287
NumRowsSelected()
Get number of rows returned by last SELECT or SHOW query.
Definition: Database.php:621
const SMALLINT_MAX_VALUE
Definition: Database.php:1286
FetchRows($NumberOfRows=NULL)
Get specified number of database rows retrieved by most recent query.
Definition: Database.php:710
static QueryDebugOutput($NewSetting)
Enable or disable debugging output for queries.
Definition: Database.php:1182
Query($QueryString, $FieldName="")
Query database (with caching if enabled).
Definition: Database.php:351
GetColumns($TableName)
Get column (database field) names.
Definition: Database.php:1159
FetchField($FieldName)
Pull next row from last DB query and get a specific value from that row.
Definition: Database.php:770
FieldExists($TableName, $FieldName)
Get whether specified field exists in specified table.
Definition: Database.php:1131
static NumCacheHits()
Get the number of queries that have resulted in cache hits since program execution began...
Definition: Database.php:1203
const DB_UNKNOWNTABLE
Definition: Database.php:1730
FetchColumn($FieldName, $IndexFieldName=NULL)
Get all available values for specified database field retrieved by most recent query.
Definition: Database.php:745
DBHostName()
Get host name of system on which database server resides.
Definition: Database.php:227
const DB_DBDOESNOTEXIST
Definition: Database.php:1733
NumRowsAffected()
Get number of rows affected by last INSERT, UPDATE, REPLACE, or DELETE query.
Definition: Database.php:647
UpdateFloatValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set a float value in the database.
Definition: Database.php:929
const DB_DBALREADYEXISTS
Definition: Database.php:1732
InsertArray($Table, $ValueField, $Values, $KeyField=NULL, $AvgDataLength=20)
Insert an array of values with a minimum number of INSERT statements.
Definition: Database.php:998
QueryErrNo()
Get most recent error code set by Query().
Definition: Database.php:600
const DB_UNKNOWNDB
Definition: Database.php:1729
GetHostInfo()
Get database connection type and hostname.
Definition: Database.php:217
UpdateValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set a value in the database.
Definition: Database.php:827
static AdvancedCaching($NewSetting=NULL)
Get or set whether advanced query result cachine is currently enabled.
Definition: Database.php:285
const BIGINT_MAX_VALUE
Definition: Database.php:1289
const TINYINT_MAX_VALUE
Definition: Database.php:1285
static DisplayQueryErrors($NewValue=NULL)
Get/set whether Query() errors will be displayed.
Definition: Database.php:611
static CacheHitRate()
Get the ratio of query cache hits to queries as a percentage.
Definition: Database.php:1213
GetMaxQueryLength()
Get maximum size for query string.
Definition: Database.php:1171
IgnoredError()
Check whether an error was ignored by the most recent query.
Definition: Database.php:333
DBName()
Get current database name.
Definition: Database.php:237
CopyValues($TableName, $IdColumn, $SrcId, $DstId, $ColumnsToExclude=array())
A convenience function to copy values from one row to another.
Definition: Database.php:948
LogComment($String)
Peform query that consists of SQL comment statement.
Definition: Database.php:1109
__wakeup()
Restore database connection when unserialized.
Definition: Database.php:90
static NumQueries()
Get the number of queries that have been run since program execution began.
Definition: Database.php:1192