CWIS Developer Documentation
Axis--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  # ---- PUBLIC INTERFACE --------------------------------------------------
25  /*@(*/
27 
40  function Database(
41  $UserName = NULL, $Password = NULL, $DatabaseName = NULL, $HostName = NULL)
42  {
43  # save DB access parameter values
44  $this->DBUserName = $UserName ? $UserName : self::$GlobalDBUserName;
45  $this->DBPassword = $Password ? $Password : self::$GlobalDBPassword;
46  $this->DBHostName = $HostName ? $HostName :
47  (isset(self::$GlobalDBHostName) ? self::$GlobalDBHostName
48  : "localhost");
49  $this->DBName = $DatabaseName ? $DatabaseName : self::$GlobalDBName;
50 
51  # if we don't already have a connection or DB access parameters were supplied
52  $HandleIndex = $this->DBHostName.":".$this->DBName;
53  if (!array_key_exists($HandleIndex, self::$ConnectionHandles)
54  || $UserName || $Password || $DatabaseName || $HostName)
55  {
56  # open connection to DB server
57  self::$ConnectionHandles[$HandleIndex] = mysql_connect(
58  $this->DBHostName, $this->DBUserName,
59  $this->DBPassword, TRUE)
60  or die("Could not connect to database: ".mysql_error());
61 
62  # set local connection handle
63  $this->Handle = self::$ConnectionHandles[$HandleIndex];
64 
65  # select DB
66  mysql_select_db($this->DBName, $this->Handle)
67  or die(mysql_error($this->Handle));
68  }
69  else
70  {
71  # set local connection handle
72  $this->Handle = self::$ConnectionHandles[$HandleIndex];
73  }
74  }
75 
80  function __sleep()
81  {
82  return array("DBUserName", "DBPassword", "DBHostName", "DBName");
83  }
87  function __wakeup()
88  {
89  # open connection to DB server
90  $this->Handle = mysql_connect(
91  $this->DBHostName, $this->DBUserName, $this->DBPassword)
92  or die("could not connect to database");
93 
94  # select DB
95  mysql_select_db($this->DBName, $this->Handle)
96  or die(mysql_error($this->Handle));
97  }
107  static function SetGlobalServerInfo($UserName, $Password, $HostName = "localhost")
108  {
109  # save default DB access parameters
110  self::$GlobalDBUserName = $UserName;
111  self::$GlobalDBPassword = $Password;
112  self::$GlobalDBHostName = $HostName;
113 
114  # clear any existing DB connection handles
115  self::$ConnectionHandles = array();
116  }
117 
122  static function SetGlobalDatabaseName($DatabaseName)
123  {
124  # save new default DB name
125  self::$GlobalDBName = $DatabaseName;
126 
127  # clear any existing DB connection handles
128  self::$ConnectionHandles = array();
129  }
130 
135  function SetDefaultStorageEngine($Engine)
136  {
137  # choose config variable to use based on server version number
138  $ConfigVar = version_compare($this->GetServerVersion(), "5.5", "<")
139  ? "storage_engine" : "default_storage_engine";
140 
141  # set storage engine in database
142  $this->Query("SET ".$ConfigVar." = ".$Engine);
143  }
144 
149  function GetServerVersion()
150  {
151  # retrieve version string
152  $Version = $this->Query("SELECT VERSION() AS ServerVer", "ServerVer");
153 
154  # strip off any build/config suffix
155  $Pieces = explode("-", $Version);
156  $Version = array_shift($Pieces);
157 
158  # return version number to caller
159  return $Version;
160  }
161 
167  function DBHostName() { return $this->DBHostName; }
168 
174  function DBName() { return $this->DBName; }
175 
181  function DBUserName() { return $this->DBUserName; }
182 
190  static function Caching($NewSetting = NULL)
191  {
192  # if cache setting has changed
193  if (($NewSetting !== NULL) && ($NewSetting != self::$CachingFlag))
194  {
195  # save new setting
196  self::$CachingFlag = $NewSetting;
197 
198  # clear any existing cached results
199  self::$QueryResultCache = array();
200  }
201 
202  # return current setting to caller
203  return self::$CachingFlag;
204  }
205 
216  static function AdvancedCaching($NewSetting = NULL)
217  {
218  if ($NewSetting !== NULL)
219  {
220  self::$AdvancedCachingFlag = $NewSetting;
221  }
222  return self::$AdvancedCachingFlag;
223  }
224 
244  function SetQueryErrorsToIgnore($ErrorsToIgnore, $NormalizeWhitespace = TRUE)
245  {
246  if ($NormalizeWhitespace && ($ErrorsToIgnore !== NULL))
247  {
248  $RevisedErrorsToIgnore = array();
249  foreach ($ErrorsToIgnore as $SqlPattern => $ErrMsgPattern)
250  {
251  $SqlPattern = preg_replace("/\\s+/", "\\s+", $SqlPattern);
252  $RevisedErrorsToIgnore[$SqlPattern] = $ErrMsgPattern;
253  }
254  $ErrorsToIgnore = $RevisedErrorsToIgnore;
255  }
256  $this->ErrorsToIgnore = $ErrorsToIgnore;
257  }
258 
264  function IgnoredError()
265  {
266  return $this->ErrorIgnored;
267  }
268 
269  /*@)*/ /* Setup/Initialization */ /*@(*/
271 
279  function Query($QueryString, $FieldName = "")
280  {
281  # clear flag that indicates whether query error was ignored
282  $this->ErrorIgnored = FALSE;
283 
284  # if caching is enabled
285  if (self::$CachingFlag)
286  {
287  # if SQL statement is read-only
288  if ($this->IsReadOnlyStatement($QueryString))
289  {
290  # if we have statement in cache
291  if (isset(self::$QueryResultCache[$QueryString]["NumRows"]))
292  {
293  if (self::$QueryDebugOutputFlag)
294  { print("DB-C: $QueryString<br>\n"); }
295 
296  # make sure query result looks okay
297  $this->QueryHandle = TRUE;
298 
299  # increment cache hit counter
300  self::$CachedQueryCounter++;
301 
302  # make local copy of results
303  $this->QueryResults = self::$QueryResultCache[$QueryString];
304  $this->NumRows = self::$QueryResultCache[$QueryString]["NumRows"];
305 
306  # set flag to indicate that results should be retrieved from cache
307  $this->GetResultsFromCache = TRUE;
308  }
309  else
310  {
311  # execute SQL statement
312  $this->QueryHandle = $this->RunQuery($QueryString);
313  if (!is_resource($this->QueryHandle)) { return FALSE; }
314 
315  # save number of rows in result
316  $this->NumRows = mysql_num_rows($this->QueryHandle);
317 
318  # if too many rows to cache
319  if ($this->NumRows >= 50)
320  {
321  # set flag to indicate that query results should not
322  # be retrieved from cache
323  $this->GetResultsFromCache = FALSE;
324  }
325  else
326  {
327  # if advanced caching is enabled
328  if (self::$AdvancedCachingFlag)
329  {
330  # save tables accessed by query
331  self::$QueryResultCache[$QueryString]["TablesAccessed"] =
332  $this->TablesAccessed($QueryString);
333  }
334 
335  # if rows found
336  if ($this->NumRows > 0)
337  {
338  # load query results
339  for ($Row = 0; $Row < $this->NumRows; $Row++)
340  {
341  $this->QueryResults[$Row] =
342  mysql_fetch_assoc($this->QueryHandle);
343  }
344 
345  # cache query results
346  self::$QueryResultCache[$QueryString] = $this->QueryResults;
347  }
348  else
349  {
350  # clear local query results
351  unset($this->QueryResults);
352  }
353 
354  # cache number of rows
355  self::$QueryResultCache[$QueryString]["NumRows"] = $this->NumRows;
356 
357  # set flag to indicate that query results should be retrieved from cache
358  $this->GetResultsFromCache = TRUE;
359  }
360  }
361  }
362  else
363  {
364  # if advanced caching is enabled
365  if (self::$AdvancedCachingFlag)
366  {
367  # if table modified by statement is known
368  $TableModified = $this->TableModified($QueryString);
369  if ($TableModified)
370  {
371  # for each cached query
372  foreach (self::$QueryResultCache
373  as $CachedQueryString => $CachedQueryResult)
374  {
375  # if we know what tables were accessed
376  if ($CachedQueryResult["TablesAccessed"])
377  {
378  # if tables accessed include the one we may modify
379  if (in_array($TableModified, $CachedQueryResult["TablesAccessed"]))
380  {
381  # clear cached query results
382  unset($GLOBALS["APDBQueryResultCache"][$CachedQueryString]);
383  }
384  }
385  else
386  {
387  # clear cached query results
388  unset($GLOBALS["APDBQueryResultCache"][$CachedQueryString]);
389  }
390  }
391  }
392  else
393  {
394  # clear entire query result cache
395  self::$QueryResultCache = array();
396  }
397  }
398  else
399  {
400  # clear entire query result cache
401  self::$QueryResultCache = array();
402  }
403 
404  # execute SQL statement
405  $this->QueryHandle = $this->RunQuery($QueryString);
406  if ($this->QueryHandle === FALSE) { return FALSE; }
407 
408  # set flag to indicate that query results should not be retrieved from cache
409  $this->GetResultsFromCache = FALSE;
410  }
411 
412  # reset row counter
413  $this->RowCounter = 0;
414 
415  # increment query counter
416  self::$QueryCounter++;
417  }
418  else
419  {
420  # execute SQL statement
421  $this->QueryHandle = $this->RunQuery($QueryString);
422  if ($this->QueryHandle === FALSE) { return FALSE; }
423  }
424 
425  if (($FieldName != "") && ($this->QueryHandle != FALSE))
426  {
427  return $this->FetchField($FieldName);
428  }
429  else
430  {
431  return $this->QueryHandle;
432  }
433  }
434 
447  function ExecuteQueriesFromFile($FileName)
448  {
449  # open file
450  $FHandle = fopen($FileName, "r");
451 
452  # if file open succeeded
453  if ($FHandle !== FALSE)
454  {
455  # while lines left in file
456  $Query = "";
457  $QueryCount = 0;
458  while (!feof($FHandle))
459  {
460  # read in line from file
461  $Line = fgets($FHandle, 32767);
462 
463  # trim whitespace from line
464  $Line = trim($Line);
465 
466  # if line is not empty and not a comment
467  if (!preg_match("/^#/", $Line)
468  && !preg_match("/^--/", $Line)
469  && strlen($Line))
470  {
471  # add line to current query
472  $Query .= " ".$Line;
473 
474  # if line completes a query
475  if (preg_match("/;$/", $Line))
476  {
477  # run query
478  $QueryCount++;
479  $Result = $this->Query($Query);
480  $Query = "";
481 
482  # if query resulted in an error that is not ignorable
483  if ($Result === FALSE)
484  {
485  # stop processing queries and set error code
486  $QueryCount = NULL;
487  break;
488  }
489  }
490  }
491  }
492 
493  # close file
494  fclose($FHandle);
495  }
496 
497  # return number of executed queries to caller
498  return $QueryCount;
499  }
500 
506  function QueryErrMsg()
507  {
508  return $this->ErrMsg;
509  }
510 
516  function QueryErrNo()
517  {
518  return $this->ErrNo;
519  }
520 
527  static function DisplayQueryErrors($NewValue = NULL)
528  {
529  if ($NewValue !== NULL) { self::$DisplayErrors = $NewValue; }
530  return self::$DisplayErrors;
531  }
532 
537  function NumRowsSelected()
538  {
539  # if caching is enabled and query was cached
540  if (self::$CachingFlag && $this->GetResultsFromCache)
541  {
542  # return cached number of rows to caller
543  return $this->NumRows;
544  }
545  else
546  {
547  # call to this method after an unsuccessful query
548  if (!is_resource($this->QueryHandle))
549  {
550  return 0;
551  }
552 
553  # retrieve number of rows and return to caller
554  return mysql_num_rows($this->QueryHandle);
555  }
556  }
557 
563  function NumRowsAffected()
564  {
565  # call to this method after an unsuccessful query
566  if (!is_resource($this->QueryHandle))
567  {
568  return 0;
569  }
570 
571  # retrieve number of rows and return to caller
572  return mysql_affected_rows($this->Handle);
573  }
574 
580  function FetchRow()
581  {
582  # if caching is enabled and query was cached
583  if (self::$CachingFlag && $this->GetResultsFromCache)
584  {
585  # if rows left to return
586  if ($this->RowCounter < $this->NumRows)
587  {
588  # retrieve row from cache
589  $Result = $this->QueryResults[$this->RowCounter];
590 
591  # increment row counter
592  $this->RowCounter++;
593  }
594  else
595  {
596  # return nothing
597  $Result = FALSE;
598  }
599  }
600  else
601  {
602  # call to this method after successful query
603  if (is_resource($this->QueryHandle))
604  {
605  $Result = mysql_fetch_assoc($this->QueryHandle);
606  }
607 
608  # call to this method after unsuccessful query
609  else
610  {
611  $Result = FALSE;
612  }
613  }
614 
615  # return row to caller
616  return $Result;
617  }
618 
625  function FetchRows($NumberOfRows = NULL)
626  {
627  # assume no rows will be returned
628  $Result = array();
629 
630  # for each available row
631  $RowsFetched = 0;
632  while ((($RowsFetched < $NumberOfRows) || ($NumberOfRows == NULL))
633  && ($Row = $this->FetchRow()))
634  {
635  # add row to results
636  $Result[] = $Row;
637  $RowsFetched++;
638  }
639 
640  # return array of rows to caller
641  return $Result;
642  }
643 
660  function FetchColumn($FieldName, $IndexFieldName = NULL)
661  {
662  $Array = array();
663  while ($Record = $this->FetchRow())
664  {
665  if ($IndexFieldName != NULL)
666  {
667  $Array[$Record[$IndexFieldName]] = $Record[$FieldName];
668  }
669  else
670  {
671  $Array[] = $Record[$FieldName];
672  }
673  }
674  return $Array;
675  }
676 
685  function FetchField($FieldName)
686  {
687  $Record = $this->FetchRow();
688  return isset($Record[$FieldName]) ? $Record[$FieldName] : NULL;
689  }
690 
697  function LastInsertId()
698  {
699  return (int)$this->Query(
700  "SELECT LAST_INSERT_ID() AS InsertId",
701  "InsertId");
702  }
703 
718  function UpdateValue(
719  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
720  {
721  # expand condition if supplied
722  if ($Condition != NULL) { $Condition = " WHERE ".$Condition; }
723 
724  # read cached record from database if not already loaded
725  if (!isset($CachedRecord))
726  {
727  $this->Query("SELECT * FROM `".$TableName."` ".$Condition);
728  $CachedRecord = $this->FetchRow();
729  }
730 
731  # if new value supplied
732  if ($NewValue !== DB_NOVALUE)
733  {
734  # update value in database
735  $this->Query("UPDATE `".$TableName."` SET `".$FieldName."` = "
736  .(($NewValue === NULL) ? "NULL" : "'"
737  .mysql_real_escape_string($NewValue)."'")
738  .$Condition);
739 
740  # update value in cached record
741  $CachedRecord[$FieldName] = $NewValue;
742  }
743 
744  # return value from cached record to caller
745  return isset($CachedRecord[$FieldName])
746  ? $CachedRecord[$FieldName] : NULL;
747  }
748 
765  function UpdateIntValue(
766  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
767  {
768  return $this->UpdateValue($TableName, $FieldName,
769  (($NewValue === DB_NOVALUE) ? DB_NOVALUE : (int)$NewValue),
770  $Condition, $CachedRecord);
771  }
772 
790  $TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
791  {
792  return $this->UpdateValue($TableName, $FieldName,
793  (($NewValue === DB_NOVALUE) ? DB_NOVALUE : (float)$NewValue),
794  $Condition, $CachedRecord);
795  }
796 
797  /*@)*/ /* Data Manipulation */ /*@(*/
799 
806  function LogComment($String)
807  {
808  $this->Query("-- ".$String);
809  }
810 
816  function TableExists($TableName)
817  {
818  $this->Query("SHOW TABLES LIKE '".addslashes($TableName)."'");
819  return $this->NumRowsSelected() ? TRUE : FALSE;
820  }
821 
828  function FieldExists($TableName, $FieldName)
829  {
830  $this->Query("DESC ".$TableName);
831  while ($CurrentFieldName = $this->FetchField("Field"))
832  {
833  if ($CurrentFieldName == $FieldName) { return TRUE; }
834  }
835  return FALSE;
836  }
837 
844  function GetFieldType($TableName, $FieldName)
845  {
846  $this->Query("DESC ".$TableName);
847  return $this->FetchField("Type");
848  }
849 
855  static function QueryDebugOutput($NewSetting)
856  {
857  self::$QueryDebugOutputFlag = $NewSetting;
858  }
859 
865  static function NumQueries()
866  {
867  return self::$QueryCounter;
868  }
869 
876  static function NumCacheHits()
877  {
878  return self::$CachedQueryCounter;
879  }
880 
886  static function CacheHitRate()
887  {
888  if (self::$QueryCounter)
889  {
890  return (self::$CachedQueryCounter / self::$QueryCounter) * 100;
891  }
892  else
893  {
894  return 0;
895  }
896  }
897 
898  /*@)*/ /* Miscellaneous */
899 
900  # ---- PRIVATE INTERFACE -------------------------------------------------
901 
902  protected $DBUserName;
903  protected $DBPassword;
904  protected $DBHostName;
905  protected $DBName;
906 
907  private $Handle;
908  private $QueryHandle;
909  private $QueryResults;
910  private $RowCounter;
911  private $NumRows;
912  private $GetResultsFromCache;
913  private $ErrorIgnored = FALSE;
914  private $ErrorsToIgnore = NULL;
915  private $ErrMsg = NULL;
916  private $ErrNo = NULL;
917 
918  private static $DisplayErrors = FALSE;
919 
920  private static $GlobalDBUserName;
921  private static $GlobalDBPassword;
922  private static $GlobalDBHostName;
923  private static $GlobalDBName;
924 
925  # debug output flag
926  private static $QueryDebugOutputFlag = FALSE;
927  # flag for whether caching is turned on
928  private static $CachingFlag = TRUE;
929  # query result advanced caching flag
930  private static $AdvancedCachingFlag = FALSE;
931  # global cache for query results
932  private static $QueryResultCache = array();
933  # stats counters
934  private static $QueryCounter = 0;
935  private static $CachedQueryCounter = 0;
936  # database connection link handles
937  private static $ConnectionHandles = array();
938 
944  private function IsReadOnlyStatement($QueryString)
945  {
946  return preg_match("/^[ ]*SELECT /i", $QueryString) ? TRUE : FALSE;
947  }
948 
955  private function TableModified($QueryString)
956  {
957  # assume we're not going to be able to determine table
958  $TableName = FALSE;
959 
960  # split query into pieces
961  $QueryString = trim($QueryString);
962  $Words = preg_split("/\s+/", $QueryString);
963 
964  # if INSERT statement
965  $WordIndex = 1;
966  if (strtoupper($Words[0]) == "INSERT")
967  {
968  # skip over modifying keywords
969  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
970  || (strtoupper($Words[$WordIndex]) == "DELAYED")
971  || (strtoupper($Words[$WordIndex]) == "IGNORE")
972  || (strtoupper($Words[$WordIndex]) == "INTO"))
973  {
974  $WordIndex++;
975  }
976 
977  # next word is table name
978  $TableName = $Words[$WordIndex];
979  }
980  # else if UPDATE statement
981  elseif (strtoupper($Words[0]) == "UPDATE")
982  {
983  # skip over modifying keywords
984  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
985  || (strtoupper($Words[$WordIndex]) == "IGNORE"))
986  {
987  $WordIndex++;
988  }
989 
990  # if word following next word is SET
991  if (strtoupper($Words[$WordIndex + 1]) == "SET")
992  {
993  # next word is table name
994  $TableName = $Words[$WordIndex];
995  }
996  }
997  # else if DELETE statement
998  elseif (strtoupper($Words[0]) == "DELETE")
999  {
1000  # skip over modifying keywords
1001  while ((strtoupper($Words[$WordIndex]) == "LOW_PRIORITY")
1002  || (strtoupper($Words[$WordIndex]) == "IGNORE")
1003  || (strtoupper($Words[$WordIndex]) == "QUICK"))
1004  {
1005  $WordIndex++;
1006  }
1007 
1008  # if next term is FROM
1009  if (strtoupper($Words[$WordIndex]) == "FROM")
1010  {
1011  # next word is table name
1012  $WordIndex++;
1013  $TableName = $Words[$WordIndex];
1014  }
1015  }
1016 
1017  # discard table name if it looks at all suspicious
1018  if ($TableName)
1019  {
1020  if (!preg_match("/[a-zA-Z0-9]+/", $TableName))
1021  {
1022  $TableName = FALSE;
1023  }
1024  }
1025 
1026  # return table name (or lack thereof) to caller
1027  return $TableName;
1028  }
1029 
1036  private function TablesAccessed($QueryString)
1037  {
1038  # assume we're not going to be able to determine tables
1039  $TableNames = FALSE;
1040 
1041  # split query into pieces
1042  $QueryString = trim($QueryString);
1043  $Words = preg_split("/\s+/", $QueryString);
1044  $UQueryString = strtoupper($QueryString);
1045  $UWords = preg_split("/\s+/", $UQueryString);
1046 
1047  # if SELECT statement
1048  if ($UWords[0] == "SELECT")
1049  {
1050  # keep going until we hit FROM or last word
1051  $WordIndex = 1;
1052  while (($UWords[$WordIndex] != "FROM")
1053  && strlen($UWords[$WordIndex]))
1054  {
1055  $WordIndex++;
1056  }
1057 
1058  # if we hit FROM
1059  if ($UWords[$WordIndex] == "FROM")
1060  {
1061  # for each word after FROM
1062  $WordIndex++;
1063  while (strlen($UWords[$WordIndex]))
1064  {
1065  # if current word ends with comma
1066  if (preg_match("/,$/", $Words[$WordIndex]))
1067  {
1068  # strip off comma and add word to table name list
1069  $TableNames[] = substr($Words[$WordIndex], 0, -1);
1070  }
1071  else
1072  {
1073  # add word to table name list
1074  $TableNames[] = $Words[$WordIndex];
1075 
1076  # if next word is not comma
1077  $WordIndex++;
1078  if ($Words[$WordIndex] != ",")
1079  {
1080  # if word begins with comma
1081  if (preg_match("/^,/", $Words[$WordIndex]))
1082  {
1083  # strip off comma (NOTE: modifies $Words array!)
1084  $Words[$WordIndex] = substr($Words[$WordIndex], 1);
1085 
1086  # decrement index so we start with this word next pass
1087  $WordIndex--;
1088  }
1089  else
1090  {
1091  # stop scanning words (non-basic JOINs not yet handled)
1092  break;
1093  }
1094  }
1095  }
1096 
1097  # move to next word
1098  $WordIndex++;
1099  }
1100  }
1101  }
1102 
1103  # discard table names if they look at all suspicious
1104  if ($TableNames)
1105  {
1106  foreach ($TableNames as $Name)
1107  {
1108  if (!preg_match("/^[a-zA-Z0-9]+$/", $Name))
1109  {
1110  $TableNames = FALSE;
1111  break;
1112  }
1113  }
1114  }
1115 
1116  # return table name (or lack thereof) to caller
1117  return $TableNames;
1118  }
1119 
1126  private function RunQuery($QueryString)
1127  {
1128  # log query start time if debugging output is enabled
1129  if (self::$QueryDebugOutputFlag) { $QueryStartTime = microtime(TRUE); }
1130 
1131  # run query against database
1132  $this->QueryHandle = mysql_query($QueryString, $this->Handle);
1133 
1134  # print query and execution time if debugging output is enabled
1135  if (self::$QueryDebugOutputFlag)
1136  {
1137  print "DB: ".$QueryString." ["
1138  .sprintf("%.2f", microtime(TRUE) - $QueryStartTime)
1139  ."s]"."<br>\n";
1140  }
1141 
1142  # if query failed and there are errors that we can ignore
1143  if (($this->QueryHandle === FALSE) && $this->ErrorsToIgnore)
1144  {
1145  # for each pattern for an error that we can ignore
1146  foreach ($this->ErrorsToIgnore as $SqlPattern => $ErrMsgPattern)
1147  {
1148  # if error matches pattern
1149  $ErrorMsg = mysql_error($this->Handle);
1150  if (preg_match($SqlPattern, $QueryString)
1151  && preg_match($ErrMsgPattern, $ErrorMsg))
1152  {
1153  # set return value to indicate error was ignored
1154  $this->QueryHandle = TRUE;
1155 
1156  # set internal flag to indicate that an error was ignored
1157  $this->ErrorIgnored = $ErrorMsg;
1158 
1159  # stop looking at patterns
1160  break;
1161  }
1162  }
1163  }
1164 
1165  # if query failed
1166  if ($this->QueryHandle === FALSE)
1167  {
1168  # clear stored value for number of rows retrieved
1169  $this->NumRows = 0;
1170 
1171  # retrieve error info
1172  $this->ErrMsg = mysql_error($this->Handle);
1173  $this->ErrNo = mysql_errno($this->Handle);
1174 
1175  # if we are supposed to be displaying errors
1176  if (self::$DisplayErrors)
1177  {
1178  # print error info
1179  print("<b>SQL Error:</b> <i>".$this->ErrMsg
1180  ."</i> (".$this->ErrNo.")<br/>\n");
1181  print("<b>SQL Statement:</b> <i>"
1182  .htmlspecialchars($QueryString)."</i><br/>\n");
1183 
1184  # retrieve execution trace that got us to this point
1185  $Trace = debug_backtrace();
1186 
1187  # remove current context from trace
1188  array_shift($Trace);
1189 
1190  # make sure file name and line number are available
1191  foreach ($Trace as $Index => $Loc)
1192  {
1193  if (!array_key_exists("file", $Loc))
1194  {
1195  $Trace[$Index]["file"] = "UNKNOWN";
1196  }
1197  if (!array_key_exists("line", $Loc))
1198  {
1199  $Trace[$Index]["line"] = "??";
1200  }
1201  }
1202 
1203  # determine length of leading path common to all file names in trace
1204  $LocString = "";
1205  $OurFile = __FILE__;
1206  $PrefixLen = 9999;
1207  foreach ($Trace as $Loc)
1208  {
1209  if ($Loc["file"] != "UNKNOWN")
1210  {
1211  $Index = 0;
1212  while ($Loc["file"][$Index] == $OurFile[$Index])
1213  { $Index++; }
1214  $PrefixLen = min($PrefixLen, $Index);
1215  }
1216  }
1217 
1218  foreach ($Trace as $Loc)
1219  {
1220  $Sep = "";
1221  $ArgString = "";
1222  foreach ($Loc["args"] as $Arg)
1223  {
1224  $ArgString .= $Sep;
1225  switch (gettype($Arg))
1226  {
1227  case "boolean":
1228  $ArgString .= $Arg ? "TRUE" : "FALSE";
1229  break;
1230 
1231  case "integer":
1232  case "double":
1233  $ArgString .= $Arg;
1234  break;
1235 
1236  case "string":
1237  $ArgString .= '"<i>'.htmlspecialchars(substr($Arg, 0, 40))
1238  .((strlen($Arg) > 40) ? "..." : "").'</i>"';
1239  break;
1240 
1241  case "array":
1242  case "resource":
1243  case "NULL":
1244  $ArgString .= strtoupper(gettype($Arg));
1245  break;
1246 
1247  case "object":
1248  $ArgString .= get_class($Arg);
1249  break;
1250 
1251  case "unknown type":
1252  $ArgString .= "UNKNOWN";
1253  break;
1254  }
1255  $Sep = ",";
1256  }
1257  $Loc["file"] = substr($Loc["file"], $PrefixLen);
1258  $LocString .= "&nbsp;&nbsp;";
1259  if (array_key_exists("class", $Loc))
1260  { $LocString .= $Loc["class"]."::"; }
1261  $LocString .= $Loc["function"]."(".$ArgString.")"
1262  ." - ".$Loc["file"].":".$Loc["line"]
1263  ."<br>\n";
1264  }
1265  print("<b>Trace:</b><br>\n".$LocString);
1266  }
1267  }
1268  return $this->QueryHandle;
1269  }
1270 }
1271 
1272 # define return values (numerical values correspond to MySQL error codes)
1273 define("DB_OKAY", 0);
1274 define("DB_ERROR", 1);
1275 define("DB_ACCESSDENIED", 2);
1276 define("DB_UNKNOWNDB", 3);
1277 define("DB_UNKNOWNTABLE", 4);
1278 define("DB_SYNTAXERROR", 5);
1279 define("DB_DBALREADYEXISTS", 6);
1280 define("DB_DBDOESNOTEXIST", 7);
1281 define("DB_DISKFULL", 8);
1282 
1283 # define value to designate omitted arguments (so DB values can be set to NULL)
1284 define("DB_NOVALUE", "!-_-_-DB_NOVALUE-_-_-!");
1285 
1286 # MySQL error code mapping
1288  1045 => DB_ACCESSDENIED,
1289  1049 => DB_UNKNOWNDB,
1290  1046 => DB_UNKNOWNTABLE,
1291  1064 => DB_SYNTAXERROR,
1292  1007 => DB_DBALREADYEXISTS, # ? (not sure)
1293  1008 => DB_DBDOESNOTEXIST, # ? (not sure)
1294  1021 => DB_DISKFULL, # ? (not sure)
1295  );
1296 
1297 # date() format for SQL dates
1298 define("DATE_SQL", "Y-m-d H:i:s");
1299 
QueryErrMsg()
Get most recent error message text set by Query().
static Caching($NewSetting=NULL)
Get or set whether query result caching is currently enabled.
const DB_UNKNOWNDB
static SetGlobalDatabaseName($DatabaseName)
Set default database name.
SetDefaultStorageEngine($Engine)
Set default database storage engine.
ExecuteQueriesFromFile($FileName)
Execute queries from specified file.
UpdateIntValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set an integer value in the database.
const DB_SYNTAXERROR
SQL database abstraction object with smart query caching.
GetServerVersion()
Get database server version number.
SetQueryErrorsToIgnore($ErrorsToIgnore, $NormalizeWhitespace=TRUE)
Set query errors to ignore.
const DB_NOVALUE
DBUserName()
Get name used to connect with database server.
Database($UserName=NULL, $Password=NULL, $DatabaseName=NULL, $HostName=NULL)
Object constructor.
FetchRow()
Get next database row retrieved by most recent query.
LastInsertId()
Get ID of row added by the last SQL "INSERT" statement.
TableExists($TableName)
Get whether specified table exists.
static SetGlobalServerInfo($UserName, $Password, $HostName="localhost")
const DB_DBALREADYEXISTS
PHP
Definition: OAIClient.php:39
GetFieldType($TableName, $FieldName)
Get field (column) type.
NumRowsSelected()
Get number of rows returned by last SELECT or SHOW query.
FetchRows($NumberOfRows=NULL)
Get specified number of database rows retrieved by most recent query.
static QueryDebugOutput($NewSetting)
Enable or disable debugging output for queries.
Query($QueryString, $FieldName="")
Query database (with caching if enabled).
FetchField($FieldName)
Pull next row from last DB query and get a specific value from that row.
FieldExists($TableName, $FieldName)
Get whether specified field exists in specified table.
static NumCacheHits()
Get the number of queries that have resulted in cache hits since program execution began...
FetchColumn($FieldName, $IndexFieldName=NULL)
Get all available values for specified database field retrieved by most recent query.
DBHostName()
Get host name of system on which database server resides.
NumRowsAffected()
Get number of rows affected by last INSERT, UPDATE, REPLACE, or DELETE query.
const DB_ACCESSDENIED
UpdateFloatValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set a float value in the database.
QueryErrNo()
Get most recent error code set by Query().
$APDBErrorCodeMappings
UpdateValue($TableName, $FieldName, $NewValue, $Condition, &$CachedRecord)
A convenience function to get or set a value in the database.
static AdvancedCaching($NewSetting=NULL)
Get or set whether advanced query result cachine is currently enabled.
static DisplayQueryErrors($NewValue=NULL)
Get/set whether Query() errors will be displayed.
static CacheHitRate()
Get the ratio of query cache hits to queries as a percentage.
IgnoredError()
Check whether an error was ignored by the most recent query.
DBName()
Get current database name.
LogComment($String)
Peform query that consists of SQL comment statement.
__wakeup()
Restore database connection when unserialized.
static NumQueries()
Get the number of queries that have been run since program execution began.
const DB_DBDOESNOTEXIST
const DB_UNKNOWNTABLE
const DB_DISKFULL