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.
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;
46 var $_execute_option = -1;
47 var $poorAffectedRows = true;
50 function __construct()
52 $this->_affectedRows
= new VARIANT
;
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')
74 if (!empty($this->charPage
))
75 $dbc = new COM('ADODB.Connection',null,$this->charPage
);
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
85 $argProvider = "SQLOLEDB"; // SQL Server Provider
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,
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,
137 adSchemaTranslations = 21,
138 adSchemaProviderTypes = 22,
140 adSchemaViewColumnUsage = 24,
141 adSchemaViewTableUsage = 25,
142 adSchemaProcedureParameters = 26,
143 adSchemaForeignKeys = 27,
144 adSchemaPrimaryKeys = 28,
145 adSchemaProcedureColumns = 29,
146 adSchemaDBInfoKeywords = 30,
147 adSchemaDBInfoLiterals = 31,
149 adSchemaDimensions = 33,
150 adSchemaHierarchies = 34,
152 adSchemaMeasures = 36,
153 adSchemaProperties = 37,
158 function MetaTables($ttype = false, $showSchema = false, $mask = false)
161 $dbc = $this->_connectionID
;
163 $adors=@$dbc->OpenSchema(20);//tables
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')
171 //print $f->value . ' ' . $t->value.'<br>';
180 function MetaColumns($table, $normalize=true)
182 $table = strtoupper($table);
184 $dbc = $this->_connectionID
;
186 $adors=@$dbc->OpenSchema(4);//tables
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;
208 return empty($arr) ?
$false : $arr;
211 function _query($sql,$inputarr=false)
214 $dbc = $this->_connectionID
;
220 if (!empty($this->charPage
))
221 $oCmd = new COM('ADODB.Command',null,$this->charPage
);
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);
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)))
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);
252 $rs = $oCmd->Execute();
254 if ($dbc->Errors
->Count
> 0) return $false;
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) {
265 return $true; // 0 = adStateClosed means no records returned
271 function BeginTrans()
273 if ($this->transOff
) return true;
275 if (isset($this->_thisTransactions
))
276 if (!$this->_thisTransactions
) return false;
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;
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;
296 function RollbackTrans() {
297 if ($this->transOff
) return true;
298 @$this->_connectionID
->RollbackTrans();
299 if ($this->transCnt
) @$this->transCnt
-= 1;
303 /* Returns: the last error message from previous database operation */
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
;
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
326 if ($this->_connectionID
) $this->_connectionID
->Close();
327 $this->_connectionID
= false;
334 /*--------------------------------------------------------------------------------------
335 Class Name: Recordset
336 --------------------------------------------------------------------------------------*/
338 class ADORecordSet_ado
extends ADORecordSet
{
341 var $databaseType = "ado";
342 var $dataProvider = "ado";
343 var $_tarr = false; // caches the types
344 var $_flds; // and field objects
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);
368 $o->type
= $this->MetaType($t);
369 $o->max_length
= $f->DefinedSize
;
372 //print "off=$off name=$o->name type=$o->type len=$o->max_length<br>";
376 /* Use associative array to get fields array */
377 function Fields($colname)
379 if ($this->fetchMode
& ADODB_FETCH_ASSOC
) return $this->fields
[$colname];
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)]];
394 $rs = $this->_queryID
;
395 $this->_numOfRows
= $rs->RecordCount
;
398 $this->_numOfFields
= $f->Count
;
402 // should only be used to move forward as we normally use forward-only cursors
405 $rs = $this->_queryID
;
406 // absoluteposition doesn't work -- my maths is wrong ?
407 // $rs->AbsolutePosition->$row-2;
409 if ($this->_currentRow
> $row) return false;
410 @$rs->Move((integer)$row - $this->_currentRow
-1); //adBookmarkFirst
427 DBTYPE_IDISPATCH = 9,
431 DBTYPE_IUNKNOWN = 13,
434 DBTYPE_ARRAY = 0x2000,
435 DBTYPE_BYREF = 0x4000,
442 DBTYPE_VECTOR = 0x1000,
443 DBTYPE_RESERVED = 0x8000,
447 DBTYPE_NUMERIC = 131,
451 DBTYPE_DBTIMESTAMP = 135
460 adUnsignedTinyInt = 17,
461 adUnsignedSmallInt = 18,
463 adUnsignedBigInt = 21,
486 adLongVarWChar = 203,
489 adLongVarBinary = 205,
496 function MetaType($t,$len=-1,$fieldobj=false)
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;
520 if ($len <= $this->blobSize
) return 'C';
530 case 133: return 'D';
533 case 135: return 'T';
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,
546 default: return ADODB_DEFAULT_METATYPE
;
550 // time stamp not supported yet
553 $rs = $this->_queryID
;
554 if (!$rs or $rs->EOF
) {
555 $this->fields
= false;
558 $this->fields
= array();
563 for ($i=0,$max = $this->_numOfFields
; $i < $max; $i++
) {
564 $f = $rs->Fields($i);
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>';
579 case 135: // timestamp
580 if (!strlen((string)$f->value
)) $this->fields
[] = false;
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;
587 $this->fields
[] = adodb_date('Y-m-d H:i:s',$val);
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);
594 $this->fields
[] = false;
597 if (!strlen((string)$f->value
)) $this->fields
[] = false;
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);
607 $this->fields
[] = false;
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
;
615 if(is_bool($f->value
)) {
616 if($f->value
==true) $val = 1;
619 if(is_null($f->value
)) $val = null;
621 $this->fields
[] = $val;
624 $this->fields
[] = $f->value
;
627 //print " $f->value $t, ";
628 $f = next($this->_flds
);
629 $t = next($this->_tarr
);
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();
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;
650 $this->fields
= false;
651 $this->_flds
= false;
652 $this->_tarr
= false;
654 $this->_inited
= false;
660 $this->_flds
= false;
661 @$this->_queryID
->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
662 $this->_queryID
= false;