MDL-58546 libraries: Patch ADOdb for PHP 7.2 compat
[moodle.git] / lib / adodb / drivers / adodb-ado.inc.php
1 <?php
2 /*
3 @version   v5.20.9  21-Dec-2016
4 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
5 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
6   Released under both BSD license and Lesser GPL library license.
7   Whenever there is any discrepancy between the two licenses,
8   the BSD license will take precedence.
9 Set tabs to 4 for best viewing.
11   Latest version is available at http://adodb.sourceforge.net
13         Microsoft ADO data driver. Requires ADO. Works only on MS Windows.
14 */
16 // security - hide paths
17 if (!defined('ADODB_DIR')) die();
19 define("_ADODB_ADO_LAYER", 1 );
20 /*--------------------------------------------------------------------------------------
21 --------------------------------------------------------------------------------------*/
24 class ADODB_ado extends ADOConnection {
25         var $databaseType = "ado";
26         var $_bindInputArray = false;
27         var $fmtDate = "'Y-m-d'";
28         var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
29         var $replaceQuote = "''"; // string to use to replace quotes
30         var $dataProvider = "ado";
31         var $hasAffectedRows = true;
32         var $adoParameterType = 201; // 201 = long varchar, 203=long wide varchar, 205 = long varbinary
33         var $_affectedRows = false;
34         var $_thisTransactions;
35         var $_cursor_type = 3; // 3=adOpenStatic,0=adOpenForwardOnly,1=adOpenKeyset,2=adOpenDynamic
36         var $_cursor_location = 3; // 2=adUseServer, 3 = adUseClient;
37         var $_lock_type = -1;
38         var $_execute_option = -1;
39         var $poorAffectedRows = true;
40         var $charPage;
42         function __construct()
43         {
44                 $this->_affectedRows = new VARIANT;
45         }
47         function ServerInfo()
48         {
49                 if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider;
50                 return array('description' => $desc, 'version' => '');
51         }
53         function _affectedrows()
54         {
55                 if (PHP_VERSION >= 5) return $this->_affectedRows;
57                 return $this->_affectedRows->value;
58         }
60         // you can also pass a connection string like this:
61         //
62         // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB');
63         function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL')
64         {
65                 $u = 'UID';
66                 $p = 'PWD';
68                 if (!empty($this->charPage))
69                         $dbc = new COM('ADODB.Connection',null,$this->charPage);
70                 else
71                         $dbc = new COM('ADODB.Connection');
73                 if (! $dbc) return false;
75                 /* special support if provider is mssql or access */
76                 if ($argProvider=='mssql') {
77                         $u = 'User Id';  //User parameter name for OLEDB
78                         $p = 'Password';
79                         $argProvider = "SQLOLEDB"; // SQL Server Provider
81                         // not yet
82                         //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename";
84                         //use trusted conection for SQL if username not specified
85                         if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes";
86                 } else if ($argProvider=='access')
87                         $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider
89                 if ($argProvider) $dbc->Provider = $argProvider;
91                 if ($argUsername) $argHostname .= ";$u=$argUsername";
92                 if ($argPassword)$argHostname .= ";$p=$argPassword";
94                 if ($this->debug) ADOConnection::outp( "Host=".$argHostname."<BR>\n version=$dbc->version");
95                 // @ added below for php 4.0.1 and earlier
96                 @$dbc->Open((string) $argHostname);
98                 $this->_connectionID = $dbc;
100                 $dbc->CursorLocation = $this->_cursor_location;
101                 return  $dbc->State > 0;
102         }
104         // returns true or false
105         function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL')
106         {
107                 return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider);
108         }
110 /*
111         adSchemaCatalogs        = 1,
112         adSchemaCharacterSets   = 2,
113         adSchemaCollations      = 3,
114         adSchemaColumns = 4,
115         adSchemaCheckConstraints        = 5,
116         adSchemaConstraintColumnUsage   = 6,
117         adSchemaConstraintTableUsage    = 7,
118         adSchemaKeyColumnUsage  = 8,
119         adSchemaReferentialContraints   = 9,
120         adSchemaTableConstraints        = 10,
121         adSchemaColumnsDomainUsage      = 11,
122         adSchemaIndexes = 12,
123         adSchemaColumnPrivileges        = 13,
124         adSchemaTablePrivileges = 14,
125         adSchemaUsagePrivileges = 15,
126         adSchemaProcedures      = 16,
127         adSchemaSchemata        = 17,
128         adSchemaSQLLanguages    = 18,
129         adSchemaStatistics      = 19,
130         adSchemaTables  = 20,
131         adSchemaTranslations    = 21,
132         adSchemaProviderTypes   = 22,
133         adSchemaViews   = 23,
134         adSchemaViewColumnUsage = 24,
135         adSchemaViewTableUsage  = 25,
136         adSchemaProcedureParameters     = 26,
137         adSchemaForeignKeys     = 27,
138         adSchemaPrimaryKeys     = 28,
139         adSchemaProcedureColumns        = 29,
140         adSchemaDBInfoKeywords  = 30,
141         adSchemaDBInfoLiterals  = 31,
142         adSchemaCubes   = 32,
143         adSchemaDimensions      = 33,
144         adSchemaHierarchies     = 34,
145         adSchemaLevels  = 35,
146         adSchemaMeasures        = 36,
147         adSchemaProperties      = 37,
148         adSchemaMembers = 38
150 */
152         function MetaTables($ttype = false, $showSchema = false, $mask = false)
153         {
154                 $arr= array();
155                 $dbc = $this->_connectionID;
157                 $adors=@$dbc->OpenSchema(20);//tables
158                 if ($adors){
159                         $f = $adors->Fields(2);//table/view name
160                         $t = $adors->Fields(3);//table type
161                         while (!$adors->EOF){
162                                 $tt=substr($t->value,0,6);
163                                 if ($tt!='SYSTEM' && $tt !='ACCESS')
164                                         $arr[]=$f->value;
165                                 //print $f->value . ' ' . $t->value.'<br>';
166                                 $adors->MoveNext();
167                         }
168                         $adors->Close();
169                 }
171                 return $arr;
172         }
174         function MetaColumns($table, $normalize=true)
175         {
176                 $table = strtoupper($table);
177                 $arr = array();
178                 $dbc = $this->_connectionID;
180                 $adors=@$dbc->OpenSchema(4);//tables
182                 if ($adors){
183                         $t = $adors->Fields(2);//table/view name
184                         while (!$adors->EOF){
187                                 if (strtoupper($t->Value) == $table) {
189                                         $fld = new ADOFieldObject();
190                                         $c = $adors->Fields(3);
191                                         $fld->name = $c->Value;
192                                         $fld->type = 'CHAR'; // cannot discover type in ADO!
193                                         $fld->max_length = -1;
194                                         $arr[strtoupper($fld->name)]=$fld;
195                                 }
197                                 $adors->MoveNext();
198                         }
199                         $adors->Close();
200                 }
201                 $false = false;
202                 return empty($arr) ? $false : $arr;
203         }
208         /* returns queryID or false */
209         function _query($sql,$inputarr=false)
210         {
212                 $dbc = $this->_connectionID;
213                 $false = false;
215         //      return rs
216                 if ($inputarr) {
218                         if (!empty($this->charPage))
219                                 $oCmd = new COM('ADODB.Command',null,$this->charPage);
220                         else
221                                 $oCmd = new COM('ADODB.Command');
222                         $oCmd->ActiveConnection = $dbc;
223                         $oCmd->CommandText = $sql;
224                         $oCmd->CommandType = 1;
226       // Map by http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdmthcreateparam.asp
227       // Check issue http://bugs.php.net/bug.php?id=40664 !!!
228                         foreach ($inputarr as $val) {
229                                 $type = gettype($val);
230                                 $len=strlen($val);
231                                 if ($type == 'boolean')
232                                         $this->adoParameterType = 11;
233                                 else if ($type == 'integer')
234                                         $this->adoParameterType = 3;
235                                 else if ($type == 'double')
236                                         $this->adoParameterType = 5;
237                                 elseif ($type == 'string')
238                                         $this->adoParameterType = 202;
239                                 else if (($val === null) || (!defined($val)))
240                                         $len=1;
241                                 else
242                                         $this->adoParameterType = 130;
244                                 // name, type, direction 1 = input, len,
245                         $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
247                                 $oCmd->Parameters->Append($p);
248                         }
249                         $p = false;
250                         $rs = $oCmd->Execute();
251                         $e = $dbc->Errors;
252                         if ($dbc->Errors->Count > 0) return $false;
253                         return $rs;
254                 }
256                 $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option);
258                 if ($dbc->Errors->Count > 0) return $false;
259                 if (! $rs) return $false;
261                 if ($rs->State == 0) {
262                         $true = true;
263                         return $true; // 0 = adStateClosed means no records returned
264                 }
265                 return $rs;
266         }
269         function BeginTrans()
270         {
271                 if ($this->transOff) return true;
273                 if (isset($this->_thisTransactions))
274                         if (!$this->_thisTransactions) return false;
275                 else {
276                         $o = $this->_connectionID->Properties("Transaction DDL");
277                         $this->_thisTransactions = $o ? true : false;
278                         if (!$o) return false;
279                 }
280                 @$this->_connectionID->BeginTrans();
281                 $this->transCnt += 1;
282                 return true;
283         }
285         function CommitTrans($ok=true)
286         {
287                 if (!$ok) return $this->RollbackTrans();
288                 if ($this->transOff) return true;
290                 @$this->_connectionID->CommitTrans();
291                 if ($this->transCnt) @$this->transCnt -= 1;
292                 return true;
293         }
294         function RollbackTrans() {
295                 if ($this->transOff) return true;
296                 @$this->_connectionID->RollbackTrans();
297                 if ($this->transCnt) @$this->transCnt -= 1;
298                 return true;
299         }
301         /*      Returns: the last error message from previous database operation        */
303         function ErrorMsg()
304         {
305                 if (!$this->_connectionID) return "No connection established";
306                 $errc = $this->_connectionID->Errors;
307                 if (!$errc) return "No Errors object found";
308                 if ($errc->Count == 0) return '';
309                 $err = $errc->Item($errc->Count-1);
310                 return $err->Description;
311         }
313         function ErrorNo()
314         {
315                 $errc = $this->_connectionID->Errors;
316                 if ($errc->Count == 0) return 0;
317                 $err = $errc->Item($errc->Count-1);
318                 return $err->NativeError;
319         }
321         // returns true or false
322         function _close()
323         {
324                 if ($this->_connectionID) $this->_connectionID->Close();
325                 $this->_connectionID = false;
326                 return true;
327         }
332 /*--------------------------------------------------------------------------------------
333          Class Name: Recordset
334 --------------------------------------------------------------------------------------*/
336 class ADORecordSet_ado extends ADORecordSet {
338         var $bind = false;
339         var $databaseType = "ado";
340         var $dataProvider = "ado";
341         var $_tarr = false; // caches the types
342         var $_flds; // and field objects
343         var $canSeek = true;
344         var $hideErrors = true;
346         function __construct($id,$mode=false)
347         {
348                 if ($mode === false) {
349                         global $ADODB_FETCH_MODE;
350                         $mode = $ADODB_FETCH_MODE;
351                 }
352                 $this->fetchMode = $mode;
353                 return parent::__construct($id,$mode);
354         }
357         // returns the field object
358         function FetchField($fieldOffset = -1) {
359                 $off=$fieldOffset+1; // offsets begin at 1
361                 $o= new ADOFieldObject();
362                 $rs = $this->_queryID;
363                 $f = $rs->Fields($fieldOffset);
364                 $o->name = $f->Name;
365                 $t = $f->Type;
366                 $o->type = $this->MetaType($t);
367                 $o->max_length = $f->DefinedSize;
368                 $o->ado_type = $t;
370                 //print "off=$off name=$o->name type=$o->type len=$o->max_length<br>";
371                 return $o;
372         }
374         /* Use associative array to get fields array */
375         function Fields($colname)
376         {
377                 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
378                 if (!$this->bind) {
379                         $this->bind = array();
380                         for ($i=0; $i < $this->_numOfFields; $i++) {
381                                 $o = $this->FetchField($i);
382                                 $this->bind[strtoupper($o->name)] = $i;
383                         }
384                 }
386                  return $this->fields[$this->bind[strtoupper($colname)]];
387         }
390         function _initrs()
391         {
392                 $rs = $this->_queryID;
393                 $this->_numOfRows = $rs->RecordCount;
395                 $f = $rs->Fields;
396                 $this->_numOfFields = $f->Count;
397         }
400          // should only be used to move forward as we normally use forward-only cursors
401         function _seek($row)
402         {
403            $rs = $this->_queryID;
404                 // absoluteposition doesn't work -- my maths is wrong ?
405                 //      $rs->AbsolutePosition->$row-2;
406                 //      return true;
407                 if ($this->_currentRow > $row) return false;
408                 @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst
409                 return true;
410         }
412 /*
413         OLEDB types
415          enum DBTYPEENUM
416         {       DBTYPE_EMPTY    = 0,
417         DBTYPE_NULL     = 1,
418         DBTYPE_I2       = 2,
419         DBTYPE_I4       = 3,
420         DBTYPE_R4       = 4,
421         DBTYPE_R8       = 5,
422         DBTYPE_CY       = 6,
423         DBTYPE_DATE     = 7,
424         DBTYPE_BSTR     = 8,
425         DBTYPE_IDISPATCH        = 9,
426         DBTYPE_ERROR    = 10,
427         DBTYPE_BOOL     = 11,
428         DBTYPE_VARIANT  = 12,
429         DBTYPE_IUNKNOWN = 13,
430         DBTYPE_DECIMAL  = 14,
431         DBTYPE_UI1      = 17,
432         DBTYPE_ARRAY    = 0x2000,
433         DBTYPE_BYREF    = 0x4000,
434         DBTYPE_I1       = 16,
435         DBTYPE_UI2      = 18,
436         DBTYPE_UI4      = 19,
437         DBTYPE_I8       = 20,
438         DBTYPE_UI8      = 21,
439         DBTYPE_GUID     = 72,
440         DBTYPE_VECTOR   = 0x1000,
441         DBTYPE_RESERVED = 0x8000,
442         DBTYPE_BYTES    = 128,
443         DBTYPE_STR      = 129,
444         DBTYPE_WSTR     = 130,
445         DBTYPE_NUMERIC  = 131,
446         DBTYPE_UDT      = 132,
447         DBTYPE_DBDATE   = 133,
448         DBTYPE_DBTIME   = 134,
449         DBTYPE_DBTIMESTAMP      = 135
451         ADO Types
453         adEmpty = 0,
454         adTinyInt       = 16,
455         adSmallInt      = 2,
456         adInteger       = 3,
457         adBigInt        = 20,
458         adUnsignedTinyInt       = 17,
459         adUnsignedSmallInt      = 18,
460         adUnsignedInt   = 19,
461         adUnsignedBigInt        = 21,
462         adSingle        = 4,
463         adDouble        = 5,
464         adCurrency      = 6,
465         adDecimal       = 14,
466         adNumeric       = 131,
467         adBoolean       = 11,
468         adError = 10,
469         adUserDefined   = 132,
470         adVariant       = 12,
471         adIDispatch     = 9,
472         adIUnknown      = 13,
473         adGUID  = 72,
474         adDate  = 7,
475         adDBDate        = 133,
476         adDBTime        = 134,
477         adDBTimeStamp   = 135,
478         adBSTR  = 8,
479         adChar  = 129,
480         adVarChar       = 200,
481         adLongVarChar   = 201,
482         adWChar = 130,
483         adVarWChar      = 202,
484         adLongVarWChar  = 203,
485         adBinary        = 128,
486         adVarBinary     = 204,
487         adLongVarBinary = 205,
488         adChapter       = 136,
489         adFileTime      = 64,
490         adDBFileTime    = 137,
491         adPropVariant   = 138,
492         adVarNumeric    = 139
493 */
494         function MetaType($t,$len=-1,$fieldobj=false)
495         {
496                 if (is_object($t)) {
497                         $fieldobj = $t;
498                         $t = $fieldobj->type;
499                         $len = $fieldobj->max_length;
500                 }
502                 if (!is_numeric($t)) return $t;
504                 switch ($t) {
505                 case 0:
506                 case 12: // variant
507                 case 8: // bstr
508                 case 129: //char
509                 case 130: //wc
510                 case 200: // varc
511                 case 202:// varWC
512                 case 128: // bin
513                 case 204: // varBin
514                 case 72: // guid
515                         if ($len <= $this->blobSize) return 'C';
517                 case 201:
518                 case 203:
519                         return 'X';
520                 case 128:
521                 case 204:
522                 case 205:
523                          return 'B';
524                 case 7:
525                 case 133: return 'D';
527                 case 134:
528                 case 135: return 'T';
530                 case 11: return 'L';
532                 case 16://      adTinyInt       = 16,
533                 case 2://adSmallInt     = 2,
534                 case 3://adInteger      = 3,
535                 case 4://adBigInt       = 20,
536                 case 17://adUnsignedTinyInt     = 17,
537                 case 18://adUnsignedSmallInt    = 18,
538                 case 19://adUnsignedInt = 19,
539                 case 20://adUnsignedBigInt      = 21,
540                         return 'I';
541                 default: return 'N';
542                 }
543         }
545         // time stamp not supported yet
546         function _fetch()
547         {
548                 $rs = $this->_queryID;
549                 if (!$rs or $rs->EOF) {
550                         $this->fields = false;
551                         return false;
552                 }
553                 $this->fields = array();
555                 if (!$this->_tarr) {
556                         $tarr = array();
557                         $flds = array();
558                         for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
559                                 $f = $rs->Fields($i);
560                                 $flds[] = $f;
561                                 $tarr[] = $f->Type;
562                         }
563                         // bind types and flds only once
564                         $this->_tarr = $tarr;
565                         $this->_flds = $flds;
566                 }
567                 $t = reset($this->_tarr);
568                 $f = reset($this->_flds);
570                 if ($this->hideErrors)  $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null
571                 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
572                         //echo "<p>",$t,' ';var_dump($f->value); echo '</p>';
573                         switch($t) {
574                         case 135: // timestamp
575                                 if (!strlen((string)$f->value)) $this->fields[] = false;
576                                 else {
577                                         if (!is_numeric($f->value)) # $val = variant_date_to_timestamp($f->value);
578                                                 // VT_DATE stores dates as (float) fractional days since 1899/12/30 00:00:00
579                                                 $val=(float) variant_cast($f->value,VT_R8)*3600*24-2209161600;
580                                         else
581                                                 $val = $f->value;
582                                         $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
583                                 }
584                                 break;
585                         case 133:// A date value (yyyymmdd)
586                                 if ($val = $f->value) {
587                                         $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2);
588                                 } else
589                                         $this->fields[] = false;
590                                 break;
591                         case 7: // adDate
592                                 if (!strlen((string)$f->value)) $this->fields[] = false;
593                                 else {
594                                         if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value);
595                                         else $val = $f->value;
597                                         if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val);
598                                         else $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
599                                 }
600                                 break;
601                         case 1: // null
602                                 $this->fields[] = false;
603                                 break;
604                         case 6: // currency is not supported properly;
605                                 ADOConnection::outp( '<b>'.$f->Name.': currency type not supported by PHP</b>');
606                                 $this->fields[] = (float) $f->value;
607                                 break;
608                         case 11: //BIT;
609                                 $val = "";
610                                 if(is_bool($f->value))  {
611                                         if($f->value==true) $val = 1;
612                                         else $val = 0;
613                                 }
614                                 if(is_null($f->value)) $val = null;
616                                 $this->fields[] = $val;
617                                 break;
618                         default:
619                                 $this->fields[] = $f->value;
620                                 break;
621                         }
622                         //print " $f->value $t, ";
623                         $f = next($this->_flds);
624                         $t = next($this->_tarr);
625                 } // for
626                 if ($this->hideErrors) error_reporting($olde);
627                 @$rs->MoveNext(); // @ needed for some versions of PHP!
629                 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
630                         $this->fields = $this->GetRowAssoc();
631                 }
632                 return true;
633         }
635                 function NextRecordSet()
636                 {
637                         $rs = $this->_queryID;
638                         $this->_queryID = $rs->NextRecordSet();
639                         //$this->_queryID = $this->_QueryId->NextRecordSet();
640                         if ($this->_queryID == null) return false;
642                         $this->_currentRow = -1;
643                         $this->_currentPage = -1;
644                         $this->bind = false;
645                         $this->fields = false;
646                         $this->_flds = false;
647                         $this->_tarr = false;
649                         $this->_inited = false;
650                         $this->Init();
651                         return true;
652                 }
654         function _close() {
655                 $this->_flds = false;
656                 @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
657                 $this->_queryID = false;
658         }