MDL-58546 libraries: Patch ADOdb for PHP 7.2 compat
[moodle.git] / lib / adodb / drivers / adodb-ado5.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. PHP5 compat version.
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,$argDBorProvider, $argProvider= '')
64         {
65         // two modes
66         //      -       if $argProvider is empty, we assume that $argDBorProvider holds provider -- this is for backward compat
67         //      -       if $argProvider is not empty, then $argDBorProvider holds db
70                  if ($argProvider) {
71                         $argDatabasename = $argDBorProvider;
72                  } else {
73                         $argDatabasename = '';
74                         if ($argDBorProvider) $argProvider = $argDBorProvider;
75                         else if (stripos($argHostname,'PROVIDER') === false) /* full conn string is not in $argHostname */
76                                 $argProvider = 'MSDASQL';
77                 }
80                 try {
81                 $u = 'UID';
82                 $p = 'PWD';
84                 if (!empty($this->charPage))
85                         $dbc = new COM('ADODB.Connection',null,$this->charPage);
86                 else
87                         $dbc = new COM('ADODB.Connection');
89                 if (! $dbc) return false;
91                 /* special support if provider is mssql or access */
92                 if ($argProvider=='mssql') {
93                         $u = 'User Id';  //User parameter name for OLEDB
94                         $p = 'Password';
95                         $argProvider = "SQLOLEDB"; // SQL Server Provider
97                         // not yet
98                         //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename";
100                         //use trusted conection for SQL if username not specified
101                         if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes";
102                 } else if ($argProvider=='access')
103                         $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider
105                 if ($argProvider) $dbc->Provider = $argProvider;
107                 if ($argProvider) $argHostname = "PROVIDER=$argProvider;DRIVER={SQL Server};SERVER=$argHostname";
110                 if ($argDatabasename) $argHostname .= ";DATABASE=$argDatabasename";
111                 if ($argUsername) $argHostname .= ";$u=$argUsername";
112                 if ($argPassword)$argHostname .= ";$p=$argPassword";
114                 if ($this->debug) ADOConnection::outp( "Host=".$argHostname."<BR>\n version=$dbc->version");
115                 // @ added below for php 4.0.1 and earlier
116                 @$dbc->Open((string) $argHostname);
118                 $this->_connectionID = $dbc;
120                 $dbc->CursorLocation = $this->_cursor_location;
121                 return  $dbc->State > 0;
122                 } catch (exception $e) {
123                         if ($this->debug) echo "<pre>",$argHostname,"\n",$e,"</pre>\n";
124                 }
126                 return false;
127         }
129         // returns true or false
130         function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL')
131         {
132                 return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider);
133         }
135 /*
136         adSchemaCatalogs        = 1,
137         adSchemaCharacterSets   = 2,
138         adSchemaCollations      = 3,
139         adSchemaColumns = 4,
140         adSchemaCheckConstraints        = 5,
141         adSchemaConstraintColumnUsage   = 6,
142         adSchemaConstraintTableUsage    = 7,
143         adSchemaKeyColumnUsage  = 8,
144         adSchemaReferentialContraints   = 9,
145         adSchemaTableConstraints        = 10,
146         adSchemaColumnsDomainUsage      = 11,
147         adSchemaIndexes = 12,
148         adSchemaColumnPrivileges        = 13,
149         adSchemaTablePrivileges = 14,
150         adSchemaUsagePrivileges = 15,
151         adSchemaProcedures      = 16,
152         adSchemaSchemata        = 17,
153         adSchemaSQLLanguages    = 18,
154         adSchemaStatistics      = 19,
155         adSchemaTables  = 20,
156         adSchemaTranslations    = 21,
157         adSchemaProviderTypes   = 22,
158         adSchemaViews   = 23,
159         adSchemaViewColumnUsage = 24,
160         adSchemaViewTableUsage  = 25,
161         adSchemaProcedureParameters     = 26,
162         adSchemaForeignKeys     = 27,
163         adSchemaPrimaryKeys     = 28,
164         adSchemaProcedureColumns        = 29,
165         adSchemaDBInfoKeywords  = 30,
166         adSchemaDBInfoLiterals  = 31,
167         adSchemaCubes   = 32,
168         adSchemaDimensions      = 33,
169         adSchemaHierarchies     = 34,
170         adSchemaLevels  = 35,
171         adSchemaMeasures        = 36,
172         adSchemaProperties      = 37,
173         adSchemaMembers = 38
175 */
177         function MetaTables($ttype = false, $showSchema = false, $mask = false)
178         {
179                 $arr= array();
180                 $dbc = $this->_connectionID;
182                 $adors=@$dbc->OpenSchema(20);//tables
183                 if ($adors){
184                         $f = $adors->Fields(2);//table/view name
185                         $t = $adors->Fields(3);//table type
186                         while (!$adors->EOF){
187                                 $tt=substr($t->value,0,6);
188                                 if ($tt!='SYSTEM' && $tt !='ACCESS')
189                                         $arr[]=$f->value;
190                                 //print $f->value . ' ' . $t->value.'<br>';
191                                 $adors->MoveNext();
192                         }
193                         $adors->Close();
194                 }
196                 return $arr;
197         }
199         function MetaColumns($table, $normalize=true)
200         {
201                 $table = strtoupper($table);
202                 $arr= array();
203                 $dbc = $this->_connectionID;
205                 $adors=@$dbc->OpenSchema(4);//tables
207                 if ($adors){
208                         $t = $adors->Fields(2);//table/view name
209                         while (!$adors->EOF){
212                                 if (strtoupper($t->Value) == $table) {
214                                         $fld = new ADOFieldObject();
215                                         $c = $adors->Fields(3);
216                                         $fld->name = $c->Value;
217                                         $fld->type = 'CHAR'; // cannot discover type in ADO!
218                                         $fld->max_length = -1;
219                                         $arr[strtoupper($fld->name)]=$fld;
220                                 }
222                                 $adors->MoveNext();
223                         }
224                         $adors->Close();
225                 }
227                 return $arr;
228         }
230         /* returns queryID or false */
231         function _query($sql,$inputarr=false)
232         {
233                 try { // In PHP5, all COM errors are exceptions, so to maintain old behaviour...
235                 $dbc = $this->_connectionID;
237         //      return rs
239                 $false = false;
241                 if ($inputarr) {
243                         if (!empty($this->charPage))
244                                 $oCmd = new COM('ADODB.Command',null,$this->charPage);
245                         else
246                                 $oCmd = new COM('ADODB.Command');
247                         $oCmd->ActiveConnection = $dbc;
248                         $oCmd->CommandText = $sql;
249                         $oCmd->CommandType = 1;
251                         foreach ($inputarr as $val) {
252                                 $type = gettype($val);
253                                 $len=strlen($val);
254                                 if ($type == 'boolean')
255                                         $this->adoParameterType = 11;
256                                 else if ($type == 'integer')
257                                         $this->adoParameterType = 3;
258                                 else if ($type == 'double')
259                                         $this->adoParameterType = 5;
260                                 elseif ($type == 'string')
261                                         $this->adoParameterType = 202;
262                                 else if (($val === null) || (!defined($val)))
263                                         $len=1;
264                                 else
265                                         $this->adoParameterType = 130;
267                                 // name, type, direction 1 = input, len,
268                         $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
270                                 $oCmd->Parameters->Append($p);
271                         }
273                         $p = false;
274                         $rs = $oCmd->Execute();
275                         $e = $dbc->Errors;
276                         if ($dbc->Errors->Count > 0) return $false;
277                         return $rs;
278                 }
280                 $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option);
282                 if ($dbc->Errors->Count > 0) return $false;
283                 if (! $rs) return $false;
285                 if ($rs->State == 0) {
286                         $true = true;
287                         return $true; // 0 = adStateClosed means no records returned
288                 }
289                 return $rs;
291                 } catch (exception $e) {
293                 }
294                 return $false;
295         }
298         function BeginTrans()
299         {
300                 if ($this->transOff) return true;
302                 if (isset($this->_thisTransactions))
303                         if (!$this->_thisTransactions) return false;
304                 else {
305                         $o = $this->_connectionID->Properties("Transaction DDL");
306                         $this->_thisTransactions = $o ? true : false;
307                         if (!$o) return false;
308                 }
309                 @$this->_connectionID->BeginTrans();
310                 $this->transCnt += 1;
311                 return true;
312         }
313         function CommitTrans($ok=true)
314         {
315                 if (!$ok) return $this->RollbackTrans();
316                 if ($this->transOff) return true;
318                 @$this->_connectionID->CommitTrans();
319                 if ($this->transCnt) @$this->transCnt -= 1;
320                 return true;
321         }
322         function RollbackTrans() {
323                 if ($this->transOff) return true;
324                 @$this->_connectionID->RollbackTrans();
325                 if ($this->transCnt) @$this->transCnt -= 1;
326                 return true;
327         }
329         /*      Returns: the last error message from previous database operation        */
331         function ErrorMsg()
332         {
333                 if (!$this->_connectionID) return "No connection established";
334                 $errmsg = '';
336                 try {
337                         $errc = $this->_connectionID->Errors;
338                         if (!$errc) return "No Errors object found";
339                         if ($errc->Count == 0) return '';
340                         $err = $errc->Item($errc->Count-1);
341                         $errmsg = $err->Description;
342                 }catch(exception $e) {
343                 }
344                 return $errmsg;
345         }
347         function ErrorNo()
348         {
349                 $errc = $this->_connectionID->Errors;
350                 if ($errc->Count == 0) return 0;
351                 $err = $errc->Item($errc->Count-1);
352                 return $err->NativeError;
353         }
355         // returns true or false
356         function _close()
357         {
358                 if ($this->_connectionID) $this->_connectionID->Close();
359                 $this->_connectionID = false;
360                 return true;
361         }
366 /*--------------------------------------------------------------------------------------
367          Class Name: Recordset
368 --------------------------------------------------------------------------------------*/
370 class ADORecordSet_ado extends ADORecordSet {
372         var $bind = false;
373         var $databaseType = "ado";
374         var $dataProvider = "ado";
375         var $_tarr = false; // caches the types
376         var $_flds; // and field objects
377         var $canSeek = true;
378         var $hideErrors = true;
380         function __construct($id,$mode=false)
381         {
382                 if ($mode === false) {
383                         global $ADODB_FETCH_MODE;
384                         $mode = $ADODB_FETCH_MODE;
385                 }
386                 $this->fetchMode = $mode;
387                 return parent::__construct($id,$mode);
388         }
391         // returns the field object
392         function FetchField($fieldOffset = -1) {
393                 $off=$fieldOffset+1; // offsets begin at 1
395                 $o= new ADOFieldObject();
396                 $rs = $this->_queryID;
397                 if (!$rs) return false;
399                 $f = $rs->Fields($fieldOffset);
400                 $o->name = $f->Name;
401                 $t = $f->Type;
402                 $o->type = $this->MetaType($t);
403                 $o->max_length = $f->DefinedSize;
404                 $o->ado_type = $t;
407                 //print "off=$off name=$o->name type=$o->type len=$o->max_length<br>";
408                 return $o;
409         }
411         /* Use associative array to get fields array */
412         function Fields($colname)
413         {
414                 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
415                 if (!$this->bind) {
416                         $this->bind = array();
417                         for ($i=0; $i < $this->_numOfFields; $i++) {
418                                 $o = $this->FetchField($i);
419                                 $this->bind[strtoupper($o->name)] = $i;
420                         }
421                 }
423                  return $this->fields[$this->bind[strtoupper($colname)]];
424         }
427         function _initrs()
428         {
429                 $rs = $this->_queryID;
431                 try {
432                         $this->_numOfRows = $rs->RecordCount;
433                 } catch (Exception $e) {
434                         $this->_numOfRows = -1;
435                 }
436                 $f = $rs->Fields;
437                 $this->_numOfFields = $f->Count;
438         }
441          // should only be used to move forward as we normally use forward-only cursors
442         function _seek($row)
443         {
444            $rs = $this->_queryID;
445                 // absoluteposition doesn't work -- my maths is wrong ?
446                 //      $rs->AbsolutePosition->$row-2;
447                 //      return true;
448                 if ($this->_currentRow > $row) return false;
449                 @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst
450                 return true;
451         }
453 /*
454         OLEDB types
456          enum DBTYPEENUM
457         {       DBTYPE_EMPTY    = 0,
458         DBTYPE_NULL     = 1,
459         DBTYPE_I2       = 2,
460         DBTYPE_I4       = 3,
461         DBTYPE_R4       = 4,
462         DBTYPE_R8       = 5,
463         DBTYPE_CY       = 6,
464         DBTYPE_DATE     = 7,
465         DBTYPE_BSTR     = 8,
466         DBTYPE_IDISPATCH        = 9,
467         DBTYPE_ERROR    = 10,
468         DBTYPE_BOOL     = 11,
469         DBTYPE_VARIANT  = 12,
470         DBTYPE_IUNKNOWN = 13,
471         DBTYPE_DECIMAL  = 14,
472         DBTYPE_UI1      = 17,
473         DBTYPE_ARRAY    = 0x2000,
474         DBTYPE_BYREF    = 0x4000,
475         DBTYPE_I1       = 16,
476         DBTYPE_UI2      = 18,
477         DBTYPE_UI4      = 19,
478         DBTYPE_I8       = 20,
479         DBTYPE_UI8      = 21,
480         DBTYPE_GUID     = 72,
481         DBTYPE_VECTOR   = 0x1000,
482         DBTYPE_RESERVED = 0x8000,
483         DBTYPE_BYTES    = 128,
484         DBTYPE_STR      = 129,
485         DBTYPE_WSTR     = 130,
486         DBTYPE_NUMERIC  = 131,
487         DBTYPE_UDT      = 132,
488         DBTYPE_DBDATE   = 133,
489         DBTYPE_DBTIME   = 134,
490         DBTYPE_DBTIMESTAMP      = 135
492         ADO Types
494         adEmpty = 0,
495         adTinyInt       = 16,
496         adSmallInt      = 2,
497         adInteger       = 3,
498         adBigInt        = 20,
499         adUnsignedTinyInt       = 17,
500         adUnsignedSmallInt      = 18,
501         adUnsignedInt   = 19,
502         adUnsignedBigInt        = 21,
503         adSingle        = 4,
504         adDouble        = 5,
505         adCurrency      = 6,
506         adDecimal       = 14,
507         adNumeric       = 131,
508         adBoolean       = 11,
509         adError = 10,
510         adUserDefined   = 132,
511         adVariant       = 12,
512         adIDispatch     = 9,
513         adIUnknown      = 13,
514         adGUID  = 72,
515         adDate  = 7,
516         adDBDate        = 133,
517         adDBTime        = 134,
518         adDBTimeStamp   = 135,
519         adBSTR  = 8,
520         adChar  = 129,
521         adVarChar       = 200,
522         adLongVarChar   = 201,
523         adWChar = 130,
524         adVarWChar      = 202,
525         adLongVarWChar  = 203,
526         adBinary        = 128,
527         adVarBinary     = 204,
528         adLongVarBinary = 205,
529         adChapter       = 136,
530         adFileTime      = 64,
531         adDBFileTime    = 137,
532         adPropVariant   = 138,
533         adVarNumeric    = 139
534 */
535         function MetaType($t,$len=-1,$fieldobj=false)
536         {
537                 if (is_object($t)) {
538                         $fieldobj = $t;
539                         $t = $fieldobj->type;
540                         $len = $fieldobj->max_length;
541                 }
543                 if (!is_numeric($t)) return $t;
545                 switch ($t) {
546                 case 0:
547                 case 12: // variant
548                 case 8: // bstr
549                 case 129: //char
550                 case 130: //wc
551                 case 200: // varc
552                 case 202:// varWC
553                 case 128: // bin
554                 case 204: // varBin
555                 case 72: // guid
556                         if ($len <= $this->blobSize) return 'C';
558                 case 201:
559                 case 203:
560                         return 'X';
561                 case 128:
562                 case 204:
563                 case 205:
564                          return 'B';
565                 case 7:
566                 case 133: return 'D';
568                 case 134:
569                 case 135: return 'T';
571                 case 11: return 'L';
573                 case 16://      adTinyInt       = 16,
574                 case 2://adSmallInt     = 2,
575                 case 3://adInteger      = 3,
576                 case 4://adBigInt       = 20,
577                 case 17://adUnsignedTinyInt     = 17,
578                 case 18://adUnsignedSmallInt    = 18,
579                 case 19://adUnsignedInt = 19,
580                 case 20://adUnsignedBigInt      = 21,
581                         return 'I';
582                 default: return 'N';
583                 }
584         }
586         // time stamp not supported yet
587         function _fetch()
588         {
589                 $rs = $this->_queryID;
590                 if (!$rs or $rs->EOF) {
591                         $this->fields = false;
592                         return false;
593                 }
594                 $this->fields = array();
596                 if (!$this->_tarr) {
597                         $tarr = array();
598                         $flds = array();
599                         for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
600                                 $f = $rs->Fields($i);
601                                 $flds[] = $f;
602                                 $tarr[] = $f->Type;
603                         }
604                         // bind types and flds only once
605                         $this->_tarr = $tarr;
606                         $this->_flds = $flds;
607                 }
608                 $t = reset($this->_tarr);
609                 $f = reset($this->_flds);
611                 if ($this->hideErrors)  $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null
612                 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
613                         //echo "<p>",$t,' ';var_dump($f->value); echo '</p>';
614                         switch($t) {
615                         case 135: // timestamp
616                                 if (!strlen((string)$f->value)) $this->fields[] = false;
617                                 else {
618                                         if (!is_numeric($f->value)) # $val = variant_date_to_timestamp($f->value);
619                                                 // VT_DATE stores dates as (float) fractional days since 1899/12/30 00:00:00
620                                                 $val= (float) variant_cast($f->value,VT_R8)*3600*24-2209161600;
621                                         else
622                                                 $val = $f->value;
623                                         $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
624                                 }
625                                 break;
626                         case 133:// A date value (yyyymmdd)
627                                 if ($val = $f->value) {
628                                         $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2);
629                                 } else
630                                         $this->fields[] = false;
631                                 break;
632                         case 7: // adDate
633                                 if (!strlen((string)$f->value)) $this->fields[] = false;
634                                 else {
635                                         if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value);
636                                         else $val = $f->value;
638                                         if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val);
639                                         else $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
640                                 }
641                                 break;
642                         case 1: // null
643                                 $this->fields[] = false;
644                                 break;
645                         case 20:
646                         case 21: // bigint (64 bit)
647                         $this->fields[] = (float) $f->value; // if 64 bit PHP, could use (int)
648                         break;
649                         case 6: // currency is not supported properly;
650                                 ADOConnection::outp( '<b>'.$f->Name.': currency type not supported by PHP</b>');
651                                 $this->fields[] = (float) $f->value;
652                                 break;
653                         case 11: //BIT;
654                                 $val = "";
655                                 if(is_bool($f->value))  {
656                                         if($f->value==true) $val = 1;
657                                         else $val = 0;
658                                 }
659                                 if(is_null($f->value)) $val = null;
661                                 $this->fields[] = $val;
662                                 break;
663                         default:
664                                 $this->fields[] = $f->value;
665                                 break;
666                         }
667                         //print " $f->value $t, ";
668                         $f = next($this->_flds);
669                         $t = next($this->_tarr);
670                 } // for
671                 if ($this->hideErrors) error_reporting($olde);
672                 @$rs->MoveNext(); // @ needed for some versions of PHP!
674                 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
675                         $this->fields = $this->GetRowAssoc();
676                 }
677                 return true;
678         }
680                 function NextRecordSet()
681                 {
682                         $rs = $this->_queryID;
683                         $this->_queryID = $rs->NextRecordSet();
684                         //$this->_queryID = $this->_QueryId->NextRecordSet();
685                         if ($this->_queryID == null) return false;
687                         $this->_currentRow = -1;
688                         $this->_currentPage = -1;
689                         $this->bind = false;
690                         $this->fields = false;
691                         $this->_flds = false;
692                         $this->_tarr = false;
694                         $this->_inited = false;
695                         $this->Init();
696                         return true;
697                 }
699         function _close() {
700                 $this->_flds = false;
701                 try {
702                 @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
703                 } catch (Exception $e) {
704                 }
705                 $this->_queryID = false;
706         }