ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilDB.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
7 //pear MDB2 abstraction layer
8 include_once ("MDB2.php");
9 
10 define("DB_FETCHMODE_ASSOC", MDB2_FETCHMODE_ASSOC);
11 define("DB_FETCHMODE_OBJECT", MDB2_FETCHMODE_OBJECT);
12 
13 //echo "-".DB_FETCHMODE_ASSOC."-";
14 //echo "+".DB_FETCHMODE_OBJECT."+";
15 
16 
28 abstract class ilDB extends PEAR
29 {
30  const LOCK_WRITE = 1;
31  const LOCK_READ = 2;
32 
33 
40 
45  var $db;
46 
51  var $result;
52 
53 
54  var $allowed_attributes = array(
55  "text" => array("length", "notnull", "default", "fixed"),
56  "integer" => array("length", "notnull", "default", "unsigned"),
57  "float" => array("notnull", "default"),
58  "date" => array("notnull", "default"),
59  "time" => array("notnull", "default"),
60  "timestamp" => array("notnull", "default"),
61  "clob" => array("notnull", "default"),
62  "blob" => array("notnull", "default")
63  );
64 
65  var $sub_type;
66 
74  function ilDB()
75  {
76  }
77 
83  function setDBUser($a_user)
84  {
85  $this->db_user = $a_user;
86  }
87 
93  function getDBUser()
94  {
95  return $this->db_user;
96  }
97 
103  function setDBPort($a_port)
104  {
105  $this->db_port = $a_port;
106  }
107 
113  function getDBPort()
114  {
115  return $this->db_port;
116  }
117 
123  function setDBHost($a_host)
124  {
125  $this->db_host = $a_host;
126  }
127 
133  function getDBHost()
134  {
135  return $this->db_host;
136  }
137 
143  function setDBPassword($a_password)
144  {
145  $this->db_password = $a_password;
146  }
147 
153  function getDBPassword()
154  {
155  return $this->db_password;
156  }
157 
163  function setDBName($a_name)
164  {
165  $this->db_name = $a_name;
166  }
167 
173  function getDBName()
174  {
175  return $this->db_name;
176  }
177 
181  abstract function getDSN();
182 
186  function getDBVersion()
187  {
188  return "Unknown";
189  }
190 
194  abstract function getDBType();
195 
202  abstract static function getReservedWords();
203 
204 
209  public function enableResultBuffering($a_status)
210  {
211  $this->db->setOption('result_buffering',$a_status);
212  }
213 
218  function initFromIniFile($tmpClientIniFile = null)
219  {
220  global $ilClientIniFile;
221 
222  //overwrite global client ini file if local parameter is set
223  if (is_object($tmpClientIniFile))
224  $clientIniFile = $tmpClientIniFile;
225  else
226  $clientIniFile = $ilClientIniFile;
227 
228  if (is_object($clientIniFile ))
229  {
230  $this->setDBUser($clientIniFile ->readVariable("db", "user"));
231  $this->setDBHost($clientIniFile ->readVariable("db", "host"));
232  $this->setDBPort($clientIniFile ->readVariable("db", "port"));
233  $this->setDBPassword($clientIniFile ->readVariable("db", "pass"));
234  $this->setDBName($clientIniFile ->readVariable("db", "name"));
235  }
236  }
237 
241  function connect($a_return_false_for_error = false)
242  {
243  //set up error handling
244  $this->error_class = new ilErrorHandling();
245  $this->setErrorHandling(PEAR_ERROR_CALLBACK, array($this->error_class,'errorHandler'));
246 //echo $this->getDSN();
247  //check dsn
248  if ($this->getDSN() == "")
249  {
250  $this->raisePearError("No DSN given");
251  }
252 
253  //connect to database
254  $this->doConnect();
255 
256  if ($a_return_false_for_error && MDB2::isError($this->db))
257  {
258  return false;
259  }
260 
261  $this->loadMDB2Extensions();
262 
263  // set empty value portability to PEAR::DB behaviour
264  if (!$this->isDbError($this->db))
265  {
266  $this->db->setOption('portability', MDB2_PORTABILITY_ALL);
267  }
268  //check error
269  $this->handleError($this->db);
270 
271  // anything, that must be done to initialize the connection
272  $this->initConnection();
273 
274  return true;
275  }
276 
280  function doConnect()
281  {
282  $this->db = MDB2::connect($this->getDSN(),
283  array("use_transactions" => true));
284  }
285 
289  function disconnect()
290  {
291  $this->db->disconnect();
292  }
293 
294  //
295  // General and MDB2 related functions
296  //
297 
301  protected function initConnection()
302  {
303  }
304 
311  function getHostDSN()
312  {
313  return false;
314  }
315 
320  function connectHost()
321  {
322  //set up error handling
323  $this->error_class = new ilErrorHandling();
324  $this->setErrorHandling(PEAR_ERROR_CALLBACK, array($this->error_class,'errorHandler'));
325 
326  //check dsn
327  if ($this->getHostDSN() == "")
328  {
329  $this->raisePearError("No Host DSN given");
330  }
331 
332  //connect to database
333  $this->db = MDB2::connect($this->getHostDSN(),
334  array("use_transactions" => true));
335  if ($a_return_false_for_error && MDB2::isError($this->db))
336  {
337  return false;
338  }
339 
340  $this->loadMDB2Extensions();
341 
342  // set empty value portability to PEAR::DB behaviour
343  if (!$this->isDbError($this->db))
344  {
345  $cur = ($this->db->getOption("portability") & MDB2_PORTABILITY_EMPTY_TO_NULL);
346  $this->db->setOption("portability", $this->db->getOption("portability") - $cur);
347 
348  $cur = ($this->db->getOption("portability") & MDB2_PORTABILITY_FIX_CASE);
349  $this->db->setOption("portability", $this->db->getOption("portability") - $cur);
350  }
351 
352  //check error
353  $this->handleError($this->db);
354 
355  // anything, that must be done to initialize the connection
356  $this->initHostConnection();
357 
358  return true;
359  }
360 
364  protected function initHostConnection()
365  {
366  }
367 
368  function supportsFulltext()
369  {
370  return false;
371  }
372 
379  function handleError($a_res, $a_info = "", $a_level = "")
380  {
381  global $ilLog;
382 
383  if (MDB2::isError($a_res))
384  {
385  if ($a_level == "")
386  {
387  $a_level = $this->error_class->FATAL;
388  }
389 
390  // Show stack
391  try
392  {
393  throw new Exception();
394  }
395  catch(Exception $e)
396  {
397  $stack = $e->getTraceAsString();
398  }
399 
400  if(is_object($ilLog))
401  $ilLog->logStack();
402  $this->raisePearError("ilDB Error: ".$a_info."<br />".
403  $a_res->getMessage()."<br />".$a_res->getUserInfo()."<br />".$stack, $a_level);
404 
405  }
406 
407  return $a_res;
408  }
409 
413  function raisePearError($a_message, $a_level = "")
414  {
415  if ($a_level == "")
416  {
417  $a_level = $this->error_class->FATAL;
418  }
419 //echo "<br>-ilDB:raising-$a_message-$a_level-";
420  $this->raiseError($a_message, $a_level);
421  }
422 
428  protected function loadMDB2Extensions()
429  {
430  if (!$this->isDbError($this->db))
431  {
432  $this->db->loadModule('Extended');
433  define('DB_AUTOQUERY_SELECT',MDB2_AUTOQUERY_SELECT);
434  define('DB_AUTOQUERY_INSERT',MDB2_AUTOQUERY_INSERT);
435  define('DB_AUTOQUERY_UPDATE',MDB2_AUTOQUERY_UPDATE);
436  define('DB_AUTOQUERY_DELETE',MDB2_AUTOQUERY_DELETE);
437  }
438  }
439 
443  static function isDbError($a_res)
444  {
445  return MDB2::isError($a_res);
446  }
447 
448  //
449  // Data Definition Methods
450  //
451 
455  function createDatabase($a_name, $a_charset = "utf8", $a_collation = "")
456  {
457  if ($a_collation != "")
458  {
459  $sql = "CREATE DATABASE ".$a_name.
460  " CHARACTER SET ".$a_charset.
461  " COLLATE ".$a_collation;
462  }
463  else
464  {
465  $sql = "CREATE DATABASE ".$a_name.
466  " CHARACTER SET ".$a_charset;
467  }
468 
469  return $this->query($sql, false);
470  }
471 
472 
480  function createTable($a_name, $a_definition_array, $a_drop_table = false,
481  $a_ignore_erros = false)
482  {
483  // check table name
484  if (!$this->checkTableName($a_name) && !$a_ignore_erros)
485  {
486  $this->raisePearError("ilDB Error: createTable(".$a_name.")<br />".
487  $this->error_str);
488  }
489 
490  // check definition array
491  if (!$this->checkTableColumns($a_definition_array) && !$a_ignore_erros)
492  {
493  $this->raisePearError("ilDB Error: createTable(".$a_name.")<br />".
494  $this->error_str);
495  }
496 
497  if ($a_drop_table)
498  {
499  $this->dropTable($a_name, false);
500  }
501 
502  $options = $this->getCreateTableOptions();
503 
504  $manager = $this->db->loadModule('Manager');
505  $r = $manager->createTable($a_name, $a_definition_array, $options);
506 
507  return $this->handleError($r, "createTable(".$a_name.")");
508  }
509 
515  protected function getCreateTableOptions()
516  {
517  return array();
518  }
519 
526  function dropTable($a_name, $a_error_if_not_existing = true)
527  {
528  if (!$a_error_if_not_existing)
529  {
530  $tables = $this->listTables();
531  if (!in_array($a_name, $tables))
532  {
533  return;
534  }
535  }
536 
537  $manager = $this->db->loadModule('Manager');
538 
539  if ($this->getDBType() == "oracle")
540  {
541  // drop table constraints
542  $constraints = $manager->listTableConstraints($a_name);
543  $this->handleError($constraints, "dropTable(".$a_name."), listTableConstraints");
544  foreach ($constraints as $c)
545  {
546  if (substr($c, 0, 4) != "sys_")
547  {
548  $r = $manager->dropConstraint($a_name, $c);
549  $this->handleError($r, "dropTable(".$a_name."), dropConstraint");
550  }
551  }
552 
553  // drop table indexes
554  $indexes = $manager->listTableIndexes($a_name);
555  $this->handleError($indexes, "dropTable(".$a_name."), listTableIndexes");
556  foreach ($indexes as $i)
557  {
558  $r = $manager->dropIndex($a_name, $i);
559  $this->handleError($r, "dropTable(".$a_name."), dropIndex");
560  }
561  }
562 
563  // drop sequence
564  $seqs = $manager->listSequences();
565  if (in_array($a_name, $seqs))
566  {
567  $r = $manager->dropSequence($a_name);
568  $this->handleError($r, "dropTable(".$a_name."), dropSequence");
569  }
570 
571  // drop table
572  $r = $manager->dropTable($a_name);
573 
574  return $this->handleError($r, "dropTable(".$a_name.")");
575  }
576 
582  function alterTable($a_name, $a_changes)
583  {
584  if ($a_options == "")
585  {
586  $a_options = array();
587  }
588 
589  $manager = $this->db->loadModule('Manager');
590  $r = $manager->alterTable($a_name, $a_changes, false);
591 
592  return $this->handleError($r, "alterTable(".$a_name.")");
593  }
594 
603  function addTableColumn($a_table, $a_column, $a_attributes)
604  {
605 
606  $manager = $this->db->loadModule('Manager');
607 
608  if (!$this->checkColumnName($a_column))
609  {
610  $this->raisePearError("ilDB Error: addTableColumn(".$a_table.", ".$a_column.")<br />".
611  $this->error_str);
612  }
613  if (!$this->checkColumnDefinition($a_attributes))
614  {
615  $this->raisePearError("ilDB Error: addTableColumn(".$a_table.", ".$a_column.")<br />".
616  $this->error_str);
617  }
618 
619  $changes = array(
620  "add" => array(
621  $a_column => $a_attributes
622  )
623  );
624 
625  $r = $manager->alterTable($a_table, $changes, false);
626 
627  return $this->handleError($r, "addTableColumn(".$a_table.", ".$a_column.")");
628  }
629 
637  function dropTableColumn($a_table, $a_column)
638  {
639 
640  $manager = $this->db->loadModule('Manager');
641 
642  $changes = array(
643  "remove" => array(
644  $a_column => array()
645  )
646  );
647 
648  $r = $manager->alterTable($a_table, $changes, false);
649 
650  return $this->handleError($r, "dropTableColumn(".$a_table.", ".$a_column.")");
651  }
652 
661  function modifyTableColumn($a_table, $a_column, $a_attributes)
662  {
663  $manager = $this->db->loadModule('Manager');
664  $reverse = $this->db->loadModule('Reverse');
665  $def = $reverse->getTableFieldDefinition($a_table, $a_column);
666 
667  $this->handleError($def, "modifyTableColumn(".$a_table.")");
668 
669  if (is_file("./Services/Database/classes/class.ilDBAnalyzer.php"))
670  {
671  include_once("./Services/Database/classes/class.ilDBAnalyzer.php");
672  }
673  else
674  {
675  include_once("../Services/Database/classes/class.ilDBAnalyzer.php");
676  }
677  $analyzer = new ilDBAnalyzer();
678  $best_alt = $analyzer->getBestDefinitionAlternative($def);
679  $def = $def[$best_alt];
680  unset($def["nativetype"]);
681  unset($def["mdb2type"]);
682 
683  // check attributes
684  $type = ($a_attributes["type"] != "")
685  ? $a_attributes["type"]
686  : $def["type"];
687  foreach ($def as $k => $v)
688  {
689  if ($k != "type" && !in_array($k, $this->allowed_attributes[$type]))
690  {
691  unset($def[$k]);
692  }
693  }
694  $check_array = $def;
695  foreach ($a_attributes as $k => $v)
696  {
697  $check_array[$k] = $v;
698  }
699  if (!$this->checkColumnDefinition($check_array, true))
700  {
701  $this->raisePearError("ilDB Error: modifyTableColumn(".$a_table.", ".$a_column.")<br />".
702  $this->error_str);
703  }
704 
705  // oracle workaround: do not set null, if null already given
706  if ($this->getDbType() == "oracle")
707  {
708  if ($def["notnull"] == true && ($a_attributes["notnull"] == true
709  || !isset($a_attributes["notnull"])))
710  {
711  unset($def["notnull"]);
712  unset($a_attributes["notnull"]);
713  }
714  if ($def["notnull"] == false && ($a_attributes["notnull"] == false
715  || !isset($a_attributes["notnull"])))
716  {
717  unset($def["notnull"]);
718  unset($a_attributes["notnull"]);
719  }
720  }
721  foreach ($a_attributes as $a => $v)
722  {
723  $def[$a] = $v;
724  }
725 
726  $a_attributes["definition"] = $def;
727 
728  $changes = array(
729  "change" => array(
730  $a_column => $a_attributes
731  )
732  );
733 
734  $r = $manager->alterTable($a_table, $changes, false);
735 
736  return $this->handleError($r, "modifyTableColumn(".$a_table.")");
737  }
738 
747  function renameTableColumn($a_table, $a_column, $a_new_column)
748  {
749  // check table name
750  if (!$this->checkColumnName($a_new_column))
751  {
752  $this->raisePearError("ilDB Error: renameTableColumn(".$a_table.",".$a_column.",".$a_new_column.")<br />".
753  $this->error_str);
754  }
755 
756  $manager = $this->db->loadModule('Manager');
757  $reverse = $this->db->loadModule('Reverse');
758  $def = $reverse->getTableFieldDefinition($a_table, $a_column);
759 
760  $this->handleError($def, "renameTableColumn(".$a_table.",".$a_column.",".$a_new_column.")");
761 
762  if (is_file("./Services/Database/classes/class.ilDBAnalyzer.php"))
763  {
764  include_once("./Services/Database/classes/class.ilDBAnalyzer.php");
765  }
766  else
767  {
768  include_once("../Services/Database/classes/class.ilDBAnalyzer.php");
769  }
770 
771  $analyzer = new ilDBAnalyzer();
772  $best_alt = $analyzer->getBestDefinitionAlternative($def);
773  $def = $def[$best_alt];
774  unset($def["nativetype"]);
775  unset($def["mdb2type"]);
776 
777  $f["definition"] = $def;
778  $f["name"] = $a_new_column;
779 
780  $changes = array(
781  "rename" => array(
782  $a_column => $f
783  )
784  );
785 
786  $r = $manager->alterTable($a_table, $changes, false);
787 
788  return $this->handleError($r, "renameTableColumn(".$a_table.",".$a_column.",".$a_new_column.")");
789  }
790 
797  function renameTable($a_name, $a_new_name)
798  {
799  // check table name
800  if (!$this->checkTableName($a_new_name))
801  {
802  $this->raisePearError("ilDB Error: renameTable(".$a_name.",".$a_new_name.")<br />".
803  $this->error_str);
804  }
805 
806  $manager = $this->db->loadModule('Manager');
807  $r = $manager->alterTable($a_name, array("name" => $a_new_name), false);
808 
809  $query = "UPDATE abstraction_progress ".
810  "SET table_name = ".$this->db->quote($a_new_name,'text')." ".
811  "WHERE table_name = ".$this->db->quote($a_name,'text');
812  $this->db->query($query);
813 
814  return $this->handleError($r, "renameTable(".$a_name.",".$a_new_name.")");
815  }
816 
824  function addPrimaryKey($a_table, $a_fields)
825  {
826  $manager = $this->db->loadModule('Manager');
827 
828  $fields = array();
829  foreach ($a_fields as $f)
830  {
831  $fields[$f] = array();
832  }
833  $definition = array (
834  'primary' => true,
835  'fields' => $fields
836  );
837  $r = $manager->createConstraint($a_table,
838  $this->constraintName($a_table, $this->getPrimaryKeyIdentifier()), $definition);
839 
840  return $this->handleError($r, "addPrimaryKey(".$a_table.")");
841  }
842 
847  {
848  return "PRIMARY";
849  }
850 
857  function dropPrimaryKey($a_table)
858  {
859  $manager = $this->db->loadModule('Manager');
860 
861  $r = $manager->dropConstraint($a_table,
862  $this->constraintName($a_table, $this->getPrimaryKeyIdentifier()), true);
863 
864  return $this->handleError($r, "dropPrimaryKey(".$a_table.")");
865  }
866 
874  function addIndex($a_table, $a_fields, $a_name = "in", $a_fulltext = false)
875  {
876  $manager = $this->db->loadModule('Manager');
877 
878  // check index name
879  if (!$this->checkIndexName($a_name))
880  {
881  $this->raisePearError("ilDB Error: addIndex(".$a_table.",".$a_name.")<br />".
882  $this->error_str);
883  }
884 
885  $fields = array();
886  foreach ($a_fields as $f)
887  {
888  $fields[$f] = array();
889  }
890  $definition = array (
891  'fields' => $fields
892  );
893 
894  if (!$a_fulltext)
895  {
896  $r = $manager->createIndex($a_table, $this->constraintName($a_table, $a_name), $definition);
897  }
898  else
899  {
900  if ($this->supportsFulltext())
901  {
902  $this->addFulltextIndex($a_table, $a_fields, $a_name);
903  }
904  }
905 
906  return $this->handleError($r, "addIndex(".$a_table.")");
907  }
908 
912  function addFulltextIndex($a_table, $a_fields, $a_name = "in")
913  {
914  return false;
915  }
916 
920  function isFulltextIndex($a_table, $a_name)
921  {
922  return false;
923  }
924 
932  function dropIndex($a_table, $a_name = "in")
933  {
934  $manager = $this->db->loadModule('Manager');
935 
936  if (!$this->isFulltextIndex($a_table, $a_name))
937  {
938  $r = $manager->dropIndex($a_table, $this->constraintName($a_table, $a_name));
939  }
940  else
941  {
942  $this->dropFulltextIndex($a_table, $a_name);
943  }
944 
945  return $this->handleError($r, "dropIndex(".$a_table.")");
946  }
947 
955  function addUniqueConstraint($a_table, $a_fields, $a_name = "con")
956  {
957  $manager = $this->db->loadModule('Manager');
958 
959  // check index name
960  if (!$this->checkIndexName($a_name))
961  {
962  $this->raisePearError("ilDB Error: addUniqueConstraint(".$a_table.",".$a_name.")<br />".
963  $this->error_str);
964  }
965 
966  $fields = array();
967  foreach ($a_fields as $f)
968  {
969  $fields[$f] = array();
970  }
971  $definition = array (
972  'unique' => true,
973  'fields' => $fields
974  );
975 
976  $r = $manager->createConstraint($a_table, $this->constraintName($a_table, $a_name), $definition);
977 
978  return $this->handleError($r, "addUniqueConstraint(".$a_table.")");
979  }
980 
984  function createSequence($a_table_name, $a_start = 1)
985  {
986  $manager = $this->db->loadModule('Manager');
987 
988  $r = $manager->createSequence($a_table_name, $a_start);
989 
990  return $this->handleError($r, "createSequence(".$a_table_name.")");
991  }
992 
993 
997  function dropSequence($a_table_name)
998  {
999  $manager = $this->db->loadModule('Manager');
1000 
1001  $r = $manager->dropSequence($a_table_name);
1002 
1003  return $this->handleError($r, "dropSequence(".$a_table_name.")");
1004  }
1005 
1011  function checkTableName($a_name)
1012  {
1013  if (!preg_match ("/^[a-z]+[_a-z0-9]*$/", $a_name))
1014  {
1015  $this->error_str = "Table name must only contain _a-z0-9 and must start with a-z.";
1016  return false;
1017  }
1018 
1019  if ($this->isReservedWord($a_name))
1020  {
1021  $this->error_str = "Invalid table name '".$a_name."' (Reserved Word).";
1022  return false;
1023  }
1024 
1025  if (strtolower(substr($a_name, 0, 4)) == "sys_")
1026  {
1027  $this->error_str = "Invalid table name '".$a_name."'. Name must not start with 'sys_'.";
1028  return false;
1029  }
1030 
1031  if (strlen($a_name) > 22)
1032  {
1033  $this->error_str = "Invalid table name '".$a_name."'. Maximum table identifer lenght is 22 bytes.";
1034  return false;
1035  }
1036 
1037  return true;
1038  }
1039 
1045  function checkTableColumns($a_cols)
1046  {
1047  foreach ($a_cols as $col => $def)
1048  {
1049  if (!$this->checkColumn($col, $def))
1050  {
1051  return false;
1052  }
1053  }
1054 
1055  return true;
1056  }
1057 
1061  function checkColumn($a_col, $a_def)
1062  {
1063  if (!$this->checkColumnName($a_col))
1064  {
1065  return false;
1066  }
1067 
1068  if (!$this->checkColumnDefinition($a_def))
1069  {
1070  return false;
1071  }
1072 
1073  return true;
1074  }
1075 
1081  function checkColumnDefinition($a_def, $a_modify_mode = false)
1082  {
1083  // check valid type
1084  if (!in_array($a_def["type"], array("text", "integer", "float", "date", "time", "timestamp", "clob", "blob")))
1085  {
1086  switch ($a_def["type"])
1087  {
1088  case "boolean":
1089  $this->error_str = "Invalid column type '".$a_def["type"]."'. Use integer(1) instead.";
1090  break;
1091 
1092  case "decimal":
1093  $this->error_str = "Invalid column type '".$a_def["type"]."'. Use float or integer instead.";
1094  break;
1095 
1096  default:
1097  $this->error_str = "Invalid column type '".$a_def["type"]."'. Allowed types are: ".
1098  "text, integer, float, date, time, timestamp, clob and blob.";
1099  }
1100  }
1101 
1102  // check used attributes
1104 
1105  foreach ($a_def as $k => $v)
1106  {
1107  if ($k != "type" && !in_array($k, $allowed_attributes[$a_def["type"]]))
1108  {
1109  $this->error_str = "Attribute '".$k."' is not allowed for column type '".$a_def["type"]."'.";
1110  return false;
1111  }
1112  }
1113 
1114  // type specific checks
1115  switch ($a_def["type"])
1116  {
1117  case "text":
1118  if ($a_def["length"] < 1 || $a_def["length"] > 4000)
1119  {
1120  if (!$a_modify_mode || isset($a_def["length"]))
1121  {
1122  $this->error_str = "Invalid length '".$a_def["length"]."' for type text.".
1123  " Length must be >=1 and <= 4000.";
1124  return false;
1125  }
1126  }
1127  break;
1128 
1129  case "integer":
1130  if (!in_array($a_def["length"], array(1, 2, 3, 4, 8)))
1131  {
1132  if (!$a_modify_mode || isset($a_def["length"]))
1133  {
1134  $this->error_str = "Invalid length '".$a_def["length"]."' for type integer.".
1135  " Length must be 1, 2, 3, 4 or 8 (bytes).";
1136  return false;
1137  }
1138  }
1139  if ($a_def["unsigned"])
1140  {
1141  $this->error_str = "Unsigned attribut must not be true for type integer.";
1142  return false;
1143  }
1144  break;
1145  }
1146 
1147  return true;
1148  }
1149 
1155  function checkColumnName($a_name)
1156  {
1157  if (!preg_match ("/^[a-z]+[_a-z0-9]*$/", $a_name))
1158  {
1159  $this->error_str = "Invalid column name '".$a_name."'. Column name must only contain _a-z0-9 and must start with a-z.";
1160  return false;
1161  }
1162 
1163  if ($this->isReservedWord($a_name))
1164  {
1165  $this->error_str = "Invalid column name '".$a_name."' (Reserved Word).";
1166  return false;
1167  }
1168 
1169  if (strtolower(substr($a_name, 0, 4)) == "sys_")
1170  {
1171  $this->error_str = "Invalid column name '".$a_name."'. Name must not start with 'sys_'.";
1172  return false;
1173  }
1174 
1175  if (strlen($a_name) > 30)
1176  {
1177  $this->error_str = "Invalid column name '".$a_name."'. Maximum column identifer lenght is 30 bytes.";
1178  return false;
1179  }
1180 
1181  return true;
1182  }
1183 
1189  function checkIndexName($a_name)
1190  {
1191  if (!preg_match ("/^[a-z]+[_a-z0-9]*$/", $a_name))
1192  {
1193  $this->error_str = "Invalid column name '".$a_name."'. Column name must only contain _a-z0-9 and must start with a-z.";
1194  return false;
1195  }
1196 
1197  if ($this->isReservedWord($a_name))
1198  {
1199  $this->error_str = "Invalid column name '".$a_name."' (Reserved Word).";
1200  return false;
1201  }
1202 
1203  if (strlen($a_name) > 3)
1204  {
1205  $this->error_str = "Invalid index name '".$a_name."'. Maximum index identifer lenght is 3 bytes.";
1206  return false;
1207  }
1208 
1209  return true;
1210  }
1211 
1213  {
1215  }
1216 
1222  function constraintName($a_table, $a_constraint)
1223  {
1224  return $a_constraint;
1225  }
1226 
1231  static function isReservedWord($a_word)
1232  {
1233  include_once("./Services/Database/classes/class.ilDBMySQL.php");
1234  $mysql_reserved_words = ilDBMySQL::getReservedWords();
1235  if (in_array(strtoupper($a_word), $mysql_reserved_words))
1236  {
1237  return true;
1238  }
1239  include_once("./Services/Database/classes/class.ilDBOracle.php");
1240  $oracle_reserved_words = ilDBOracle::getReservedWords();
1241  if (in_array(strtoupper($a_word), $oracle_reserved_words))
1242  {
1243  return true;
1244  }
1245  include_once("./Services/Database/classes/class.ilDBPostgreSQL.php");
1246  $postgres_reserved_words = ilDBPostgreSQL::getReservedWords();
1247  if (in_array(strtoupper($a_word), $postgres_reserved_words))
1248  {
1249  return true;
1250  }
1251  }
1252 
1253  //
1254  // Data query and manupilation functions
1255  //
1256 
1268  function query($sql, $a_handle_error = true)
1269  {
1270  global $ilBench;
1271 
1272  if (is_object($ilBench))
1273  {
1274  $ilBench->startDbBench($sql);
1275  }
1276  $r = $this->db->query($sql);
1277  if (is_object($ilBench))
1278  {
1279  $ilBench->stopDbBench();
1280  }
1281 
1282  if ($a_handle_error)
1283  {
1284  return $this->handleError($r, "query(".$sql.")");
1285  }
1286 
1287  return $r;
1288  }
1289 
1297  function queryF($a_query, $a_types, $a_values)
1298  {
1299  if (!is_array($a_types) || !is_array($a_values) ||
1300  count($a_types) != count($a_values))
1301  {
1302  $this->raisePearError("ilDB::queryF: Types and values must be arrays of same size. ($a_query)");
1303  }
1304  $quoted_values = array();
1305  foreach($a_types as $k => $t)
1306  {
1307  $quoted_values[] = $this->quote($a_values[$k], $t);
1308  }
1309  $query = vsprintf($a_query, $quoted_values);
1310 
1311  return $this->query($query);
1312  }
1313 
1321  function manipulateF($a_query, $a_types, $a_values)
1322  {
1323  if (!is_array($a_types) || !is_array($a_values) ||
1324  count($a_types) != count($a_values))
1325  {
1326  $this->raisePearError("ilDB::manipulateF: types and values must be arrays of same size. ($a_query)");
1327  }
1328  $quoted_values = array();
1329  foreach($a_types as $k => $t)
1330  {
1331  $quoted_values[] = $this->quote($a_values[$k], $t);
1332  }
1333  $query = vsprintf($a_query, $quoted_values);
1334 
1335  return $this->manipulate($query);
1336  }
1337 
1341  function logStatement($sql)
1342  {
1343  $pos1 = strpos(strtolower($sql), "from ");
1344  $table = "";
1345  if ($pos1 > 0)
1346  {
1347  $tablef = substr($sql, $pos1+5);
1348  $pos2 = strpos(strtolower($tablef), " ");
1349  if ($pos2 > 0)
1350  {
1351  $table =substr($tablef, 0, $pos2);
1352  }
1353  else
1354  {
1355  $table = $tablef;
1356  }
1357  }
1358  if (trim($table) != "")
1359  {
1360  if (!is_array($this->ttt) || !in_array($table, $this->ttt))
1361  {
1362  echo "<br>".$table;
1363  $this->ttt[] = $table;
1364  }
1365  }
1366  else
1367  {
1368  echo "<br><b>".$sql."</b>";
1369  }
1370  }
1371 
1375  function setLimit($a_limit, $a_offset = 0)
1376  {
1377  $this->db->setLimit($a_limit, $a_offset);
1378  }
1379 
1383  function nextId($a_table_name)
1384  {
1385  // we do not create missing sequences automatically here
1386  // otherwise misspelled statements result in additional tables
1387  // please create sequences explicitly in the db update script
1388  $r = $this->db->nextId($a_table_name, false);
1389 
1390  return $this->handleError($r, "nextId(".$a_table_name.")");
1391  }
1392 
1403  function manipulate($sql)
1404  {
1405  global $ilBench;
1406 
1407  if (is_object($ilBench))
1408  {
1409  $ilBench->startDbBench($sql);
1410  }
1411  $r = $this->db->exec($sql);
1412  if (is_object($ilBench))
1413  {
1414  $ilBench->stopDbBench();
1415  }
1416 
1417  return $this->handleError($r, "manipulate(".$sql.")");
1418  }
1419 
1428  function prepare($a_query, $a_types = null, $a_result_types = null)
1429  {
1430  $res = $this->db->prepare($a_query, $a_types, $a_result_types);
1431 
1432  return $this->handleError($res, "prepare(".$a_query.")");
1433  }
1434 
1443  function prepareManip($a_query, $a_types = null)
1444  {
1445  $res = $this->db->prepare($a_query, $a_types, MDB2_PREPARE_MANIP);
1446 
1447  return $this->handleError($res, "prepareManip(".$a_query.")");
1448  }
1449 
1458  function execute($a_stmt, $a_data = null)
1459  {
1460  $res = $a_stmt->execute($a_data);
1461 
1462  return $this->handleError($res, "execute(".$a_stmt->query.")");
1463  }
1464 
1474  function executeMultiple($a_stmt, $a_data)
1475  {
1476  $res = $this->db->extended->executeMultiple($a_stmt,$a_data);
1477 
1478  return $this->handleError($res, "executeMultiple(".$a_stmt->query.")");
1479  }
1480 
1487  function insert($a_table, $a_columns)
1488  {
1489  $fields = array();
1490  $field_values = array();
1491  $placeholders = array();
1492  $types = array();
1493  $values = array();
1494  $lobs = false;
1495  $lob = array();
1496  foreach ($a_columns as $k => $col)
1497  {
1498  $fields[] = $k;
1499  $placeholders[] = "%s";
1500  $placeholders2[] = ":$k";
1501  $types[] = $col[0];
1502 
1503  // integer auto-typecast (this casts bool values to integer)
1504  if ($col[0] == 'integer' && !is_null($col[1]))
1505  {
1506  $col[1] = (int) $col[1];
1507  }
1508 
1509  $values[] = $col[1];
1510  $field_values[$k] = $col[1];
1511  if ($col[0] == "blob" || $col[0] == "clob")
1512  {
1513  $lobs = true;
1514  $lob[$k] = $k;
1515  }
1516  }
1517  if ($lobs) // lobs -> use prepare execute (autoexecute broken in PEAR 2.4.1)
1518  {
1519  $st = $this->db->prepare("INSERT INTO ".$a_table." (".implode($fields,",").") VALUES (".
1520  implode($placeholders2,",").")", $types, MDB2_PREPARE_MANIP, $lob);
1521 
1522  $this->handleError($st, "insert / prepare/execute(".$a_table.")");
1523 
1524  $r = $st->execute($field_values);
1525 
1526 
1527  //$r = $this->db->extended->autoExecute($a_table, $field_values, MDB2_AUTOQUERY_INSERT, null, $types);
1528  $this->handleError($r, "insert / prepare/execute(".$a_table.")");
1529  $this->free($st);
1530  }
1531  else // if no lobs are used, take simple manipulateF
1532  {
1533  $q = "INSERT INTO ".$a_table." (".implode($fields,",").") VALUES (".
1534  implode($placeholders,",").")";
1535  $r = $this->manipulateF($q, $types, $values);
1536  }
1537  return $r;
1538  }
1539 
1548  function update($a_table, $a_columns, $a_where)
1549  {
1550  $fields = array();
1551  $field_values = array();
1552  $placeholders = array();
1553  $types = array();
1554  $values = array();
1555  $lobs = false;
1556  $lob = array();
1557  foreach ($a_columns as $k => $col)
1558  {
1559  $fields[] = $k;
1560  $placeholders[] = "%s";
1561  $placeholders2[] = ":$k";
1562  $types[] = $col[0];
1563 
1564  // integer auto-typecast (this casts bool values to integer)
1565  if ($col[0] == 'integer' && !is_null($col[1]))
1566  {
1567  $col[1] = (int) $col[1];
1568  }
1569 
1570  $values[] = $col[1];
1571  $field_values[$k] = $col[1];
1572  if ($col[0] == "blob" || $col[0] == "clob")
1573  {
1574  $lobs = true;
1575  $lob[$k] = $k;
1576  }
1577  }
1578 
1579  if ($lobs)
1580  {
1581  $q = "UPDATE ".$a_table." SET ";
1582  $lim = "";
1583  foreach ($fields as $k => $field)
1584  {
1585  $q.= $lim.$field." = ".$placeholders2[$k];
1586  $lim = ", ";
1587  }
1588  $q.= " WHERE ";
1589  $lim = "";
1590  foreach ($a_where as $k => $col)
1591  {
1592  $q.= $lim.$k." = ".$this->quote($col[1], $col[0]);
1593  $lim = " AND ";
1594  }
1595  $st = $this->db->prepare($q, $types, MDB2_PREPARE_MANIP, $lob);
1596  $r = $st->execute($field_values);
1597 
1598  //$r = $this->db->extended->autoExecute($a_table, $field_values, MDB2_AUTOQUERY_INSERT, null, $types);
1599  $this->handleError($r, "update / prepare/execute(".$a_table.")");
1600  $this->free($st);
1601  }
1602  else
1603  {
1604  foreach ($a_where as $k => $col)
1605  {
1606  $types[] = $col[0];
1607  $values[] = $col[1];
1608  $field_values[$k] = $col;
1609  }
1610  $q = "UPDATE ".$a_table." SET ";
1611  $lim = "";
1612  foreach ($fields as $k => $field)
1613  {
1614  $q.= $lim.$field." = ".$placeholders[$k];
1615  $lim = ", ";
1616  }
1617  $q.= " WHERE ";
1618  $lim = "";
1619  foreach ($a_where as $k => $col)
1620  {
1621  $q.= $lim.$k." = %s";
1622  $lim = " AND ";
1623  }
1624 
1625  $r = $this->manipulateF($q, $types, $values);
1626  }
1627  return $r;
1628  }
1629 
1637  function replace($a_table, $a_pk_columns, $a_other_columns)
1638  {
1639  // this is the mysql implementation
1640  $a_columns = array_merge($a_pk_columns, $a_other_columns);
1641  $fields = array();
1642  $field_values = array();
1643  $placeholders = array();
1644  $types = array();
1645  $values = array();
1646  $lobs = false;
1647  $lob = array();
1648  foreach ($a_columns as $k => $col)
1649  {
1650  $fields[] = $k;
1651  $placeholders[] = "%s";
1652  $placeholders2[] = ":$k";
1653  $types[] = $col[0];
1654 
1655  // integer auto-typecast (this casts bool values to integer)
1656  if ($col[0] == 'integer' && !is_null($col[1]))
1657  {
1658  $col[1] = (int) $col[1];
1659  }
1660 
1661  $values[] = $col[1];
1662  $field_values[$k] = $col[1];
1663  if ($col[0] == "blob" || $col[0] == "clob")
1664  {
1665  $lobs = true;
1666  $lob[$k] = $k;
1667  }
1668  }
1669  if ($lobs) // lobs -> use prepare execute (autoexecute broken in PEAR 2.4.1)
1670  {
1671  $st = $this->db->prepare("REPLACE INTO ".$a_table." (".implode($fields,",").") VALUES (".
1672  implode($placeholders2,",").")", $types, MDB2_PREPARE_MANIP, $lob);
1673  $this->handleError($st, "insert / prepare/execute(".$a_table.")");
1674  $r = $st->execute($field_values);
1675  //$r = $this->db->extended->autoExecute($a_table, $field_values, MDB2_AUTOQUERY_INSERT, null, $types);
1676  $this->handleError($r, "insert / prepare/execute(".$a_table.")");
1677  $this->free($st);
1678  }
1679  else // if no lobs are used, take simple manipulateF
1680  {
1681  $q = "REPLACE INTO ".$a_table." (".implode($fields,",").") VALUES (".
1682  implode($placeholders,",").")";
1683  $r = $this->manipulateF($q, $types, $values);
1684  }
1685  return $r;
1686  }
1687 
1693  function fetchAssoc($a_set)
1694  {
1695  return $a_set->fetchRow(DB_FETCHMODE_ASSOC);
1696  }
1697 
1701  function free($a_st)
1702  {
1703  return $a_st->free();
1704  }
1705 
1711  function fetchObject($a_set)
1712  {
1713  return $a_set->fetchRow(DB_FETCHMODE_OBJECT);
1714  }
1715 
1721  function numRows($a_set)
1722  {
1723  return $a_set->numRows();
1724  }
1725 
1726  //
1727  // function and clauses abstraction
1728  //
1729 
1741  function in($a_field, $a_values, $negate = false, $a_type = "")
1742  {
1743  if (count($a_values) == 0)
1744  {
1745  return " 1=2 "; // return a false statement on empty array
1746  }
1747  if ($a_type == "") // untyped: used ? for prepare/execute
1748  {
1749  $str = $a_field.(($negate) ? " NOT" : "")." IN (?".str_repeat(",?", count($a_values) - 1).")";
1750  }
1751  else // typed, use values for query/manipulate
1752  {
1753  $str = $a_field.(($negate) ? " NOT" : "")." IN (";
1754  $sep = "";
1755  foreach ($a_values as $v)
1756  {
1757  $str.= $sep.$this->quote($v, $a_type);
1758  $sep = ",";
1759  }
1760  $str.= ")";
1761  }
1762 
1763  return $str;
1764  }
1765 
1769  function addTypesToArray($a_arr, $a_type, $a_cnt)
1770  {
1771  if (!is_array($a_arr))
1772  {
1773  $a_arr = array();
1774  }
1775  if ($a_cnt > 0)
1776  {
1777  $type_arr = array_fill(0, $a_cnt, $a_type);
1778  }
1779  else
1780  {
1781  $type_arr = array();
1782  }
1783  return array_merge($a_arr, $type_arr);
1784  }
1785 
1790  function now()
1791  {
1792  return "now()";
1793  }
1794 
1795 
1805  public function concat($a_values,$a_allow_null = true)
1806  {
1807  if(!count($a_values))
1808  {
1809  return ' ';
1810  }
1811 
1812  $concat = ' CONCAT(';
1813  $first = true;
1814  foreach($a_values as $field_info)
1815  {
1816  $val = $field_info[0];
1817 
1818  if(!$first)
1819  {
1820  $concat .= ',';
1821  }
1822 
1823  if($a_allow_null)
1824  {
1825  $concat .= 'COALESCE(';
1826  }
1827  $concat .= $val;
1828 
1829  if($a_allow_null)
1830  {
1831  $concat .= ",''";
1832  $concat .= ')';
1833  }
1834 
1835  $first = false;
1836  }
1837  $concat .= ') ';
1838  return $concat;
1839  }
1840 
1847  function substr($a_exp, $a_pos = 1, $a_len = -1)
1848  {
1849  $lenstr = "";
1850  if ($a_len > -1)
1851  {
1852  $lenstr = ", ".$a_len;
1853  }
1854  return " SUBSTR(".$a_exp.", ".$a_pos.$lenstr.") ";
1855  }
1856 
1863  function upper($a_exp)
1864  {
1865  return " UPPER(".$a_exp.") ";
1866  }
1867 
1874  function lower($a_exp)
1875  {
1876  return " LOWER(".$a_exp.") ";
1877  }
1878 
1886  public function locate($a_needle,$a_string,$a_start_pos = 1)
1887  {
1888  $locate = ' LOCATE( ';
1889  $locate .= $a_needle;
1890  $locate .= ',';
1891  $locate .= $a_string;
1892  $locate .= ',';
1893  $locate .= $a_start_pos;
1894  $locate .= ') ';
1895  return $locate;
1896  }
1897 
1898 
1904  function like($a_col, $a_type, $a_value = "?", $case_insensitive = true)
1905  {
1906  if (!in_array($a_type, array("text", "clob", "blob")))
1907  {
1908  $this->raisePearError("Like: Invalid column type '".$a_type."'.", $this->error_class->FATAL);
1909  }
1910  if ($a_value == "?")
1911  {
1912  if ($case_insensitive)
1913  {
1914  return "UPPER(".$a_col.") LIKE(UPPER(?))";
1915  }
1916  else
1917  {
1918  return $a_col ." LIKE(?)";
1919  }
1920  }
1921  else
1922  {
1923  if ($case_insensitive)
1924  {
1925  // Always quote as text
1926  return " UPPER(".$a_col.") LIKE(UPPER(".$this->quote($a_value, 'text')."))";
1927  }
1928  else
1929  {
1930  // Always quote as text
1931  return " ".$a_col." LIKE(".$this->quote($a_value, 'text').")";
1932  }
1933  }
1934  }
1935 
1936 
1940  function equals($a_col, $a_value, $a_type, $a_empty_or_null = false)
1941  {
1942  if (!$a_empty_or_null || $a_value != "")
1943  {
1944  return $a_col." = ".$this->quote($a_value, $a_type);
1945  }
1946  else
1947  {
1948  return "(".$a_col." = '' OR $a_col IS NULL)";
1949  }
1950  }
1951 
1955  function equalsNot($a_col, $a_value, $a_type, $a_empty_or_null = false)
1956  {
1957  if (!$a_empty_or_null)
1958  {
1959  return $a_col." <> ".$this->quote($a_value, $a_type);
1960  }
1961  if ($a_value != "")
1962  {
1963  return "(".$a_col." <> ".$this->quote($a_value, $a_type). " OR ".
1964  $a_col." IS NULL)";
1965  }
1966  else
1967  {
1968  return "(".$a_col." <> '' AND $a_col IS NOT NULL)";
1969  }
1970  }
1971 
1978  function fromUnixtime($a_expr, $a_to_text = true)
1979  {
1980  return "FROM_UNIXTIME(".$a_expr.")";
1981  }
1982 
1986  function unixTimestamp()
1987  {
1988  return "UNIX_TIMESTAMP()";
1989  }
1990 
1994  function optimizeTable($a_table)
1995  {
1996  // needs to be overwritten in DBMS specific class
1997  // if necessary and possible
1998  }
1999 
2000  //
2001  // Schema related functions
2002  //
2003 
2010  function tableExists($a_table)
2011  {
2012  $tables = $this->listTables();
2013 
2014  if (is_array($tables))
2015  {
2016  if (in_array($a_table, $tables))
2017  {
2018  return true;
2019  }
2020  }
2021  return false;
2022  }
2023 
2031  function tableColumnExists($a_table, $a_column_name)
2032  {
2033 
2034  $column_visibility = false;
2035  $manager = $this->db->loadModule('Manager');
2036  $r = $manager->listTableFields($a_table);
2037 
2038  if (!MDB2::isError($r))
2039  {
2040  foreach($r as $field)
2041  {
2042  if ($field == $a_column_name)
2043  {
2044  $column_visibility = true;
2045  }
2046  }
2047  }
2048 
2049  return $column_visibility;
2050  }
2051 
2057  function listTables()
2058  {
2059  $manager = $this->db->loadModule('Manager');
2060  $r = $manager->listTables();
2061 
2062  if (!MDB2::isError($r))
2063  {
2064  return $r;
2065  }
2066 
2067  return false;
2068  }
2069 
2076  function sequenceExists($a_sequence)
2077  {
2078  $sequences = $this->listSequences();
2079 
2080  if (is_array($sequences))
2081  {
2082  if (in_array($a_sequence, $sequences))
2083  {
2084  return true;
2085  }
2086  }
2087  return false;
2088  }
2089 
2095  function listSequences()
2096  {
2097  $manager = $this->db->loadModule('Manager');
2098  $r = $manager->listSequences();
2099 
2100  if (!MDB2::isError($r))
2101  {
2102  return $r;
2103  }
2104 
2105  return false;
2106  }
2107 
2108 
2109  //
2110  // Quote Functions
2111  //
2112 
2116  function quote($a_query, $a_type = null)
2117  {
2118  if ($a_query == "" && is_null($a_type))
2119  {
2120  $a_query = "";
2121  }
2122 
2123  // Performance fix
2124  if($a_type == 'integer' && !is_null($a_query))
2125  {
2126  return (int) $a_query;
2127  }
2128 
2129  if ($a_type == "blob" || $a_type == "clob")
2130  {
2131  $this->raisePearError("ilDB::quote: Quoting not allowed on type '".$a_type."'. Please use ilDB->insert and ilDB->update to write clobs.", $this->error_class->FATAL);
2132  }
2133 
2134  return $this->db->quote($a_query, $a_type);
2135  }
2136 
2144  function quoteIdentifier($a_identifier)
2145  {
2146  return $this->db->quoteIdentifier($a_identifier);
2147  }
2148 
2149 
2150  //
2151  // Transaction and Locking methods
2152  //
2153 
2159  function beginTransaction()
2160  {
2161  if (!$this->db->supports('transactions'))
2162  {
2163  $this->raisePearError("ilDB::beginTransaction: Transactions are not supported.", $this->error_class->FATAL);
2164  }
2165  $res = $this->db->beginTransaction();
2166 
2167  return $this->handleError($res, "beginTransaction()");
2168  }
2169 
2173  function commit()
2174  {
2175  $res = $this->db->commit();
2176 
2177  return $this->handleError($res, "commit()");
2178  }
2179 
2183  function rollback()
2184  {
2185  $res = $this->db->rollback();
2186 
2187  return $this->handleError($res, "rollback()");
2188  }
2189 
2195  abstract public function lockTables($a_tables);
2196 
2201  abstract public function unlockTables();
2202 
2203 
2204 //
2205 //
2206 // Older functions. Must be checked.
2207 //
2208 //
2209 
2218  function autoExecute($a_tablename,$a_fields,$a_mode = MDB2_AUTOQUERY_INSERT,$a_where = false)
2219  {
2220  $res = $this->db->autoExecute($a_tablename,$a_fields,$a_mode,$a_where);
2221 
2222  return $this->handleError($res, "autoExecute(".$a_tablename.")");
2223  }
2224 
2225 //
2226 //
2227 // Deprecated functions.
2228 //
2229 //
2230 
2234  function getLastInsertId()
2235  {
2236  $res = $this->db->lastInsertId();
2237  if(MDB2::isError($res))
2238  {
2239  return false;
2240  }
2241  return $res;
2242  }
2243 
2253  function getOne($sql)
2254  {
2255  //$r = $this->db->getOne($sql);
2256  $set = $this->db->query($sql);
2257 
2258  $this->handleError($set, "getOne(".$sql.")");
2259 
2260  if (!MDB2::isError($set))
2261  {
2262  $r = $set->fetchRow(DB_FETCHMODE_ASSOC);
2263 
2264  return $r[0];
2265  }
2266  }
2267 
2277  function getRow($sql,$mode = DB_FETCHMODE_OBJECT)
2278  {
2279  $set = $this->query($sql);
2280  $r = $set->fetchRow($mode);
2281  //$r = $this->db->getrow($sql,$mode);
2282 
2283  $this->handleError($r, "getRow(".$sql.")");
2284 
2285  return $r;
2286  } //end function
2287 
2293  function setSubType($a_value)
2294  {
2295  $this->sub_type = (string)$a_value;
2296  }
2297 
2303  function getSubType()
2304  {
2305  return $this->sub_type;
2306  }
2307 
2308 } //end Class
2309 ?>