on-demand release 4.5dev+
[moodle.git] / lib / adodb / drivers / adodb-ado.inc.php
blobdf95c69012901f8d32421971d4cc0fecbd8b7afe
1 <?php
2 /**
3 * Microsoft ADO driver.
5 * Requires ADO. Works only on MS Windows.
7 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
9 * @package ADOdb
10 * @link https://adodb.org Project's web site and documentation
11 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
13 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
14 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
15 * any later version. This means you can use it in proprietary products.
16 * See the LICENSE.md file distributed with this source code for details.
17 * @license BSD-3-Clause
18 * @license LGPL-2.1-or-later
20 * @copyright 2000-2013 John Lim
21 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
24 // security - hide paths
25 if (!defined('ADODB_DIR')) die();
27 define("_ADODB_ADO_LAYER", 1 );
28 /*--------------------------------------------------------------------------------------
29 --------------------------------------------------------------------------------------*/
32 class ADODB_ado extends ADOConnection {
33 var $databaseType = "ado";
34 var $_bindInputArray = false;
35 var $fmtDate = "'Y-m-d'";
36 var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
37 var $replaceQuote = "''"; // string to use to replace quotes
38 var $dataProvider = "ado";
39 var $hasAffectedRows = true;
40 var $adoParameterType = 201; // 201 = long varchar, 203=long wide varchar, 205 = long varbinary
41 var $_affectedRows = false;
42 var $_thisTransactions;
43 var $_cursor_type = 3; // 3=adOpenStatic,0=adOpenForwardOnly,1=adOpenKeyset,2=adOpenDynamic
44 var $_cursor_location = 3; // 2=adUseServer, 3 = adUseClient;
45 var $_lock_type = -1;
46 var $_execute_option = -1;
47 var $poorAffectedRows = true;
48 var $charPage;
50 function __construct()
52 $this->_affectedRows = new VARIANT;
55 function ServerInfo()
57 if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider;
58 return array('description' => $desc, 'version' => '');
61 function _affectedrows()
63 return $this->_affectedRows;
66 // you can also pass a connection string like this:
68 // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB');
69 function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL')
71 $u = 'UID';
72 $p = 'PWD';
74 if (!empty($this->charPage))
75 $dbc = new COM('ADODB.Connection',null,$this->charPage);
76 else
77 $dbc = new COM('ADODB.Connection');
79 if (! $dbc) return false;
81 /* special support if provider is mssql or access */
82 if ($argProvider=='mssql') {
83 $u = 'User Id'; //User parameter name for OLEDB
84 $p = 'Password';
85 $argProvider = "SQLOLEDB"; // SQL Server Provider
87 // not yet
88 //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename";
90 //use trusted connection for SQL if username not specified
91 if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes";
92 } else if ($argProvider=='access')
93 $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider
95 if ($argProvider) $dbc->Provider = $argProvider;
97 if ($argUsername) $argHostname .= ";$u=$argUsername";
98 if ($argPassword)$argHostname .= ";$p=$argPassword";
100 if ($this->debug) ADOConnection::outp( "Host=".$argHostname."<BR>\n version=$dbc->version");
101 // @ added below for php 4.0.1 and earlier
102 @$dbc->Open((string) $argHostname);
104 $this->_connectionID = $dbc;
106 $dbc->CursorLocation = $this->_cursor_location;
107 return $dbc->State > 0;
110 // returns true or false
111 function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL')
113 return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider);
117 adSchemaCatalogs = 1,
118 adSchemaCharacterSets = 2,
119 adSchemaCollations = 3,
120 adSchemaColumns = 4,
121 adSchemaCheckConstraints = 5,
122 adSchemaConstraintColumnUsage = 6,
123 adSchemaConstraintTableUsage = 7,
124 adSchemaKeyColumnUsage = 8,
125 adSchemaReferentialContraints = 9,
126 adSchemaTableConstraints = 10,
127 adSchemaColumnsDomainUsage = 11,
128 adSchemaIndexes = 12,
129 adSchemaColumnPrivileges = 13,
130 adSchemaTablePrivileges = 14,
131 adSchemaUsagePrivileges = 15,
132 adSchemaProcedures = 16,
133 adSchemaSchemata = 17,
134 adSchemaSQLLanguages = 18,
135 adSchemaStatistics = 19,
136 adSchemaTables = 20,
137 adSchemaTranslations = 21,
138 adSchemaProviderTypes = 22,
139 adSchemaViews = 23,
140 adSchemaViewColumnUsage = 24,
141 adSchemaViewTableUsage = 25,
142 adSchemaProcedureParameters = 26,
143 adSchemaForeignKeys = 27,
144 adSchemaPrimaryKeys = 28,
145 adSchemaProcedureColumns = 29,
146 adSchemaDBInfoKeywords = 30,
147 adSchemaDBInfoLiterals = 31,
148 adSchemaCubes = 32,
149 adSchemaDimensions = 33,
150 adSchemaHierarchies = 34,
151 adSchemaLevels = 35,
152 adSchemaMeasures = 36,
153 adSchemaProperties = 37,
154 adSchemaMembers = 38
158 function MetaTables($ttype = false, $showSchema = false, $mask = false)
160 $arr= array();
161 $dbc = $this->_connectionID;
163 $adors=@$dbc->OpenSchema(20);//tables
164 if ($adors){
165 $f = $adors->Fields(2);//table/view name
166 $t = $adors->Fields(3);//table type
167 while (!$adors->EOF){
168 $tt=substr($t->value,0,6);
169 if ($tt!='SYSTEM' && $tt !='ACCESS')
170 $arr[]=$f->value;
171 //print $f->value . ' ' . $t->value.'<br>';
172 $adors->MoveNext();
174 $adors->Close();
177 return $arr;
180 function MetaColumns($table, $normalize=true)
182 $table = strtoupper($table);
183 $arr = array();
184 $dbc = $this->_connectionID;
186 $adors=@$dbc->OpenSchema(4);//tables
188 if ($adors){
189 $t = $adors->Fields(2);//table/view name
190 while (!$adors->EOF){
193 if (strtoupper($t->Value) == $table) {
195 $fld = new ADOFieldObject();
196 $c = $adors->Fields(3);
197 $fld->name = $c->Value;
198 $fld->type = 'CHAR'; // cannot discover type in ADO!
199 $fld->max_length = -1;
200 $arr[strtoupper($fld->name)]=$fld;
203 $adors->MoveNext();
205 $adors->Close();
207 $false = false;
208 return empty($arr) ? $false : $arr;
211 function _query($sql,$inputarr=false)
214 $dbc = $this->_connectionID;
215 $false = false;
217 // return rs
218 if ($inputarr) {
220 if (!empty($this->charPage))
221 $oCmd = new COM('ADODB.Command',null,$this->charPage);
222 else
223 $oCmd = new COM('ADODB.Command');
224 $oCmd->ActiveConnection = $dbc;
225 $oCmd->CommandText = $sql;
226 $oCmd->CommandType = 1;
228 // Map by http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdmthcreateparam.asp
229 // Check issue http://bugs.php.net/bug.php?id=40664 !!!
230 foreach ($inputarr as $val) {
231 $type = gettype($val);
232 $len=strlen($val);
233 if ($type == 'boolean')
234 $this->adoParameterType = 11;
235 else if ($type == 'integer')
236 $this->adoParameterType = 3;
237 else if ($type == 'double')
238 $this->adoParameterType = 5;
239 elseif ($type == 'string')
240 $this->adoParameterType = 202;
241 else if (($val === null) || (!defined($val)))
242 $len=1;
243 else
244 $this->adoParameterType = 130;
246 // name, type, direction 1 = input, len,
247 $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
249 $oCmd->Parameters->Append($p);
251 $p = false;
252 $rs = $oCmd->Execute();
253 $e = $dbc->Errors;
254 if ($dbc->Errors->Count > 0) return $false;
255 return $rs;
258 $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option);
260 if ($dbc->Errors->Count > 0) return $false;
261 if (! $rs) return $false;
263 if ($rs->State == 0) {
264 $true = true;
265 return $true; // 0 = adStateClosed means no records returned
267 return $rs;
271 function BeginTrans()
273 if ($this->transOff) return true;
275 if (isset($this->_thisTransactions))
276 if (!$this->_thisTransactions) return false;
277 else {
278 $o = $this->_connectionID->Properties("Transaction DDL");
279 $this->_thisTransactions = $o ? true : false;
280 if (!$o) return false;
282 @$this->_connectionID->BeginTrans();
283 $this->transCnt += 1;
284 return true;
287 function CommitTrans($ok=true)
289 if (!$ok) return $this->RollbackTrans();
290 if ($this->transOff) return true;
292 @$this->_connectionID->CommitTrans();
293 if ($this->transCnt) @$this->transCnt -= 1;
294 return true;
296 function RollbackTrans() {
297 if ($this->transOff) return true;
298 @$this->_connectionID->RollbackTrans();
299 if ($this->transCnt) @$this->transCnt -= 1;
300 return true;
303 /* Returns: the last error message from previous database operation */
305 function ErrorMsg()
307 if (!$this->_connectionID) return "No connection established";
308 $errc = $this->_connectionID->Errors;
309 if (!$errc) return "No Errors object found";
310 if ($errc->Count == 0) return '';
311 $err = $errc->Item($errc->Count-1);
312 return $err->Description;
315 function ErrorNo()
317 $errc = $this->_connectionID->Errors;
318 if ($errc->Count == 0) return 0;
319 $err = $errc->Item($errc->Count-1);
320 return $err->NativeError;
323 // returns true or false
324 function _close()
326 if ($this->_connectionID) $this->_connectionID->Close();
327 $this->_connectionID = false;
328 return true;
334 /*--------------------------------------------------------------------------------------
335 Class Name: Recordset
336 --------------------------------------------------------------------------------------*/
338 class ADORecordSet_ado extends ADORecordSet {
340 var $bind = false;
341 var $databaseType = "ado";
342 var $dataProvider = "ado";
343 var $_tarr = false; // caches the types
344 var $_flds; // and field objects
345 var $canSeek = true;
346 var $hideErrors = true;
348 function __construct($id,$mode=false)
350 if ($mode === false) {
351 global $ADODB_FETCH_MODE;
352 $mode = $ADODB_FETCH_MODE;
354 $this->fetchMode = $mode;
355 parent::__construct($id);
359 // returns the field object
360 function FetchField($fieldOffset = -1) {
361 $off=$fieldOffset+1; // offsets begin at 1
363 $o= new ADOFieldObject();
364 $rs = $this->_queryID;
365 $f = $rs->Fields($fieldOffset);
366 $o->name = $f->Name;
367 $t = $f->Type;
368 $o->type = $this->MetaType($t);
369 $o->max_length = $f->DefinedSize;
370 $o->ado_type = $t;
372 //print "off=$off name=$o->name type=$o->type len=$o->max_length<br>";
373 return $o;
376 /* Use associative array to get fields array */
377 function Fields($colname)
379 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
380 if (!$this->bind) {
381 $this->bind = array();
382 for ($i=0; $i < $this->_numOfFields; $i++) {
383 $o = $this->FetchField($i);
384 $this->bind[strtoupper($o->name)] = $i;
388 return $this->fields[$this->bind[strtoupper($colname)]];
392 function _initrs()
394 $rs = $this->_queryID;
395 $this->_numOfRows = $rs->RecordCount;
397 $f = $rs->Fields;
398 $this->_numOfFields = $f->Count;
402 // should only be used to move forward as we normally use forward-only cursors
403 function _seek($row)
405 $rs = $this->_queryID;
406 // absoluteposition doesn't work -- my maths is wrong ?
407 // $rs->AbsolutePosition->$row-2;
408 // return true;
409 if ($this->_currentRow > $row) return false;
410 @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst
411 return true;
415 OLEDB types
417 enum DBTYPEENUM
418 { DBTYPE_EMPTY = 0,
419 DBTYPE_NULL = 1,
420 DBTYPE_I2 = 2,
421 DBTYPE_I4 = 3,
422 DBTYPE_R4 = 4,
423 DBTYPE_R8 = 5,
424 DBTYPE_CY = 6,
425 DBTYPE_DATE = 7,
426 DBTYPE_BSTR = 8,
427 DBTYPE_IDISPATCH = 9,
428 DBTYPE_ERROR = 10,
429 DBTYPE_BOOL = 11,
430 DBTYPE_VARIANT = 12,
431 DBTYPE_IUNKNOWN = 13,
432 DBTYPE_DECIMAL = 14,
433 DBTYPE_UI1 = 17,
434 DBTYPE_ARRAY = 0x2000,
435 DBTYPE_BYREF = 0x4000,
436 DBTYPE_I1 = 16,
437 DBTYPE_UI2 = 18,
438 DBTYPE_UI4 = 19,
439 DBTYPE_I8 = 20,
440 DBTYPE_UI8 = 21,
441 DBTYPE_GUID = 72,
442 DBTYPE_VECTOR = 0x1000,
443 DBTYPE_RESERVED = 0x8000,
444 DBTYPE_BYTES = 128,
445 DBTYPE_STR = 129,
446 DBTYPE_WSTR = 130,
447 DBTYPE_NUMERIC = 131,
448 DBTYPE_UDT = 132,
449 DBTYPE_DBDATE = 133,
450 DBTYPE_DBTIME = 134,
451 DBTYPE_DBTIMESTAMP = 135
453 ADO Types
455 adEmpty = 0,
456 adTinyInt = 16,
457 adSmallInt = 2,
458 adInteger = 3,
459 adBigInt = 20,
460 adUnsignedTinyInt = 17,
461 adUnsignedSmallInt = 18,
462 adUnsignedInt = 19,
463 adUnsignedBigInt = 21,
464 adSingle = 4,
465 adDouble = 5,
466 adCurrency = 6,
467 adDecimal = 14,
468 adNumeric = 131,
469 adBoolean = 11,
470 adError = 10,
471 adUserDefined = 132,
472 adVariant = 12,
473 adIDispatch = 9,
474 adIUnknown = 13,
475 adGUID = 72,
476 adDate = 7,
477 adDBDate = 133,
478 adDBTime = 134,
479 adDBTimeStamp = 135,
480 adBSTR = 8,
481 adChar = 129,
482 adVarChar = 200,
483 adLongVarChar = 201,
484 adWChar = 130,
485 adVarWChar = 202,
486 adLongVarWChar = 203,
487 adBinary = 128,
488 adVarBinary = 204,
489 adLongVarBinary = 205,
490 adChapter = 136,
491 adFileTime = 64,
492 adDBFileTime = 137,
493 adPropVariant = 138,
494 adVarNumeric = 139
496 function MetaType($t,$len=-1,$fieldobj=false)
498 if (is_object($t)) {
499 $fieldobj = $t;
500 $t = $fieldobj->type;
501 $len = $fieldobj->max_length;
504 if (array_key_exists($t,$this->connection->customActualTypes))
505 return $this->connection->customActualTypes[$t];
507 if (!is_numeric($t)) return $t;
509 switch ($t) {
510 case 0:
511 case 12: // variant
512 case 8: // bstr
513 case 129: //char
514 case 130: //wc
515 case 200: // varc
516 case 202:// varWC
517 case 128: // bin
518 case 204: // varBin
519 case 72: // guid
520 if ($len <= $this->blobSize) return 'C';
522 case 201:
523 case 203:
524 return 'X';
525 case 128:
526 case 204:
527 case 205:
528 return 'B';
529 case 7:
530 case 133: return 'D';
532 case 134:
533 case 135: return 'T';
535 case 11: return 'L';
537 case 16:// adTinyInt = 16,
538 case 2://adSmallInt = 2,
539 case 3://adInteger = 3,
540 case 4://adBigInt = 20,
541 case 17://adUnsignedTinyInt = 17,
542 case 18://adUnsignedSmallInt = 18,
543 case 19://adUnsignedInt = 19,
544 case 20://adUnsignedBigInt = 21,
545 return 'I';
546 default: return ADODB_DEFAULT_METATYPE;
550 // time stamp not supported yet
551 function _fetch()
553 $rs = $this->_queryID;
554 if (!$rs or $rs->EOF) {
555 $this->fields = false;
556 return false;
558 $this->fields = array();
560 if (!$this->_tarr) {
561 $tarr = array();
562 $flds = array();
563 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
564 $f = $rs->Fields($i);
565 $flds[] = $f;
566 $tarr[] = $f->Type;
568 // bind types and flds only once
569 $this->_tarr = $tarr;
570 $this->_flds = $flds;
572 $t = reset($this->_tarr);
573 $f = reset($this->_flds);
575 if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null
576 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
577 //echo "<p>",$t,' ';var_dump($f->value); echo '</p>';
578 switch($t) {
579 case 135: // timestamp
580 if (!strlen((string)$f->value)) $this->fields[] = false;
581 else {
582 if (!is_numeric($f->value)) # $val = variant_date_to_timestamp($f->value);
583 // VT_DATE stores dates as (float) fractional days since 1899/12/30 00:00:00
584 $val=(float) variant_cast($f->value,VT_R8)*3600*24-2209161600;
585 else
586 $val = $f->value;
587 $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
589 break;
590 case 133:// A date value (yyyymmdd)
591 if ($val = $f->value) {
592 $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2);
593 } else
594 $this->fields[] = false;
595 break;
596 case 7: // adDate
597 if (!strlen((string)$f->value)) $this->fields[] = false;
598 else {
599 if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value);
600 else $val = $f->value;
602 if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val);
603 else $this->fields[] = adodb_date('Y-m-d H:i:s',$val);
605 break;
606 case 1: // null
607 $this->fields[] = false;
608 break;
609 case 6: // currency is not supported properly;
610 ADOConnection::outp( '<b>'.$f->Name.': currency type not supported by PHP</b>');
611 $this->fields[] = (float) $f->value;
612 break;
613 case 11: //BIT;
614 $val = "";
615 if(is_bool($f->value)) {
616 if($f->value==true) $val = 1;
617 else $val = 0;
619 if(is_null($f->value)) $val = null;
621 $this->fields[] = $val;
622 break;
623 default:
624 $this->fields[] = $f->value;
625 break;
627 //print " $f->value $t, ";
628 $f = next($this->_flds);
629 $t = next($this->_tarr);
630 } // for
631 if ($this->hideErrors) error_reporting($olde);
632 @$rs->MoveNext(); // @ needed for some versions of PHP!
634 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
635 $this->fields = $this->GetRowAssoc();
637 return true;
640 function NextRecordSet()
642 $rs = $this->_queryID;
643 $this->_queryID = $rs->NextRecordSet();
644 //$this->_queryID = $this->_QueryId->NextRecordSet();
645 if ($this->_queryID == null) return false;
647 $this->_currentRow = -1;
648 $this->_currentPage = -1;
649 $this->bind = false;
650 $this->fields = false;
651 $this->_flds = false;
652 $this->_tarr = false;
654 $this->_inited = false;
655 $this->Init();
656 return true;
659 function _close() {
660 $this->_flds = false;
661 @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
662 $this->_queryID = false;