Trying to solve path disclosure weakness (CWE-200)
[testlink-ga:testlink-code.git] / lib / functions / database.class.php
1 <?php
2 /**
3  * TestLink Open Source Project - http://testlink.sourceforge.net/
4  * This script is distributed under the GNU General Public License 2 or later. 
5  * 
6  * @filesource  database.class.php
7  * @package     TestLink
8  * @author      Francisco Mancardi
9  * @author      Mantis Team
10  * @copyright   2006-2011 TestLink community 
11  * @copyright   2002-2004  Mantis Team   - mantisbt-dev@lists.sourceforge.net
12  *             (Parts of code has been adapted from Mantis BT)
13  * @link       http://www.testlink.org
14  *
15  * @internal revisions
16  * @since 1.9.12 
17
18  */
19  
20 /**
21  * IMPORTANT NOTICE
22  * As stated on ADODB documentation:
23  *
24  * ----------------------------------------------------------------------------------------------------------------------
25  * $ADODB_COUNTRECS
26  * If the database driver API does not support counting the number of records returned in a SELECT statement, 
27  * the function RecordCount() is emulated when the global variable $ADODB_COUNTRECS is set to true, which is the default.
28  * We emulate this by buffering the records, WHICH CAN TAKE UP LARGE AMOUNTS OF MEMORY FOR BIG RECORDSETS. 
29  * Set this variable to false for the best performance. 
30  * THIS VARIABLE IS CHECKED EVERY TIME A QUERY IS EXECUTED, so you can selectively choose which recordsets to count. 
31  * ----------------------------------------------------------------------------------------------------------------------
32  *
33  * this set will improve performance but have a side
34  * effect, for DBMS like POSTGRES method num_rows() will return ALWAYS -1, causing problems
35  *
36  */
37 $ADODB_COUNTRECS = TRUE;
38
39 // To use a different version of ADODB that provided with TL, use a similar bunch of lines
40 // on custom_config.inc.php
41 if( !defined('TL_ADODB_RELATIVE_PATH') )
42 {
43     define('TL_ADODB_RELATIVE_PATH','/../../third_party/adodb/adodb.inc.php' );
44 }
45 require_once( dirname(__FILE__). TL_ADODB_RELATIVE_PATH );
46 require_once( dirname(__FILE__). '/logging.inc.php' );
47
48 /**
49  * TestLink wrapper for ADODB component
50  * @package   TestLink
51  */
52 class database 
53 {
54   const CUMULATIVE=1;
55   const ONERROREXIT=1;
56   
57   var $db;
58   var $queries_array = array();
59   var $is_connected=false;
60   var $nQuery = 0;
61   var $overallDuration = 0;
62   var $dbType;
63   
64   private $logEnabled=0;
65   private $logQueries=0;
66   
67   // timer analysis
68   function microtime_float() 
69   {
70     list( $usec, $sec ) = explode( " ", microtime() );
71     return ( (float)$usec + (float)$sec );
72   }
73   
74   function setLogEnabled($value)
75   {
76       $this->logEnabled=$value ? 1 : 0;
77   }
78   
79   function getLogEnabled($value)
80   {
81       return $this->logEnabled;
82   }
83   
84   function setLogQueries($value)
85   {
86       $this->logQueries = $value ? 1 : 0;
87   }
88   
89   function getLogQueries($value)
90   {
91       return $this->logQueries;
92   }
93
94   // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher
95   function database($db_type)
96   {
97     $this->dbType = $adodb_driver = $db_type;
98     $fetch_mode = ADODB_FETCH_ASSOC;
99     
100     // added to reduce memory usage (before this setting we used ADODB_FETCH_BOTH)
101     if($this->dbType == 'mssql')
102     {
103       $fetch_mode = ADODB_FETCH_BOTH;
104       if(PHP_OS == 'WINNT')
105       {
106         // Faced this problem when testing XAMPP 1.7.7 on Windows 7 with MSSQL 2008 Express
107         // From PHP MANUAL - reganding mssql_* functions
108         // These functions allow you to access MS SQL Server database.
109         // This extension is not available anymore on Windows with PHP 5.3 or later.
110         // SQLSRV, an alternative driver for MS SQL is available from Microsoft:
111         // http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx.       
112           //
113         // PHP_VERSION_ID is available as of PHP 5.2.7
114         if ( defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 50300)  
115         {
116           $adodb_driver = 'mssqlnative';
117         }     
118       } 
119     }
120     $this->db = NewADOConnection($adodb_driver);
121     $this->db->SetFetchMode($fetch_mode);
122   }
123
124
125   // access to the ADODB object
126   function get_dbmgr_object()
127   {
128     return($this->db);
129   }
130
131   
132   
133   /** Make a connection to the database */
134   # changed Connect() to NConnect() see ADODB Manuals
135   function connect( $p_dsn, $p_hostname = null, $p_username = null, 
136                             $p_password = null, $p_database_name = null ) 
137   {
138     $result = array('status' => 1, 'dbms_msg' => 'ok');
139     
140     if(  $p_dsn === false ) {
141       $t_result = $this->db->NConnect($p_hostname, $p_username, $p_password, $p_database_name );
142     } else {
143       $t_result = $this->db->IsConnected();
144     }
145     
146     if ( $t_result ) {
147       $this->is_connected = true;
148     } else {
149       $result['status'] = 0;
150       $result['dbms_msg']=$this->error();
151     }
152     return ($result);
153   }
154
155
156   /** 
157    * execute SQL query, 
158    * requires connection to be opened
159    * 
160    * @param string $p_query SQL request
161    * @param integer $p_limit (optional) number of rows
162    * @param integer $p_offset (optional) begining row number
163    * 
164    * @return boolean result of request 
165    **/
166   function exec_query( $p_query, $p_limit = -1, $p_offset = -1 )
167   {
168     $ec = 0;
169     $emsg = null;
170     $logLevel = 'DEBUG';
171     $message = '';
172
173     if($this->logQueries)
174     {
175       $this->nQuery++;
176       $t_start = $this->microtime_float();
177     }
178       
179     if ( ( $p_limit != -1 ) || ( $p_offset != -1 ) ) {
180       $t_result = $this->db->SelectLimit( $p_query, $p_limit, $p_offset );
181     } else {
182       $t_result = $this->db->Execute( $p_query );
183     }
184     
185     if($this->logQueries)
186     {
187       $t_elapsed = number_format( $this->microtime_float() - $t_start, 4);
188       $this->overallDuration += $t_elapsed;
189       $message =  "SQL [". $this->nQuery . "] executed [took {$t_elapsed} secs]" .
190                   "[all took {$this->overallDuration} secs]:\n\t\t";
191     }
192     $message .= $p_query;
193
194     if (!$t_result)
195     {
196       $ec       = $this->error_num();
197       $emsg     = $this->error_msg();
198       $message .= "\nQuery failed: errorcode[" . $ec . "]". "\n\terrormsg:".$emsg;
199       $logLevel = 'ERROR';
200
201
202       tLog("ERROR ON exec_query() - database.class.php <br />" . $this->error(htmlspecialchars($p_query)) . 
203            "<br />THE MESSAGE : $message ", 'ERROR', "DATABASE");     
204       echo "<pre> ============================================================================== </pre>";
205       echo "<pre> DB Access Error - debug_print_backtrace() OUTPUT START </pre>";
206       echo "<pre> ATTENTION: Enabling more debug info will produce path disclosure weakness (CWE-200) </pre>";
207       echo "<pre>            Having this additional Information could be useful for reporting </pre>";
208       echo "<pre>            issue to development TEAM. </pre>";
209       echo "<pre> ============================================================================== </pre>";
210       
211       if(defined('DBUG_ON') && DBUG_ON == 1)
212       { 
213         echo "<pre>"; debug_print_backtrace(); echo "</pre>";
214       }   
215       
216       //else
217       //{
218       //  echo "<pre>"; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); echo "</pre>";
219       //}  
220       echo "<pre> ============================================================================== </pre>";
221       $t_result = false;
222     }
223     
224     if($this->logEnabled)
225     {
226         tLog($message,$logLevel,"DATABASE");
227     }
228     
229     if($this->logQueries)
230     {
231       array_push ($this->queries_array, array( $p_query, $t_elapsed, $ec, $emsg ) );
232     }
233
234     return $t_result;
235     
236   }
237
238
239   // TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher
240   function fetch_array( &$p_result ) 
241   {
242     if ( $p_result->EOF ) {
243       return false;
244     }   
245     
246     // mysql obeys FETCH_MODE_BOTH, hence ->fields works, other drivers do not support this
247     switch ($this->db->databaseType) 
248     {
249       case "mysql":
250       case "oci8po":
251       case "mssql":
252       case "mssqlnative":
253         $t_array = $p_result->fields;
254         break;
255         
256       default:
257         $t_array = $p_result->GetRowAssoc(false);
258         break;
259     }
260     
261     $p_result->MoveNext();
262     return $t_array;
263   }
264
265
266     // 20080315 - franciscom - Got new code from Mantis, that manages FETCH_MODE_ASSOC
267   function db_result( $p_result, $p_index1=0, $p_index2=0 ) {
268     if ( $p_result && ( $this->num_rows( $p_result ) > 0 ) ) 
269     {
270       $p_result->Move( $p_index1 );
271       $t_result = $p_result->GetArray();
272         
273       if ( isset( $t_result[0][$p_index2] ) ) {
274         return $t_result[0][$p_index2];
275       }
276         
277       // The numeric index doesn't exist. FETCH_MODE_ASSOC may have been used.
278       // Get 2nd dimension and make it numerically indexed
279       $t_result = array_values( $t_result[0] );
280       return $t_result[$p_index2];
281     }
282     return false;
283   }
284
285
286   /** @return integer the last inserted id */
287   function insert_id($p_table = null) 
288   {
289     if ( isset($p_table) && ($this->db_is_pgsql() || $this->db_is_oracle()))
290     {
291       if ( $this->db_is_pgsql() ) 
292       {
293         $sql = "SELECT currval('".$p_table."_id_seq')";
294       }
295       elseif ($this->db_is_oracle())
296       {
297         $sql = "SELECT ".$p_table."_id_seq.currval from dual";
298       }
299       $result = $this->exec_query( $sql );
300       return $this->db_result($result);
301     }
302     return $this->db->Insert_ID( );
303   }
304
305
306   /** Check is the database is PostgreSQL */
307   function db_is_pgsql() 
308   {
309     $status_ok = false;
310     switch( $this->dbType ) 
311     {
312       case 'postgres':
313       case 'postgres7':
314       case 'pgsql':
315         $status_ok = true;
316       break;  
317     }
318     return $status_ok;
319   }
320
321
322   /** 
323    * Check is the database is ORACLE 
324    * @return boolean TRUE = Oracle type
325    **/
326   function db_is_oracle() 
327   {
328     $status_ok = false;
329     switch( $this->dbType ) 
330     {
331       case 'oci8':
332       case 'oci8po':
333         $status_ok = true;
334       break;  
335     }   
336     return $status_ok;
337   }
338
339
340   function db_table_exists( $p_table_name ) {
341     return in_array ( $p_table_name , $this->db->MetaTables( "TABLE" ) ) ;
342   }
343
344
345   function db_field_exists( $p_field_name, $p_table_name ) {
346     return in_array ( $p_field_name , $this->db->MetaColumnNames( $p_table_name ) ) ;
347   }
348
349
350   /** 
351    * Check if there is an index defined on the specified table/field and with
352    * the specified type.
353    * Warning: only works with MySQL
354    * 
355    * @param string $p_table Name of table to check
356    * @param string $p_field Name of field to check
357    * @param string $p_key key type to check for (eg: PRI, MUL, ...etc)
358    * 
359    * @return boolean 
360    */
361   function key_exists_on_field( $p_table, $p_field, $p_key ) {
362     $c_table = $this->db->prepare_string( $p_table );
363     $c_field = $this->db->prepare_string( $p_field );
364     $c_key   = $this->db->prepare_string( $p_key );
365
366     $sql = "DESCRIBE $c_table";
367     $result = $this->exec_query( $sql );
368     
369     $count = $this->num_rows( $result );
370     for ( $i=0 ; $i < $count ; $i++ ) {
371       $row = $this->db->fetch_array( $result );
372
373       if ( $row['Field'] == $c_field ) {
374         return ( $row['Key'] == $c_key );
375       }
376     }
377     return false;
378   }
379
380
381   # prepare a string before DB insertion
382   # 20051226 - fm
383   function prepare_string( $p_string )
384   {
385     if (is_null($p_string))
386       return '';
387       
388     $t_escaped = $this->db->qstr( $p_string, false );
389     // from second char(1) to one before last(-1)
390     return(substr($t_escaped,1,-1));
391   }
392
393
394   # prepare an integer before DB insertion
395   function prepare_int( $p_int ) {
396     return (int)$p_int;
397   }
398
399
400   # prepare a boolean before DB insertion
401   function prepare_bool( $p_bool ) {
402     return (int)(bool)$p_bool;
403   }
404
405   # return current timestamp for DB
406   function db_now()
407   {
408       switch($this->db->databaseType)
409       {
410       /* @todo: maybe we should use this?
411           case 'odbc_mssql':
412         return "GETDATE()";
413       */
414       default:
415         return $this->db->DBTimeStamp(time());
416     }
417   }
418
419
420   # generate a unixtimestamp of a date
421   # > SELECT UNIX_TIMESTAMP();
422   # -> 882226357
423   # > SELECT UNIX_TIMESTAMP('1997-10-04 22:23:00');
424   # -> 875996580
425   function db_timestamp( $p_date=null ) {
426
427     if ( null !== $p_date ) {
428       $p_timestamp = $this->db->UnixTimeStamp($p_date);
429     } else {
430       $p_timestamp = time();
431     }
432     return $this->db->DBTimeStamp($p_timestamp) ;
433   }
434
435
436   function db_unixtimestamp( $p_date=null ) {
437
438     if ( null !== $p_date ) {
439       $p_timestamp = $this->db->UnixTimeStamp($p_date);
440     } else {
441       $p_timestamp = time();
442     }
443     return $p_timestamp ;
444   }
445
446
447   /** @return integer count queries */
448   function count_queries () {
449     return count( $this->queries_array );
450     }
451
452
453   /** @return integer count unique queries */
454   function count_unique_queries () {
455
456     $t_unique_queries = 0;
457     $t_shown_queries = array();
458     foreach ($this->queries_array as $t_val_array) {
459       if ( ! in_array( $t_val_array[0], $t_shown_queries ) ) {
460         $t_unique_queries++;
461         array_push( $t_shown_queries, $t_val_array[0] );
462       }
463     }
464     return $t_unique_queries;
465     }
466
467
468   /** get total time for queries */
469   function time_queries () {
470     $t_count = count( $this->queries_array );
471     $t_total = 0;
472     for ( $i = 0; $i < $t_count; $i++ ) {
473       $t_total += $this->queries_array[$i][1];
474     }
475     return $t_total;
476   }
477
478
479   /** 
480    * close the connection.
481    * Not really necessary most of the time since a connection is
482    * automatically closed when a page finishes loading.
483    */
484   function close() {
485     $t_result = $this->db->Close();
486   }
487
488
489   function error_num() {
490     return $this->db->ErrorNo();
491   }
492
493
494   function error_msg() {
495     return $this->db->ErrorMsg();
496   }
497
498
499   /** 
500    * returns a message string with: error num, error msg and query.
501    * 
502    * @return string the message
503    */
504   function error( $p_query=null ) {
505     $msg= $this->error_num() . " - " . $this->error_msg();
506     
507     if ( null !== $p_query ) 
508     {
509       $msg .= " - " . $p_query ;
510     } 
511     return $msg;
512   }
513
514
515   function num_rows( $p_result ) {
516     return $p_result->RecordCount( );
517   }
518
519
520   function affected_rows() {
521     return $this->db->Affected_Rows( );
522   }
523
524
525   /**
526    * Fetches the first column first row 
527    *
528    * @param string $sql the query to be executed
529    * @param string $column the name of the column which shall be returned
530    * 
531    * @return mixed the value of the column
532    **/
533   function fetchFirstRowSingleColumn($sql,$column)
534   {
535     $value = null;
536     $row = $this->fetchFirstRow($sql);
537     
538     // BUGID 1318
539     if ($row && array_key_exists($column, $row))
540     {
541         $value = $row[$column];
542     }
543     return $value;
544   }
545
546   /**
547    * Fetches the first row (in a assoc-array)
548    *
549    * @param string $sql the query to be executed
550    * @return array the first row
551    **/
552   function fetchFirstRow($sql)
553   {
554     $result = $this->exec_query($sql);
555     $row = null;
556     if ($result)
557     {
558       $row = $this->fetch_array($result);
559     }
560     unset($result);
561     return $row;
562   }
563
564
565   /**
566    * Get one value (no array)
567    * for example: SELECT COUNT(*) FROM table 
568    *
569    * @param string $sql the query to be executed
570    * @return string of one value || null
571    **/
572   public function fetchOneValue($sql)
573   {
574       $row = $this->fetchFirstRow($sql);
575     if ($row)
576       {
577       $fieldName = array_keys($row);   
578       return $row[$fieldName[0]];
579     }
580     return null;
581   }
582   
583   
584   /**
585    * Fetches all values for a given column of all returned rows
586    *
587    * @param string $sql the query to be executed
588    * @param string $column the name of the column
589    * @param integer $limit (optional) number of rows
590      *
591    * @return array an enumerated array, which contains all the values
592    **/
593   function fetchColumnsIntoArray($sql,$column,$limit = -1)
594   {
595     $items = null;
596     $result = $this->exec_query($sql,$limit);
597     if ($result)
598     {
599       while($row = $this->fetch_array($result))
600       {
601         $items[] = $row[$column];
602       } 
603     }
604     
605     unset($result);
606     return $items;
607   }
608
609
610   /**
611    * Fetches all rows into a map whose keys are the values of columns
612    *
613    * @param string $sql the query to be executed
614    * @param string $column the name of the column
615    * @param booleam $cumulative default 0
616    *                useful in situations with results set with multiple
617    *                rows with same value on key column like this:
618    *
619    *                col1   col2  col3 ...
620    *                 X      A     C
621    *                 X      B     Z
622    *                 Y      B     0
623    *
624    *        cumulative=0 -> return items= array('X' => array('A','C'), 'Y' => array('B','0') )
625    *
626    *        cumulative=1 -> return items= 
627    *                        array('X' => array( 0 => array('A','C'), 1 => array('B','Z')),
628    *                              'Y' => array( 0 => array('B','0')I )
629    *
630    * @param integer $limit (optional) number of rows
631    *
632    * @return array an assoc array whose keys are the values from the columns
633    *         of the rows
634    **/
635   function fetchRowsIntoMap($sql,$column,$cumulative = 0,$limit = -1)
636   {
637     $items = null;
638     $result = $this->exec_query($sql,$limit);
639     if ($result)
640     {
641       // -----------------------------------------------
642             // Error management Code         
643             $errorMsg=__CLASS__ . '/' . __FUNCTION__ . ' - ';
644       if( ($empty_column = (trim($column)=='') ) )
645       {
646         $errorMsg .= 'empty column - SQL:' . $sql;
647           trigger_error($errorMsg,E_USER_NOTICE);
648           return null;
649         }
650
651       while($row = $this->fetch_array($result))
652       {
653         // -----------------------------------------------
654                 // Error management Code         
655                 if( !isset($row[$column]) )
656                 {
657                 $errorMsg .= 'missing column:' . $column;
658               $errorMsg .= ' - SQL:' . $sql;
659             trigger_error($errorMsg,E_USER_NOTICE);
660             return null;
661           } 
662                 // -----------------------------------------------
663                 
664         if ($cumulative)
665         {
666           $items[$row[$column]][] = $row;
667         }
668         else
669         {
670           $items[$row[$column]] = $row;
671         } 
672       }
673     }
674     
675     unset($result);
676     unset($row);
677     return $items;
678   }
679   
680   
681   /**
682    * Fetches the values of two columns from all rows into a map
683    *
684    * @param string $sql the query to be executed
685    * @param string $column1 the name of the column (keys for the map)
686    * @param string $column2 the name of the second column (values of the map)
687    * @param boolean $cumulative
688    *                useful in situations with results set like
689    *                col1   col2
690    *                 X      A
691    *                 X      B
692    *                 Y      B
693    *
694    *        cumulative=0 -> return items= array('X' => 'B', 'Y' => 'B')
695    *
696    *        cumulative=1 -> return items= array('X' => array('A','B'), 'Y' => array('B') )
697    *               
698    * @param integer $limit (optional) number of rows
699    *               
700    * @return assoc array whose keys are the values of column1 and the values are:
701    *
702    *         cumulative=0  => the values of column2 
703    *         cumulative=1  => array with the values of column2 
704    *
705    **/
706   function fetchColumnsIntoMap($sql,$column1,$column2,$cumulative=0,$limit = -1)
707   {
708     $result = $this->exec_query($sql,$limit);
709     $items = null;
710     if ($result)
711     {
712       while ($myrow = $this->fetch_array($result))
713       {
714         if($cumulative)
715           {
716           $items[$myrow[$column1]][] = $myrow[$column2];
717         }
718         else
719         {
720           $items[$myrow[$column1]] = $myrow[$column2];
721         }
722       } 
723     }
724
725     unset($result);
726     return $items;
727   }
728
729
730   /**
731    * database server information
732    * wrapper for adodb method ServerInfo
733    *
734    * @return assoc array members 'version' and 'description'
735    **/
736   function get_version_info()
737   {
738     $version = $this->db->ServerInfo();
739     return $version;
740   }
741
742
743   /**
744    **/
745   function get_recordset($sql,$fetch_mode = null,$limit = -1, $start = -1)
746   {
747     $output = null;
748
749     $result = $this->exec_query($sql,$limit,$start);
750     if ($result)
751     {
752       while($row = $this->fetch_array($result))
753       {
754         $output[] = $row;
755       } 
756     }
757
758     unset($result);
759     return $output;
760   }
761
762
763   /**
764    * Fetches all rows into a map whose keys are the values of columns
765    *
766    * @param string $sql the query to be executed
767    * @param string $column the name of the column
768    * @param integer $limit (optional) number of rows
769      *
770    * @return array an assoc array whose keys are the values from the columns
771    *         of the rows
772    **/
773   function fetchArrayRowsIntoMap($sql,$column,$limit = -1)
774   {
775     $items = null;
776     $result = $this->exec_query($sql,$limit);
777     if ($result)
778     {
779       while($row = $this->fetch_array($result))
780       {
781         $items[$row[$column]][] = $row;
782       }
783     }
784
785     unset($result);
786     return $items;
787   }
788
789
790   /**
791    * Fetches all rows into a map whose keys are the values of columns
792    *
793    * @param string $sql the query to be executed
794    * @param string $column_main_key the name of the column
795    * @param string $column_sec_key the name of the column
796    * @param boolean $cumulative
797    * @param integer $limit (optional) number of rows
798    * 
799    * @return array $items[$row[$column_main_key]][$row[$column_sec_key]]
800    * 
801    **/
802   function fetchMapRowsIntoMap($sql,$column_main_key,$column_sec_key,$cumulative = 0,$limit = -1)
803   {
804     $items = null;
805     $result = $this->exec_query($sql,$limit);
806     if ($result)
807     {
808       while($row = $this->fetch_array($result))
809       {
810         if($cumulative)
811         {
812           $items[$row[$column_main_key]][$row[$column_sec_key]][] = $row;
813         }
814         else
815         {
816           $items[$row[$column_main_key]][$row[$column_sec_key]] = $row;
817         } 
818       }
819     }
820     
821     unset($result);
822     return $items;
823   }
824
825   /** 
826    *  TICKET 4898: MSSQL - Add support for SQLSRV drivers needed for PHP on WINDOWS version 5.3 and higher
827    **/
828   function build_sql_create_db($db_name)
829   {
830     $sql='';
831     
832     switch($this->db->databaseType)
833     {
834       case 'postgres7':
835         $sql = 'CREATE DATABASE "' . $this->prepare_string($db_name) . '" ' . "WITH ENCODING='UNICODE' "; 
836         break;
837         
838       case 'mssql':
839       case 'mssqlnative':
840         $sql = 'CREATE DATABASE [' . $this->prepare_string($db_name) . '] '; 
841         break;
842         
843       case 'mysql':
844       default:
845         $sql = "CREATE DATABASE `" . $this->prepare_string($db_name) . "` CHARACTER SET utf8 "; 
846       break;
847     }
848     return ($sql);
849   }
850
851
852   function db_null_timestamp()
853   {
854     $db_type = $this->db->databaseType;
855     $nullValue = NULL;
856     
857     switch($db_type)
858     {
859       case 'mysql':
860         // is not an error i put single quote on value      
861         $nullValue = " '0000-00-00 00:00:00' ";
862       break;
863     }
864     return $nullValue;
865   }
866
867
868   /**
869    * Fetches all rows into a map of 3 levels
870    *
871    * @param string $sql the query to be executed
872    * @param array $keyCols, columns to used as access key
873    * @param boolean $cumulative
874    * @param integer $limit (optional) number of rows
875    * 
876    * @return array $items[$row[$column_main_key]][$row[$column_sec_key]]
877    * 
878    **/
879   function fetchRowsIntoMap3l($sql,$keyCols,$cumulative = 0,$limit = -1)
880   {
881     $items = null;
882     $result = $this->exec_query($sql,$limit);
883     
884     // new dBug($result);
885     if ($result)
886     {
887       while($row = $this->fetch_array($result))
888       {
889         if($cumulative)
890         {
891           $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][] = $row;
892         }
893         else
894         {
895           $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]] = $row;
896         } 
897       }
898     }
899
900     unset($result);
901     return $items;
902   }
903
904
905   /**
906    * Fetches all rows into a map of 3 levels
907    *
908    * @param string $sql the query to be executed
909    * @param array $keyCols, columns to used as access key
910    * @param boolean $cumulative
911    * @param integer $limit (optional) number of rows
912    * 
913    * @return array $items[$row[$column_main_key]][$row[$column_sec_key]]
914    * 
915    **/
916   function fetchRowsIntoMap4l($sql,$keyCols,$cumulative = 0,$limit = -1)
917   {
918     $items = null;
919     $result = $this->exec_query($sql,$limit);
920   
921     // displayMemUsage(__FUNCTION__);
922   
923     // new dBug($result);
924     if ($result)
925     {
926       while($row = $this->fetch_array($result))
927       {
928         if($cumulative)
929         {
930           $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]][] = $row;
931         }
932         else
933         {
934           $items[$row[$keyCols[0]]][$row[$keyCols[1]]][$row[$keyCols[2]]][$row[$keyCols[3]]] = $row;
935         } 
936       }
937     }
938     // displayMemUsage(__FUNCTION__);
939     unset($result);
940     // displayMemUsage(__FUNCTION__);
941     return $items;
942   }
943
944
945
946
947   /**
948    * Fetches all rows into a map whose keys are the values of columns
949    *
950    * @param string $sql the query to be executed
951    * @param string $column the name of the column
952    *
953    * @return array an assoc array 
954    **/
955   function fetchRowsIntoMapAddRC($sql,$column,$limit = -1)
956   {
957     $items = null;
958     $result = $this->exec_query($sql,$limit);
959     if ($result)
960     {
961       $errorMsg=__CLASS__ . '/' . __FUNCTION__ . ' - ';
962       if( ($empty_column = (trim($column)=='') ) )
963       {
964         $errorMsg .= 'empty column - SQL:' . $sql;
965         trigger_error($errorMsg,E_USER_NOTICE);
966         return null;
967       }
968
969       while($row = $this->fetch_array($result))
970       {
971         if( !isset($row[$column]) )
972         {
973           $errorMsg .= 'missing column:' . $column;
974           $errorMsg .= ' - SQL:' . $sql;
975           trigger_error($errorMsg,E_USER_NOTICE);
976           return null;
977         } 
978         if(!isset($items[$row[$column]]) )
979         {
980           $row['recordcount'] = 0;
981         }
982         else
983         {
984           $row['recordcount'] = $items[$row[$column]]['recordcount'];
985         }
986         $row['recordcount']++;
987         $items[$row[$column]] = $row;
988       }
989     }
990     
991     unset($result);
992     unset($row);
993     return $items;
994   }
995
996   /**
997    * @used-by testplan.class.php
998    */
999   function fetchMapRowsIntoMapStackOnCol($sql,$column_main_key,$column_sec_key,$stackOnCol)
1000   {
1001     $items = null;
1002     $result = $this->exec_query($sql);
1003     if ($result)
1004     {
1005       while($row = $this->fetch_array($result))
1006       {
1007         if( !isset($items[$row[$column_main_key]][$row[$column_sec_key]]) )
1008         {
1009           $items[$row[$column_main_key]][$row[$column_sec_key]] = $row;
1010           $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol] = array();
1011         }
1012         $items[$row[$column_main_key]][$row[$column_sec_key]][$stackOnCol][]=$row[$stackOnCol];
1013       }
1014     }
1015     unset($result);
1016     return $items;
1017   }
1018
1019
1020 } // end of database class