Adjust order of error output
[misc/kostenrechnung] / lib / rico / dbClass2.php
1 <?php
2 /*****************************************************************
3  Page        : dbClass2.php
4  Description : Routines to access MySQL, SQL Server, Oracle, & ODBC databases
5  Date        : 25 May 2006
6  Author      : Matt Brown (dowdybrown@yahoo.com)
7  Copyright (C) 2006-2008 Matt Brown
8
9  Rico is licensed under the Apache License, Version 2.0 (the "License"); you may not use this
10  file except in compliance with the License. You may obtain a copy of the License at
11  http://www.apache.org/licenses/LICENSE-2.0
12 ******************************************************************/
13
14
15 //********************************************************************************************************
16 // Parse a SQL statement
17 //********************************************************************************************************
18
19 class sqlParse {
20   var $IsDistinct;
21   var $arSelList;
22   var $arSelListAs;
23   var $FromClause;
24   var $WhereClause;
25   var $arGroupBy;
26   var $HavingClause;
27   var $arOrderBy;
28   // -------------------------------------------------------------
29   // Rebuilds a SQL select statement that was parsed by ParseSelect
30   // -------------------------------------------------------------
31
32   function Unparse() {
33     $sqltext="SELECT ";
34     if ($this->IsDistinct) $sqltext.="DISTINCT ";
35     $sqltext.=$this->UnparseColumnList()." FROM ".$this->FromClause;
36     if (!empty($this->WhereClause)) {
37       $sqltext.=" WHERE ".$this->WhereClause;
38     }
39     if (is_array($this->arGroupBy)) {
40       if (count($this->arGroupBy) >  0) {
41         $sqltext.=" GROUP BY ".implode(",",$this->arGroupBy);
42       }
43     }
44     if (!empty($this->HavingClause)) {
45       $sqltext.=" HAVING ".$this->HavingClause;
46     }
47     if (is_array($this->arOrderBy)) {
48       if (count($this->arOrderBy) >  0) {
49         $sqltext.=" ORDER BY ".implode(",",$this->arOrderBy);
50       }
51     }
52     return $sqltext;
53   }
54
55   function UnparseSelect() {
56     return $this->Unparse();
57   }
58
59   function UnparseSelectDistinct() {
60     $this->IsDistinct=true;
61     return $this->Unparse();
62   }
63
64   function UnparseDistinctColumn($colnum) {
65     $sqltext="SELECT DISTINCT ".$this->UnparseColumn($colnum)." FROM ".$this->FromClause;
66     if (!empty($this->WhereClause)) {
67       $sqltext.=" WHERE ".$this->WhereClause;
68     }
69     return $sqltext;
70   }
71
72   function UnparseColumn($i) {
73     $s=$this->arSelList[$i];
74     if (!empty($this->arSelListAs[$i])) {
75       $s.=" AS ".$this->arSelListAs[$i];
76     }
77     return $s;
78   }
79
80   function UnparseColumnList() {
81     if (empty($this->arSelList)) return "";
82     $sqltext=$this->UnparseColumn(0);
83     for ($i=1; $i<count($this->arSelList); $i++) {
84       $sqltext.=",".$this->UnparseColumn($i);
85     }
86     return $sqltext;
87   }
88   
89   function DebugPrint() {
90     echo "<p>Parse Result:";
91     echo "<table border='1'>";
92     if ($this->IsDistinct) echo "<tr valign='top'><td>DISTINCT<td>&nbsp;";
93     echo "<tr valign='top'><td>COLUMNS:<td><ol>";
94     if (!empty($this->arSelList)) {
95       for ($i=0; $i<count($this->arSelList); $i++) {
96         echo "<li>".$this->UnparseColumn($i);
97       }
98     }
99     echo "</ol><tr valign='top'><td>FROM:<td>" . $this->FromClause;
100     if (!empty($this->WhereClause)) {
101       echo "<tr valign='top'><td>WHERE:<td>".$this->WhereClause;
102     }
103     if (is_array($this->arGroupBy)) {
104       if (count($this->arGroupBy) >  0) {
105         echo "<tr valign='top'><td>GROUP BY:<td>".implode("<br>",$this->arGroupBy);
106       }
107     }
108     if (!empty($this->HavingClause)) {
109       echo "<tr valign='top'><td>HAVING:<td>".$this->HavingClause;
110     }
111     if (is_array($this->arOrderBy)) {
112       if (count($this->arOrderBy) >  0) {
113         echo "<tr valign='top'><td>ORDER BY:<td>".implode("<br>",$this->arOrderBy);
114       }
115     }
116     echo "</table>";
117   }
118
119   function Init() {
120     $this->arSelList=array();
121     $this->arSelListAs=array();
122     $this->arGroupBy=array();
123     $this->arOrderBy=array();
124     $this->FromClause=NULL;
125     $this->WhereClause=NULL;
126     $this->HavingClause=NULL;
127     $this->IsDistinct=false;
128   }
129   // -------------------------------------------------------------
130   // Parse a SQL select statement into its major components
131   // Does not handle:
132   // 1) union queries
133   // 2) select into
134   // 3) more than one space between "group" and "by", or "order" and "by"
135   // If distinct is specified, it will be part of the first item in arSelList
136   // -------------------------------------------------------------
137
138   function ParseSelect($sqltext) {
139     $this->Init();
140     $clause='';
141     $_retval=false;
142     $sqltext=str_replace("\n"," ",$sqltext);
143     $sqltext=" ".str_replace("\r"," ",$sqltext)." SELECT ";
144     // SELECT suffix forces last curfield to be saved
145     $l=strlen($sqltext);
146     $parencnt=0;
147     $inquote=false;
148     $curfield="";
149     for ($i=0; $i<$l; $i++) {
150       $ch=substr($sqltext,$i,1);
151       if ($inquote) {
152         if ($ch == $endquote) {
153           if ($endquote=="'" && substr($sqltext,$i,2) == "''") {
154             $curfield.="'";
155             $i++;
156           }
157           else {
158             $inquote=false;
159           }
160         }
161         $curfield.=$ch;
162       }
163       elseif ($ch == "'" || $ch == "\"" || $ch == "`") {
164         $inquote=true;
165         $endquote=$ch;
166         $curfield.=$ch;
167       }
168       elseif ($ch == "[") {
169         $inquote=true;
170         $endquote="]";
171         $curfield.=$ch;
172       }
173       elseif ($ch == "(") {
174         $parencnt++;
175         $curfield.=$ch;
176       }
177       elseif ($ch == ")") {
178         if ($parencnt == 0) {
179           return $_retval;
180           // sql statement has a syntax error
181         }
182         $parencnt--;
183         $curfield.=$ch;
184       }
185       elseif ($parencnt > 0) {
186         $curfield.=$ch;
187       }
188       elseif ($ch == ",") {
189         switch ($clause) {
190           case "SELECT":
191             array_push($this->arSelList, $curfield);
192             array_push($this->arSelListAs, NULL);
193             $curfield='';
194             break;
195           case "AS":
196             $this->arSelListAs[count($this->arSelList)-1]=$curfield;
197             $curfield='';
198             $clause="SELECT";
199             break;
200           case "GROUP BY":
201             array_push($this->arGroupBy, $curfield);
202             $curfield='';
203             break;
204           case "ORDER BY":
205             array_push($this->arOrderBy, $curfield);
206             $curfield='';
207             break;
208           default:
209             $curfield.=$ch;
210             break;
211         }
212       }
213       elseif ($ch == " ") {
214         $j=strpos($sqltext," ",$i+1);
215         if ($j===false)
216         {
217           $curfield.=$ch;
218         }
219           else
220         {
221           if (strtoupper(substr($sqltext,$j+1,3))=="BY ") $j+=3;
222           $nexttoken=strtoupper(substr($sqltext,$i+1,$j-$i-1));
223           //echo '<br>'.$nexttoken;
224           switch ($nexttoken) {
225
226             case "SELECT":
227             case "INTO":
228             case "FROM":
229             case "WHERE":
230             case "GROUP BY":
231             case "HAVING":
232             case "ORDER BY":
233               switch ($clause) {
234                 case "SELECT":
235                   $this->AddColumn($curfield, NULL);
236                   $curfield='';
237                   break;
238                 case "AS":
239                   $this->arSelListAs[count($this->arSelList)-1]=$curfield;
240                   $curfield='';
241                   break;
242                 case "FROM":     $this->FromClause=$curfield;             $curfield=''; break;
243                 case "WHERE":    $this->WhereClause=$curfield;            $curfield=''; break;
244                 case "GROUP BY": array_push($this->arGroupBy, $curfield); $curfield=''; break;
245                 case "HAVING":   $this->HavingClause=$curfield;           $curfield=''; break;
246                 case "ORDER BY": array_push($this->arOrderBy, $curfield); $curfield=''; break;
247               }
248               $clause=$nexttoken;
249               $i=$j-1;
250               break;
251
252             case "AS":
253               if ($clause=="SELECT") {
254                 $this->AddColumn($curfield, NULL);
255                 $curfield='';
256                 $clause=$nexttoken;
257                 $i=$j;
258               } else if ($curfield != "") {
259                 $curfield.=$ch;
260               }
261               break;
262
263             case "DISTINCT":
264               if ($clause=="SELECT") {
265                 $this->IsDistinct=true;
266                 $curfield='';
267                 $i=$j;
268               } else if ($curfield != "") {
269                 $curfield.=$ch;
270               }
271               break;
272
273             default:
274               if ($curfield != "") {
275                 $curfield.=$ch;
276               }
277               break;
278           }
279         }
280       }
281       else {
282         $curfield.=$ch;
283       }
284     }
285     return true;
286   }
287
288   // -------------------------------------------------------------
289   // Add column to select list
290   // -------------------------------------------------------------
291   function AddColumn($ColumnSql, $ColumnName) {
292     array_push($this->arSelList, $ColumnSql);
293     array_push($this->arSelListAs, $ColumnName);
294   }
295
296   // -------------------------------------------------------------
297   // Add a join to the from clause
298   // -------------------------------------------------------------
299   function AddJoin($JoinClause) {
300     if (preg_match("/ join /i",$this->FromClause)) {
301       $this->FromClause="(".$this->FromClause.")";
302       // required by Access
303     }
304     $this->FromClause.=" ".$JoinClause;
305   }
306
307   function SplitSortSpec($sortspec, &$sortcol, &$sortdir) {
308     $sortspec=strtoupper($sortspec);
309     if (substr($sortspec,-3) == "ASC") {
310       $sortcol=trim(substr($sortspec,0,-3));
311       $sortdir="ASC";
312     }
313     elseif (substr($sortspec,-4) == "DESC") {
314       $sortcol=trim(substr($sortspec,0,-4));
315       $sortdir="DESC";
316     }
317     else {
318       $sortcol=trim($sortspec);
319       $sortdir="";
320     }
321   }
322   
323   function FindSortColumn($sortspec) {
324     $this->SplitSortSpec($sortspec, $findcol, $finddir);
325     for ($i=0; $i<=count($this->arOrderBy)-1; $i++) {
326       $this->SplitSortSpec($this->arOrderBy[$i], $sortcol, $sortdir);
327       if ($sortcol == $findcol) {
328         return $i;
329       }
330     }
331     return -1;
332   }
333
334   // -------------------------------------------------------------
335   // Add sort criteria to the beginning of the order by clause
336   // -------------------------------------------------------------
337   function AddSort($NewSort) {
338     $colidx=$this->FindSortColumn($NewSort);
339     if ($colidx >= 0) {
340       for ($i=$colidx; $i>0; $i--) {
341         $this->arOrderBy[$i]=$this->arOrderBy[$i-1];
342       }
343       $this->arOrderBy[0]=$NewSort;
344     }
345     else {
346       array_unshift($this->arOrderBy, $NewSort);
347     }
348   }
349   
350   // -------------------------------------------------------------
351   // Append sort criteria to the order by clause
352   // -------------------------------------------------------------
353   function AppendSort($NewSort) {
354     array_push($this->arOrderBy, $NewSort);
355   }
356
357   // -------------------------------------------------------------
358   // Add a condition to the where clause
359   // -------------------------------------------------------------
360   function AddWhereCondition($NewCondition) {
361     $this->AddCondition($this->WhereClause, $NewCondition);
362   }
363
364   // -------------------------------------------------------------
365   // Add a condition to the having clause
366   // -------------------------------------------------------------
367   function AddHavingCondition($NewCondition) {
368     $this->AddCondition($this->HavingClause, $NewCondition);
369   }
370
371   function AddCondition(&$Clause, $NewCondition) {
372     if (empty($NewCondition)) {
373       return;
374     }
375     if (empty($Clause)) {
376       $Clause="(".$NewCondition.")";
377     }
378     else {
379       $Clause.=" AND (".$NewCondition.")";
380     }
381   }
382 }
383
384
385 // -------------------------------------------------------------
386 // created by dbClass.GetColumnInfo()
387 // -------------------------------------------------------------
388 class dbColumn
389 {
390   var $ColName,$Nullable,$ColType,$ColLength,$Writeable,$IsPKey;
391 }
392
393 // tested ok with MySQL 4 & 5
394 class dbClass_mysql
395 {
396   var $lastQuery;
397   function dbClass_mysql($conn) { $this->conn=$conn; }
398   function HasError() { return mysql_errno()!=0; }
399   function ErrorMsg() { return mysql_error(); }
400   function Close() { return mysql_close($this->conn); }
401   function FreeResult($rsLookUp) { return mysql_free_result($rsLookUp); }
402   function RunQuery($sqltext) { $this->lastQuery=$sqltext; return mysql_query($sqltext,$this->conn); }
403   function NumFields($rsMain) { return mysql_num_fields($rsMain); }
404   function NumRows($rsMain) { return mysql_num_rows($rsMain); }
405   function FieldType($rsMain,$i) { return mysql_field_type($rsMain,$i); }
406   function FetchRow($rsMain,&$result) { $result=mysql_fetch_row($rsMain); return ($result==false) ? false : true; }
407   function FetchAssoc($rsMain,&$result) { $result=mysql_fetch_assoc($rsMain); return ($result==false) ? false : true; }
408   function FetchArray($rsMain,&$result) { $result=mysql_fetch_array($rsMain,MYSQL_NUM); return ($result==false) ? false : true; }
409   function AffectedRows($rsMain) { return mysql_affected_rows($this->conn); }
410   function Seek($rsMain,$offset) { return mysql_data_seek($rsMain,$offset); }
411   function RunParamQuery($query, $phs = array()) {
412     foreach ($phs as $ph) {   // from php.net
413       if ( isset($ph) ) {
414         $ph = "'" . mysql_real_escape_string($ph) . "'";
415       } else {
416         $ph = "NULL" ;
417       }
418       $query = substr_replace($query, $ph, strpos($query, '?'), 1);
419     }
420     $this->lastQuery=$query;
421     return mysql_query($query,$this->conn);
422   }
423   function GetColumnInfo($TableName) {
424     $rsMain=$this->RunQuery("SHOW COLUMNS FROM ".$TableName);
425     if (!$rsMain) return null;
426     $arColumns=array();
427     while($this->FetchAssoc($rsMain,$row)) {
428       $colinfo=new dbColumn;
429       $colinfo->IsPKey=($row["Key"]=="PRI");
430       $colinfo->ColName=$row["Field"];
431       $colinfo->ColType=$row["Type"];
432       $colinfo->Nullable=($row["Null"]=="YES");
433       if (preg_match("/\((\d+)\)/", $row["Type"], $matches))
434         $colinfo->ColLength=$matches[1];
435       else
436         $colinfo->ColLength=0;
437       $colinfo->Writeable=($row["Extra"] != 'auto_increment');
438       array_push($arColumns, $colinfo);
439     } 
440     $this->FreeResult($rsMain);
441     return $arColumns;
442   }
443   function GetTableList($TableType) {
444     if ($TableType!='VIEW') $TableType='BASE TABLE';
445     $rsMain=$this->RunQuery("SHOW FULL TABLES WHERE Table_type='".$TableType."'");
446     if (!$rsMain) return null;
447     $arTables=array();
448     while($this->FetchRow($rsMain,$row)) {
449       array_push($arTables, $row[0]);
450     } 
451     $this->FreeResult($rsMain);
452     return $arTables;
453   }
454   function Concat($arStrings) {
455     return "concat(".implode(",",$arStrings).")";
456   }
457   function Convert2Char($s) {
458     return $s; // implicit conversion
459   }
460   function SqlDay($s) {
461     return "dayofmonth(".$s.")";
462   }
463   function SqlMonth($s) {
464     return "month(".$s.")";
465   }
466   function SqlYear($s) {
467     return "year(".$s.")";
468   }
469   function CurrentTime() {
470     return "LOCALTIMESTAMP";
471   }
472 }
473
474
475 // tested ok with Oracle XE
476 class dbClass_oci
477 {
478   var $lastQuery;
479   function dbClass_oci($conn) { $this->conn=$conn; }
480   function HasError() { return is_array(ocierror()); }
481   function ErrorMsg() { $e=ocierror(); return is_array($e) ? $e['message'] : ''; }
482   function Close() { return ocilogoff($this->conn); }
483   function FreeResult($rsLookUp) { return ocifreestatement($rsLookUp); }
484   function RunQuery($sqltext) { $this->lastQuery=$sqltext; $stmt=ociparse($this->conn, $sqltext); ociexecute($stmt); return $stmt; }
485   function NumFields($rsMain) { return ocinumcols($rsMain); }
486   function NumRows($rsMain) { return -1; }
487   function FieldType($rsMain,$i) { return ocicolumntype($rsMain,$i+1); }
488   function FetchRow($rsMain,&$result) { return ocifetchinto($rsMain,$result,OCI_NUM); }
489   function FetchAssoc($rsMain,&$result) { return ocifetchinto($rsMain,$result,OCI_ASSOC); }
490   function FetchArray($rsMain,&$result) { return ocifetchinto($rsMain,$result,OCI_NUM); }
491   function AffectedRows($rsMain) { return (is_resource($rsMain) ? ocirowcount($rsMain) : false); }
492   function Seek($rsMain,$offset) { 
493     for($i=0; $i<$offset; $i++) ocifetchrow($rsMain);
494   }
495   function RunParamQuery($query, $phs = array()) {
496     foreach ($phs as $ph) {   // from php.net
497       $ph = isset($ph) ? "'" . str_replace("'","''",$ph) . "'" : "NULL";
498       $query = substr_replace($query, $ph, strpos($query, '?'), 1);
499     }
500     $this->lastQuery=$query;
501     return $this->RunQuery($query);
502   }
503   function GetColumnInfo($TableName) {
504     $TableName=strtoupper($TableName);
505     $rsMain=$this->RunQuery("select * from col WHERE tname='$TableName' order by colno");
506     if (!$rsMain) return null;
507     $arColumns=array();
508     while($this->FetchAssoc($rsMain,$row)) {
509       $colinfo=new dbColumn;
510       $colinfo->IsPKey=false;
511       $colinfo->ColName=$row["CNAME"];
512       $colinfo->ColType=$row["COLTYPE"];
513       $colinfo->Nullable=($row["NULLS"]=="NULL");
514       $colinfo->ColLength=$row["WIDTH"];
515       $colinfo->Writeable=true;   // need to figure out where to find this
516       array_push($arColumns, $colinfo);
517       //echo "<p>GetColumnInfo: ".$row["CNAME"].' - '.$row["COLTYPE"]."</p>";
518     } 
519     $this->FreeResult($rsMain);
520     $sql = "SELECT b.column_name FROM USER_CONSTRAINTS a, USER_CONS_COLUMNS b WHERE (b.table_name='$TableName') AND (a.table_name='$TableName') AND (a.constraint_type = 'P') AND (a.constraint_name = b.constraint_name)";
521     $rsMain = $this->RunQuery($sql);
522     if ($rsMain) {
523       while($this->FetchRow($rsMain,$row)) {
524         $colname=$row[0];
525         //echo "<p>GetColumnInfo pk: ".$colname."</p>";
526         for($i=0; $i<count($arColumns); $i++) {
527           if ($arColumns[$i]->ColName==$colname) {
528             $arColumns[$i]->IsPKey=true;
529             break;
530           }
531         }
532       } 
533       $this->FreeResult($rsMain);
534     }
535     return $arColumns;
536   }
537   function Concat($arStrings) {
538     return implode(" || ",$arStrings);
539   }
540   function Convert2Char($s) {
541     return "cast(".$s." as varchar2(20))";
542   }
543   function SqlDay($s) {
544     return "to_char(".$s.",'DD')";
545   }
546   function SqlMonth($s) {
547     return "to_char(".$s.",'MM')";
548   }
549   function SqlYear($s) {
550     return "to_char(".$s.",'YYYY')";
551   }
552   function CurrentTime() {
553     return "LOCALTIMESTAMP";
554   }
555 }
556
557
558 // tested ok with MS SQL Server & MS Access
559 // Oracle works ok except for GetColumnInfo
560 class dbClass_odbc
561 {
562   var $lastQuery,$dbc;
563   function dbClass_odbc(&$dbc) { $this->dbc=$dbc; }
564   function HasError() { return odbc_error()!=''; }
565   function ErrorMsg() { return @odbc_error() . ' ' . @odbc_errormsg(); }
566   function Close() { return odbc_close(); }
567   function FreeResult($rsLookUp) { return odbc_free_result($rsLookUp); }
568   function RunQuery($sqltext) { $this->lastQuery=$sqltext; return odbc_exec($this->dbc->dbMain,$sqltext); }
569   function NumFields($rsMain) { return odbc_num_fields($rsMain); }
570   function NumRows($rsMain) { return odbc_num_rows($rsMain); }
571   function FieldType($rsMain,$i) { return odbc_field_type($rsMain,$i+1); }
572   function FetchRow($rsMain,&$result) { $rc=odbc_fetch_into($rsMain,$result); return ($rc==false) ? false : true; }
573   function FetchAssoc($rsMain,&$result) { $result=odbc_fetch_array($rsMain); return ($result==false) ? false : true; }
574   function FetchArray($rsMain,&$result) { $rc=odbc_fetch_into($rsMain,$result); return ($rc==false) ? false : true; }
575   function AffectedRows($rsMain) { return odbc_num_rows($rsMain); }
576   function Seek($rsMain,$offset) { 
577     for($i=0; $i<$offset; $i++) odbc_fetch_row($rsMain);
578   }
579   function RunParamQuery($query, $phs = array()) {
580     // odbc_prepare/odbc_execute chokes on this: SELECT * FROM (SELECT * FROM orders WHERE ShipCountry=?) AS rico_Main
581     // so that approach cannot be used
582     foreach ($phs as $ph) {
583       if ( isset($ph) ) {
584         if (preg_match("/^\d\d\d\d-?\d\d-?\d\d(?:[T ](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|(?:([-+])(\d\d)(?::?(\d\d))?)?)?)?$/",$ph)) {
585           $ph = "{ts '".$ph."'}";
586         } else {
587           $ph = "'" . str_replace("'","''",$ph) . "'";
588         }
589       } else {
590         $ph = "NULL" ;
591       }
592       $query = substr_replace($query, $ph, strpos($query, '?'), 1);
593     }
594     $this->lastQuery=$query;
595     return odbc_exec($this->dbc->dbMain,$query);
596   }
597   function GetColumnInfo($TableName) {
598     switch ($this->dbc->Dialect) {
599       case "TSQL":
600         $qualifier=$this->dbc->dbDefault;
601         $schema="%";
602         break;
603       case "Access":
604         $qualifier=$this->dbc->dsn;
605         $schema="";
606         break;
607       default:
608         return null;
609     }
610     //echo "<p>GetColumnInfo: ".$qualifier.".".$schema.".".$TableName."</p>";
611     $rsMain=odbc_columns($this->dbc->dbMain, $qualifier, $schema, $TableName);
612     //odbc_result_all($rsMain);
613     if (!$rsMain) return null;
614     $arColumns=array();
615     while($this->FetchAssoc($rsMain,$row)) {
616       if ($row["TABLE_NAME"]!=$TableName) continue;
617       $colinfo=new dbColumn;
618       //echo "<p>GetColumnInfo: ".$row["COLUMN_NAME"].':'.$row["TYPE_NAME"]."</p>";
619       $colinfo->ColName=$row["COLUMN_NAME"];
620       $colinfo->ColType=$row["TYPE_NAME"];
621       if (array_key_exists("PRECISION",$row)) {
622         $colinfo->ColLength=$row["PRECISION"];
623       } else if (array_key_exists("COLUMN_SIZE",$row)) {
624         $colinfo->ColLength=$row["COLUMN_SIZE"];
625       }
626       $colinfo->Nullable=($row["NULLABLE"]=="YES");
627       $colinfo->IsPKey=false;
628       $colinfo->Writeable=($row["TYPE_NAME"] != 'int identity');
629       array_push($arColumns, $colinfo);
630     } 
631     $this->FreeResult($rsMain);
632     //$rsMain=odbc_columnprivileges($this->dbc->dbMain, $qualifier, $schema, $TableName,"%");
633     //odbc_result_all($rsMain);
634     //$this->FreeResult($rsMain);
635     $rsMain=odbc_primarykeys($this->dbc->dbMain, $qualifier, $schema, $TableName);
636     if ($rsMain) {
637       while($this->FetchAssoc($rsMain,$row)) {
638         $colname=$row["COLUMN_NAME"];
639         //echo "<p>GetColumnInfo pk: ".$colname."</p>";
640         for($i=0; $i<count($arColumns); $i++) {
641           if ($arColumns[$i]->ColName==$colname) {
642             $arColumns[$i]->IsPKey=true;
643             break;
644           }
645         }
646       } 
647       $this->FreeResult($rsMain);
648     }
649     return $arColumns;
650   }
651   function Concat($arStrings) {
652     $cnt=count($arStrings);
653     switch ($cnt) {
654       case 0: return '';
655       case 1: return $arStrings[0];
656       default:
657         $result="{fn concat(".$arStrings[0].",".$arStrings[1].")}";
658         for ($i=2; $i<$cnt; $i++) {
659           $result="{fn concat(".$result.",".$arStrings[$i].")}";
660         }
661     }
662     return $result;
663   }
664   function Convert2Char($s) {
665     return "{fn CONVERT(" . $s . ",SQL_VARCHAR)}";
666   }
667   function SqlDay($s) {
668     return "{fn DAYOFMONTH(" . $s . ")}";
669   }
670   function SqlMonth($s) {
671     return "{fn MONTH(" . $s . ")}";
672   }
673   function SqlYear($s) {
674     return "{fn YEAR(" . $s . ")}";
675   }
676   function CurrentTime() {
677     return "{fn CURDATE()}";
678   }
679 }
680
681 // For MS SQL Server
682 class dbClass_mssql
683 {
684   var $lastQuery,$dbc;
685   function dbClass_mssql($conn) { $this->conn=$conn; }
686   function HasError() { return mssql_get_last_message()!=0; }
687   function ErrorMsg() { return mssql_get_last_message(); }
688   function Close() { return mssql_close($this->conn); }
689   function FreeResult($rsLookUp) { return mssql_free_result($rsLookUp); }
690   function RunQuery($sqltext) { $this->lastQuery=$sqltext; return mssql_query($sqltext,$this->conn); }
691   function NumFields($rsMain) { return mssql_num_fields($rsMain); }
692   function NumRows($rsMain) { return mssql_num_rows($rsMain); }
693   function FieldType($rsMain,$i) { return mssql_field_type($rsMain,$i); }
694   function FetchRow($rsMain,&$result) { $result=mssql_fetch_row($rsMain); return ($result==false) ? false : true; }
695   function FetchAssoc($rsMain,&$result) { $result=mssql_fetch_assoc($rsMain); return ($result==false) ? false : true; }
696   function FetchArray($rsMain,&$result) { $result=mssql_fetch_array($rsMain,MSSQL_NUM); return ($result==false) ? false : true; }
697   function AffectedRows($rsMain) { return mssql_rows_affected($rsMain); }
698   function Seek($rsMain,$offset) { return mssql_data_seek($rsMain,$offset); }
699   function RunParamQuery($query, $phs = array()) {
700     foreach ($phs as $ph) {
701       if ( isset($ph) ) {
702         $ph = "'" . str_replace("'","''",$ph) . "'";
703       } else {
704         $ph = "NULL" ;
705       }
706       $query = substr_replace($query, $ph, strpos($query, '?'), 1);
707     }
708     $this->lastQuery=$query;
709     return mssql_query($query,$this->conn);
710   }
711   
712   function GetColumnInfo($TableName) {
713     $TableName=strtoupper($TableName);
714     $rsMain=$this->RunQuery("exec sp_columns '$TableName'");
715     if (!$rsMain) return null;
716     $arColumns=array();
717
718     while($this->FetchAssoc($rsMain,$row)) {
719       $colinfo=new dbColumn;
720       $colinfo->ColName=$row["COLUMN_NAME"];
721       $colinfo->ColType=$row["TYPE_NAME"];
722       if (array_key_exists("PRECISION",$row)) {
723         $colinfo->ColLength=$row["PRECISION"];
724       } else if (array_key_exists("LENGTH",$row)) {
725         $colinfo->ColLength=$row["LENGTH"];
726       }
727       $colinfo->Nullable=($row["NULLABLE"]==1);
728       $colinfo->IsPKey=false;
729       $colinfo->Writeable=(strtoupper($row["TYPE_NAME"]) != "INT IDENTITY");
730       array_push($arColumns, $colinfo);
731     $tableid = $row["Id_table"];
732     } 
733     $this->FreeResult($rsMain);
734     // Get Primary Keys
735     $rsMain=$this->RunQuery("exec sp_pkeys '$TableName'");
736     if ($rsMain) {
737       while($this->FetchAssoc($rsMain,$row)) {
738         $colname=$row["COLUMN_NAME"];
739         for($i=0; $i<count($arColumns); $i++) {
740           if ($arColumns[$i]->ColName==$colname) {
741             $arColumns[$i]->IsPKey=true;
742             break;
743           }
744         }
745       } 
746       $this->FreeResult($rsMain);
747     }
748     return $arColumns;
749   }
750   
751   function Concat($arStrings) {
752     return implode("+",$arStrings);
753   }
754   function Convert2Char($s) {
755     return "CAST(" . $s . " AS VARCHAR)";
756   }
757   function SqlDay($s) {
758     return "DAY(" . $s . ")";
759   }
760   function SqlMonth($s) {
761     return "MONTH(" . $s . ")";
762   }
763   function SqlYear($s) {
764     return "YEAR(" . $s . ")";
765   }
766   function CurrentTime() {
767     return "CURRENT_TIMESTAMP";
768   }
769 }
770
771
772
773 class dbClassRico
774 {
775
776   var $debug,$ConnTimeout,$CmdTimeout,$LockTimeout,$Provider;
777   var $ErrMsgFmt;
778   var $DisplayErrors;
779   var $LastErrorMsg;
780   var $Dialect;
781   var $Wildcard;
782   // these are private:
783   var $dbMain,$DisplayFunc,$dbDefault;
784
785 // -------------------------------------------------------------
786 // Class Constructor
787 // -------------------------------------------------------------
788   function dbClassRico()
789   {
790     $this->Provider="localhost";
791     $this->debug=false;
792     $this->ConnTimeout=30; // seconds
793     $this->LockTimeout=5000; // milliseconds
794     $this->DisplayErrors=true;
795     $this->CmdTimeout=120; // 2 minutes
796     $this->ErrMsgFmt="HTML";
797     $this->DisplayFunc="echo";
798     $this->Dialect="MySQL";
799     $this->Wildcard="%";
800   }
801
802 // -------------------------------------------------------------
803 // Class Destructor (only called if php5)
804 // -------------------------------------------------------------
805   function __destruct()
806   {
807     $this->dbClose();
808   }
809
810   function DefaultDB()
811   {
812     return $this->dbDefault;
813   }
814
815 //********************************************************************************************************
816 // If the database is down, then an explanation can be placed here
817 //********************************************************************************************************
818   function MaintenanceMsg()
819   {
820     return "";
821   }
822
823   function DisplayMsg($msg)
824   {
825     if (!empty($this->DisplayFunc))
826     {
827       if ($this->ErrMsgFmt=="HTML" && substr($msg,0,1)!="<")
828       {
829         $msg="<p>".htmlspecialchars(str_replace("\n","<br>",$msg))."</p>";
830       }
831         else
832       {
833         $msg=str_replace("\n"," ",$msg);
834       }
835       eval($this->DisplayFunc."(\"".$msg."\");");
836     }
837   }
838
839   function HandleError($msg)
840   {
841     if ($this->DisplayErrors)
842     {
843       $this->DisplayMsg($this->LastErrorMsg);
844     }
845   }
846
847 //********************************************************************************************************
848 // Checks if an error has occurred, and if so, displays a message & returns true
849 //********************************************************************************************************
850   function CheckForError($msg)
851   {
852     if (!$this->db->HasError()) return false;
853     $this->LastErrorMsg=$msg;
854     if (empty($this->ErrMsgFmt)) return true;
855     $this->HandleError($this->FormatErrorMsg($msg));
856     return true;
857   }
858
859 //********************************************************************************************************
860 // Attempts to connect to the Database. Returns true on success.
861 //********************************************************************************************************
862   function MySqlLogon($DefDB,$userid,$pw)
863   {
864     $this->Dialect="MySQL";
865     $this->dbDefault = $DefDB;
866     $this->dbMain = mysql_connect($this->Provider,$userid,$pw);
867     mysql_select_db($DefDB,$this->dbMain);
868     $this->db =& new dbClass_mysql($this->dbMain);
869     if ($this->CheckForError("opening connection")) return false;
870     return true;
871   }
872
873 //********************************************************************************************************
874 // Attempts to connect to the Database. Returns true on success.
875 //********************************************************************************************************
876   function OracleLogon($sim,$user,$pw)
877   {
878     $this->Dialect="Oracle";
879     $this->dbDefault = $user;
880     $this->dbMain = ocilogon($user,$pw,$sim);
881     $this->db =& new dbClass_oci($this->dbMain);
882     if ($this->CheckForError("opening connection")) return false;
883     $this->RunActionQuery("alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'");
884     return true;
885   }
886
887 //********************************************************************************************************
888 // Attempts to connect to the Database. Returns true on success.
889 //********************************************************************************************************
890   function MSSqlLogon($servername,$DefDB,$user,$pw)
891   {
892     $this->Dialect="TSQL";
893     $this->dbDefault = $DefDB;
894     ini_set("mssql.datetimeconvert","Off");
895     $this->dbMain = mssql_connect($servername,$user,$pw);
896     if (!is_resource($this->dbMain)) {
897       $this->LastErrorMsg="Error while connecting to ".$servername;
898       return false;
899     }
900     $this->db =& new dbClass_mssql($this->dbMain);
901     mssql_select_db($DefDB,$this->dbMain);
902     return true;
903   }
904
905 //********************************************************************************************************
906 // Attempts to connect to the Database. Returns true on success.
907 //********************************************************************************************************
908   function OdbcLogon($dsn,$DefDB,$userid,$pw)
909   {
910     $this->dsn = $dsn;
911     $this->dbDefault = $DefDB;
912     $this->dbMain = odbc_connect($dsn,$userid,$pw,SQL_CUR_USE_ODBC);
913     if (!is_resource($this->dbMain)) {
914       $this->LastErrorMsg="Error while opening ODBC connection: " . odbc_error();
915       return false;
916     } else {
917       $this->db = new dbClass_odbc($this);
918       return true;
919     }
920   }
921
922 //********************************************************************************************************
923 // Close database connection
924 //********************************************************************************************************
925   function dbClose() {
926     if (is_resource($this->dbMain)) $this->db->Close();
927     $this->dbMain = NULL;
928     return true;
929   }
930
931   function CurrentTime() {
932     return $this->db->CurrentTime();
933   }
934
935   function Convert2Char($s) {
936     return $this->db->Convert2Char($s);
937   }
938
939   // returns SQL that converts a datetime value to a date in YYYY-MM-DD format
940   function SqlDate($s) {
941     return $this->Concat(array($this->SqlYear($s),"'-'",$this->SqlMonth($s),"'-'",$this->SqlDay($s)),false);
942   }
943
944   // returns SQL that converts a datetime value to the day-of-month
945   function SqlDay($s) {
946     return $this->db->SqlDay($s);
947   }
948
949   // returns SQL that converts a datetime value to the month number
950   function SqlMonth($s) {
951     return $this->db->SqlMonth($s);
952   }
953
954   // returns SQL that converts a datetime value to the year
955   function SqlYear($s) {
956     return $this->db->SqlYear($s);
957   }
958
959   // requires an active db connection when using MySQL
960   function addQuotes($s) {
961     if (get_magic_quotes_gpc())
962       $s = stripslashes($s);
963     
964     switch ($this->Dialect) {
965       case "MySQL": return "'" . mysql_real_escape_string($s) . "'";
966       default:      return "'".str_replace("'","''",$s)."'";
967     }
968   }
969
970   // returns SQL that concatenates an array of strings
971   function Concat($arStrings, $addQuotes) {
972     if ($addQuotes) {
973       for ($i=0; $i<count($arStrings); $i++)
974         $arStrings[$i]=$this->addQuotes($arStrings[$i]);
975     }
976     return $this->db->Concat($arStrings);
977   }
978
979 //********************************************************************************************************
980 // Return a string containing an error message
981 // String format is based on ErrMsgFmt
982 //********************************************************************************************************
983   function FormatErrorMsg($ContextMsg)
984   {
985     switch ($this->ErrMsgFmt)
986     {
987       case "HTML":
988         $function_ret="<p class=dberror id=dbError>Error! " . $this->db->ErrorMsg() ."</p>".
989           "<p class=dberror id=dbErrorDetail><u>Operation that caused the error:</u><br>".$ContextMsg."</p>";
990         break;
991       case "MULTILINE":
992         $function_ret="Error! " . $this->db->ErrorMsg() ."\n\nOperation that caused the error:\n".$ContextMsg;
993         break;
994       case "1LINE":
995         $function_ret="Error! " . $this->db->ErrorMsg() ."  (".$ContextMsg.")";
996         break;
997     }
998     return $function_ret;
999   }
1000
1001 //********************************************************************************************************
1002 // Runs a query and moves to the first record.
1003 // Use only for queries that return records (no updates or deletes).
1004 // If the query generated an error then Nothing is returned, otherwise it returns a new recordset object.
1005 //********************************************************************************************************
1006   function RunQuery($sqltext) {
1007     $rsLookUp=$this->db->RunQuery($sqltext);
1008     if ($this->CheckForError($sqltext)) return null;
1009     if ($this->debug) $this->DisplayMsg($sqltext);
1010     return $rsLookUp;
1011   }
1012
1013
1014 //********************************************************************************************************
1015 // Runs a parameterized query (put ? in $sqltext to indicate where parameters should be inserted)
1016 // Use only for queries that return records (no updates or deletes).
1017 // If the query generated an error then null is returned, otherwise it returns a new recordset object.
1018 //********************************************************************************************************
1019   function RunParamQuery($sqltext, $arParams) {
1020     $rsLookUp=$this->db->RunParamQuery($sqltext, $arParams);
1021     if ($this->CheckForError($sqltext)) return null;
1022     if ($this->debug) $this->DisplayMsg($sqltext);
1023     return $rsLookUp;
1024   }
1025
1026
1027 //********************************************************************************************************
1028 // Safely close a recordset
1029 //********************************************************************************************************
1030   function rsClose($rsLookUp) {
1031     if (is_resource($rsLookUp)) $this->db->FreeResult($rsLookUp);
1032     $rsLookUp = NULL;
1033   }
1034
1035 //********************************************************************************************************
1036 // Runs a query and returns results from the first record in arData.
1037 // Returns true if arData is modified (ie. a record exists).
1038 // If the query generates an error then arData is left unchanged
1039 // returns arData as an array, fields indexed numerically
1040 //********************************************************************************************************
1041   function SingleRecordQuery($sqltext,&$arData)
1042   {
1043     $rsMain=$this->RunQuery($sqltext);
1044     if (!$rsMain) return false;
1045     $success=$this->db->FetchArray($rsMain,$arData);
1046     $this->rsClose($rsMain);
1047     return $success;
1048   }
1049
1050
1051 //********************************************************************************************************
1052 // Runs a query where no result set is expected (updates, deletes, etc)
1053 //   - returns the number of records affected by the action query
1054 //********************************************************************************************************
1055   function RunActionQuery($sqltext)
1056   {
1057     $rsMain=$this->db->RunQuery($sqltext);
1058     if ($this->CheckForError($sqltext)) {
1059       return 0;
1060     }
1061     else if ($this->debug) {
1062       $this->DisplayMsg($sqltext);
1063     }
1064     return $this->db->AffectedRows($rsMain);
1065   }
1066
1067
1068 //********************************************************************************************************
1069 // Runs a query where no result set is expected (updates, deletes, etc)
1070 //   - if an error occurs, then the message is returned in errmsg
1071 //********************************************************************************************************
1072   function RunActionQueryReturnMsg($sqltext,&$errmsg)
1073   {
1074     $tmpDisplayErrors=$this->DisplayErrors;
1075     $this->DisplayErrors=false;
1076     $this->LastErrorMsg="";
1077     $function_ret=$this->RunActionQuery($sqltext);
1078     if (!empty($this->LastErrorMsg))
1079     {
1080       $errmsg=$this->LastErrorMsg;
1081     }
1082     $this->DisplayErrors=$tmpDisplayErrors;
1083     return $function_ret;
1084   }
1085
1086
1087 //********************************************************************************************************
1088 // Takes a sql create (table or view) statement and performs:
1089 //   1) a conditional drop (if it already exists)
1090 //   2) the create
1091 //   3) grants select access to public (if not a temp table)
1092 //
1093 // for views, all actions must occur on the default database for the connection
1094 //********************************************************************************************************
1095   function DropCreate($sqlcreate)
1096   {
1097     $parsed=explode(" ",$sqlcreate);
1098     if (count($parsed) < 3) return;  // error
1099     $sqltext="DROP ".$parsed[1]." ".$parsed[2];
1100     $this->RunActionQueryReturnMsg($sqltext,$dropmsg);
1101     $this->RunActionQuery($sqlcreate);
1102   }
1103
1104 //********************************************************************************************************
1105 // Returns a comma-separated list of column names that make up the primary key
1106 // Returns empty string if no primary key has been defined
1107 //********************************************************************************************************
1108   function PrimaryKey($TableName) {
1109     $keys='';
1110     $arColumns=$this->GetColumnInfo($TableName);
1111     if (!is_array($arColumns)) return '';
1112     foreach ($arColumns as $colinfo) {
1113       if ($colinfo->IsPKey) {
1114         if ($keys!='') $keys.=',';
1115         $keys.=$colinfo->ColName;
1116       }
1117     }
1118     return $keys;
1119   }
1120
1121
1122 //********************************************************************************************************
1123 // Returns array of column info - one entry for each column in $TableName
1124 //********************************************************************************************************
1125   function GetColumnInfo($TableName) {
1126     return $this->db->GetColumnInfo($TableName);
1127   }
1128
1129
1130 //********************************************************************************************************
1131 // Returns array of table/view names
1132 // $ObjectType is one of 'VIEW' or 'TABLE'
1133 //********************************************************************************************************
1134   function GetTableList($ObjectType) {
1135     return $this->db->GetTableList($ObjectType);
1136   }
1137
1138
1139 //********************************************************************************************************
1140 // Add a condition to a where or having clause
1141 //********************************************************************************************************
1142   function AddCondition(&$WhereClause,$NewCondition)
1143   {
1144     if (empty($WhereClause))
1145       $WhereClause="(".$NewCondition.")";
1146     else
1147       $WhereClause.=" AND (".$NewCondition.")";
1148   }
1149
1150 }
1151 ?>