3 V4.22 15 Apr 2004 (c) 2000-2004 John Lim (jlim@natsoft.com.my). All rights reserved.
\r
4 Released under both BSD license and Lesser GPL library license.
\r
5 Whenever there is any discrepancy between the two licenses,
\r
6 the BSD license will take precedence.
\r
7 Set tabs to 4 for best viewing.
\r
9 Latest version is available at http://php.weblogs.com/
\r
11 Microsoft ADO data driver. Requires ADO. Works only on MS Windows.
\r
13 define("_ADODB_ADO_LAYER", 1 );
\r
14 /*--------------------------------------------------------------------------------------
\r
15 --------------------------------------------------------------------------------------*/
\r
17 class ADODB_ado extends ADOConnection {
\r
18 var $databaseType = "ado";
\r
19 var $_bindInputArray = false;
\r
20 var $fmtDate = "'Y-m-d'";
\r
21 var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
\r
22 var $replaceQuote = "''"; // string to use to replace quotes
\r
23 var $dataProvider = "ado";
\r
24 var $hasAffectedRows = true;
\r
25 var $adoParameterType = 201; // 201 = long varchar, 203=long wide varchar, 205 = long varbinary
\r
26 var $_affectedRows = false;
\r
27 var $_thisTransactions;
\r
28 var $_cursor_type = 3; // 3=adOpenStatic,0=adOpenForwardOnly,1=adOpenKeyset,2=adOpenDynamic
\r
29 var $_cursor_location = 3; // 2=adUseServer, 3 = adUseClient;
\r
30 var $_lock_type = -1;
\r
31 var $_execute_option = -1;
\r
32 var $poorAffectedRows = true;
\r
35 function ADODB_ado()
\r
37 $this->_affectedRows = new VARIANT;
\r
40 function ServerInfo()
\r
42 if (!empty($this->_connectionID)) $desc = $this->_connectionID->provider;
\r
43 return array('description' => $desc, 'version' => '');
\r
46 function _affectedrows()
\r
48 if (PHP_VERSION >= 5) return $this->_affectedRows;
\r
50 return $this->_affectedRows->value;
\r
53 // you can also pass a connection string like this:
\r
55 // $DB->Connect('USER ID=sa;PASSWORD=pwd;SERVER=mangrove;DATABASE=ai',false,false,'SQLOLEDB');
\r
56 function _connect($argHostname, $argUsername, $argPassword, $argProvider= 'MSDASQL')
\r
61 if (!empty($this->charPage))
\r
62 $dbc = new COM('ADODB.Connection',null,$this->charPage);
\r
64 $dbc = new COM('ADODB.Connection');
\r
66 if (! $dbc) return false;
\r
68 /* special support if provider is mssql or access */
\r
69 if ($argProvider=='mssql') {
\r
70 $u = 'User Id'; //User parameter name for OLEDB
\r
72 $argProvider = "SQLOLEDB"; // SQL Server Provider
\r
75 //if ($argDatabasename) $argHostname .= ";Initial Catalog=$argDatabasename";
\r
77 //use trusted conection for SQL if username not specified
\r
78 if (!$argUsername) $argHostname .= ";Trusted_Connection=Yes";
\r
79 } else if ($argProvider=='access')
\r
80 $argProvider = "Microsoft.Jet.OLEDB.4.0"; // Microsoft Jet Provider
\r
82 if ($argProvider) $dbc->Provider = $argProvider;
\r
84 if ($argUsername) $argHostname .= ";$u=$argUsername";
\r
85 if ($argPassword)$argHostname .= ";$p=$argPassword";
\r
87 if ($this->debug) ADOConnection::outp( "Host=".$argHostname."<BR>\n version=$dbc->version");
\r
88 // @ added below for php 4.0.1 and earlier
\r
89 @$dbc->Open((string) $argHostname);
\r
91 $this->_connectionID = $dbc;
\r
93 $dbc->CursorLocation = $this->_cursor_location;
\r
94 return $dbc->State > 0;
\r
97 // returns true or false
\r
98 function _pconnect($argHostname, $argUsername, $argPassword, $argProvider='MSDASQL')
\r
100 return $this->_connect($argHostname,$argUsername,$argPassword,$argProvider);
\r
104 adSchemaCatalogs = 1,
\r
105 adSchemaCharacterSets = 2,
\r
106 adSchemaCollations = 3,
\r
107 adSchemaColumns = 4,
\r
108 adSchemaCheckConstraints = 5,
\r
109 adSchemaConstraintColumnUsage = 6,
\r
110 adSchemaConstraintTableUsage = 7,
\r
111 adSchemaKeyColumnUsage = 8,
\r
112 adSchemaReferentialContraints = 9,
\r
113 adSchemaTableConstraints = 10,
\r
114 adSchemaColumnsDomainUsage = 11,
\r
115 adSchemaIndexes = 12,
\r
116 adSchemaColumnPrivileges = 13,
\r
117 adSchemaTablePrivileges = 14,
\r
118 adSchemaUsagePrivileges = 15,
\r
119 adSchemaProcedures = 16,
\r
120 adSchemaSchemata = 17,
\r
121 adSchemaSQLLanguages = 18,
\r
122 adSchemaStatistics = 19,
\r
123 adSchemaTables = 20,
\r
124 adSchemaTranslations = 21,
\r
125 adSchemaProviderTypes = 22,
\r
126 adSchemaViews = 23,
\r
127 adSchemaViewColumnUsage = 24,
\r
128 adSchemaViewTableUsage = 25,
\r
129 adSchemaProcedureParameters = 26,
\r
130 adSchemaForeignKeys = 27,
\r
131 adSchemaPrimaryKeys = 28,
\r
132 adSchemaProcedureColumns = 29,
\r
133 adSchemaDBInfoKeywords = 30,
\r
134 adSchemaDBInfoLiterals = 31,
\r
135 adSchemaCubes = 32,
\r
136 adSchemaDimensions = 33,
\r
137 adSchemaHierarchies = 34,
\r
138 adSchemaLevels = 35,
\r
139 adSchemaMeasures = 36,
\r
140 adSchemaProperties = 37,
\r
141 adSchemaMembers = 38
\r
145 function &MetaTables()
\r
148 $dbc = $this->_connectionID;
\r
150 $adors=@$dbc->OpenSchema(20);//tables
\r
152 $f = $adors->Fields(2);//table/view name
\r
153 $t = $adors->Fields(3);//table type
\r
154 while (!$adors->EOF){
\r
155 $tt=substr($t->value,0,6);
\r
156 if ($tt!='SYSTEM' && $tt !='ACCESS')
\r
158 //print $f->value . ' ' . $t->value.'<br>';
\r
159 $adors->MoveNext();
\r
167 function &MetaColumns($table)
\r
169 $table = strtoupper($table);
\r
171 $dbc = $this->_connectionID;
\r
173 $adors=@$dbc->OpenSchema(4);//tables
\r
176 $t = $adors->Fields(2);//table/view name
\r
177 while (!$adors->EOF){
\r
180 if (strtoupper($t->Value) == $table) {
\r
182 $fld = new ADOFieldObject();
\r
183 $c = $adors->Fields(3);
\r
184 $fld->name = $c->Value;
\r
185 $fld->type = 'CHAR'; // cannot discover type in ADO!
\r
186 $fld->max_length = -1;
\r
187 $arr[strtoupper($fld->name)]=$fld;
\r
190 $adors->MoveNext();
\r
201 /* returns queryID or false */
\r
202 function &_query($sql,$inputarr=false)
\r
205 $dbc = $this->_connectionID;
\r
210 if (!empty($this->charPage))
\r
211 $oCmd = new COM('ADODB.Command',null,$this->charPage);
\r
213 $oCmd = new COM('ADODB.Command');
\r
214 $oCmd->ActiveConnection = $dbc;
\r
215 $oCmd->CommandText = $sql;
\r
216 $oCmd->CommandType = 1;
\r
218 foreach($inputarr as $val) {
\r
219 // name, type, direction 1 = input, len,
\r
220 $this->adoParameterType = 130;
\r
221 $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val);
\r
222 //print $p->Type.' '.$p->value;
\r
223 $oCmd->Parameters->Append($p);
\r
226 $rs = $oCmd->Execute();
\r
228 if ($dbc->Errors->Count > 0) return false;
\r
232 $rs = @$dbc->Execute($sql,$this->_affectedRows, $this->_execute_option);
\r
234 $rs = new COM('ADODB.Recordset');
\r
236 $rs->Open ($sql, $dbc, $this->_cursor_type,$this->_lock_type, $this->_execute_option);
\r
239 if ($dbc->Errors->Count > 0) return false;
\r
240 if (! $rs) return false;
\r
242 if ($rs->State == 0) return true; // 0 = adStateClosed means no records returned
\r
247 function BeginTrans()
\r
249 if ($this->transOff) return true;
\r
251 if (isset($this->_thisTransactions))
\r
252 if (!$this->_thisTransactions) return false;
\r
254 $o = $this->_connectionID->Properties("Transaction DDL");
\r
255 $this->_thisTransactions = $o ? true : false;
\r
256 if (!$o) return false;
\r
258 @$this->_connectionID->BeginTrans();
\r
259 $this->transCnt += 1;
\r
262 function CommitTrans($ok=true)
\r
264 if (!$ok) return $this->RollbackTrans();
\r
265 if ($this->transOff) return true;
\r
267 @$this->_connectionID->CommitTrans();
\r
268 if ($this->transCnt) @$this->transCnt -= 1;
\r
271 function RollbackTrans() {
\r
272 if ($this->transOff) return true;
\r
273 @$this->_connectionID->RollbackTrans();
\r
274 if ($this->transCnt) @$this->transCnt -= 1;
\r
278 /* Returns: the last error message from previous database operation */
\r
280 function ErrorMsg()
\r
282 $errc = $this->_connectionID->Errors;
\r
283 if ($errc->Count == 0) return '';
\r
284 $err = $errc->Item($errc->Count-1);
\r
285 return $err->Description;
\r
288 function ErrorNo()
\r
290 $errc = $this->_connectionID->Errors;
\r
291 if ($errc->Count == 0) return 0;
\r
292 $err = $errc->Item($errc->Count-1);
\r
293 return $err->NativeError;
\r
296 // returns true or false
\r
299 if ($this->_connectionID) $this->_connectionID->Close();
\r
300 $this->_connectionID = false;
\r
307 /*--------------------------------------------------------------------------------------
\r
308 Class Name: Recordset
\r
309 --------------------------------------------------------------------------------------*/
\r
311 class ADORecordSet_ado extends ADORecordSet {
\r
314 var $databaseType = "ado";
\r
315 var $dataProvider = "ado";
\r
316 var $_tarr = false; // caches the types
\r
317 var $_flds; // and field objects
\r
318 var $canSeek = true;
\r
319 var $hideErrors = true;
\r
321 function ADORecordSet_ado($id,$mode=false)
\r
323 if ($mode === false) {
\r
324 global $ADODB_FETCH_MODE;
\r
325 $mode = $ADODB_FETCH_MODE;
\r
327 $this->fetchMode = $mode;
\r
328 return $this->ADORecordSet($id,$mode);
\r
332 // returns the field object
\r
333 function FetchField($fieldOffset = -1) {
\r
334 $off=$fieldOffset+1; // offsets begin at 1
\r
336 $o= new ADOFieldObject();
\r
337 $rs = $this->_queryID;
\r
338 $f = $rs->Fields($fieldOffset);
\r
339 $o->name = $f->Name;
\r
341 $o->type = $this->MetaType($t);
\r
342 $o->max_length = $f->DefinedSize;
\r
346 //print "off=$off name=$o->name type=$o->type len=$o->max_length<br>";
\r
350 /* Use associative array to get fields array */
\r
351 function Fields($colname)
\r
353 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
\r
354 if (!$this->bind) {
\r
355 $this->bind = array();
\r
356 for ($i=0; $i < $this->_numOfFields; $i++) {
\r
357 $o = $this->FetchField($i);
\r
358 $this->bind[strtoupper($o->name)] = $i;
\r
362 return $this->fields[$this->bind[strtoupper($colname)]];
\r
368 $rs = $this->_queryID;
\r
369 $this->_numOfRows = $rs->RecordCount;
\r
372 $this->_numOfFields = $f->Count;
\r
376 // should only be used to move forward as we normally use forward-only cursors
\r
377 function _seek($row)
\r
379 $rs = $this->_queryID;
\r
380 // absoluteposition doesn't work -- my maths is wrong ?
\r
381 // $rs->AbsolutePosition->$row-2;
\r
383 if ($this->_currentRow > $row) return false;
\r
384 @$rs->Move((integer)$row - $this->_currentRow-1); //adBookmarkFirst
\r
392 { DBTYPE_EMPTY = 0,
\r
401 DBTYPE_IDISPATCH = 9,
\r
404 DBTYPE_VARIANT = 12,
\r
405 DBTYPE_IUNKNOWN = 13,
\r
406 DBTYPE_DECIMAL = 14,
\r
408 DBTYPE_ARRAY = 0x2000,
\r
409 DBTYPE_BYREF = 0x4000,
\r
416 DBTYPE_VECTOR = 0x1000,
\r
417 DBTYPE_RESERVED = 0x8000,
\r
418 DBTYPE_BYTES = 128,
\r
421 DBTYPE_NUMERIC = 131,
\r
423 DBTYPE_DBDATE = 133,
\r
424 DBTYPE_DBTIME = 134,
\r
425 DBTYPE_DBTIMESTAMP = 135
\r
434 adUnsignedTinyInt = 17,
\r
435 adUnsignedSmallInt = 18,
\r
436 adUnsignedInt = 19,
\r
437 adUnsignedBigInt = 21,
\r
445 adUserDefined = 132,
\r
453 adDBTimeStamp = 135,
\r
457 adLongVarChar = 201,
\r
460 adLongVarWChar = 203,
\r
463 adLongVarBinary = 205,
\r
466 adDBFileTime = 137,
\r
467 adPropVariant = 138,
\r
470 function MetaType($t,$len=-1,$fieldobj=false)
\r
472 if (is_object($t)) {
\r
474 $t = $fieldobj->type;
\r
475 $len = $fieldobj->max_length;
\r
478 if (!is_numeric($t)) return $t;
\r
482 case 12: // variant
\r
489 case 204: // varBin
\r
491 if ($len <= $this->blobSize) return 'C';
\r
501 case 133: return 'D';
\r
504 case 135: return 'T';
\r
506 case 11: return 'L';
\r
508 case 16:// adTinyInt = 16,
\r
509 case 2://adSmallInt = 2,
\r
510 case 3://adInteger = 3,
\r
511 case 4://adBigInt = 20,
\r
512 case 17://adUnsignedTinyInt = 17,
\r
513 case 18://adUnsignedSmallInt = 18,
\r
514 case 19://adUnsignedInt = 19,
\r
515 case 20://adUnsignedBigInt = 21,
\r
517 default: return 'N';
\r
521 // time stamp not supported yet
\r
524 $rs = $this->_queryID;
\r
525 if (!$rs or $rs->EOF) {
\r
526 $this->fields = false;
\r
529 $this->fields = array();
\r
531 if (!$this->_tarr) {
\r
534 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
\r
535 $f = $rs->Fields($i);
\r
537 $tarr[] = $f->Type;
\r
539 // bind types and flds only once
\r
540 $this->_tarr = $tarr;
\r
541 $this->_flds = $flds;
\r
543 $t = reset($this->_tarr);
\r
544 $f = reset($this->_flds);
\r
546 if ($this->hideErrors) $olde = error_reporting(E_ERROR|E_CORE_ERROR);// sometimes $f->value be null
\r
547 for ($i=0,$max = $this->_numOfFields; $i < $max; $i++) {
\r
550 case 135: // timestamp
\r
551 if (!strlen((string)$f->value)) $this->fields[] = false;
\r
552 else $this->fields[] = adodb_date('Y-m-d H:i:s',(float)$f->value);
\r
554 case 133:// A date value (yyyymmdd)
\r
555 if ($val = $f->value) {
\r
556 $this->fields[] = substr($val,0,4).'-'.substr($val,4,2).'-'.substr($val,6,2);
\r
558 $this->fields[] = false;
\r
561 if (!strlen((string)$f->value)) $this->fields[] = false;
\r
562 else $this->fields[] = adodb_date('Y-m-d',(float)$f->value);
\r
565 $this->fields[] = false;
\r
567 case 6: // currency is not supported properly;
\r
568 ADOConnection::outp( '<b>'.$f->Name.': currency type not supported by PHP</b>');
\r
569 $this->fields[] = (float) $f->value;
\r
572 $this->fields[] = $f->value;
\r
575 //print " $f->value $t, ";
\r
576 $f = next($this->_flds);
\r
577 $t = next($this->_tarr);
\r
579 if ($this->hideErrors) error_reporting($olde);
\r
580 @$rs->MoveNext(); // @ needed for some versions of PHP!
\r
582 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
\r
583 $this->fields = &$this->GetRowAssoc(ADODB_ASSOC_CASE);
\r
588 function NextRecordSet()
\r
590 $rs = $this->_queryID;
\r
591 $this->_queryID = $rs->NextRecordSet();
\r
592 //$this->_queryID = $this->_QueryId->NextRecordSet();
\r
593 if ($this->_queryID == null) return false;
\r
595 $this->_currentRow = -1;
\r
596 $this->_currentPage = -1;
\r
597 $this->bind = false;
\r
598 $this->fields = false;
\r
599 $this->_flds = false;
\r
600 $this->_tarr = false;
\r
602 $this->_inited = false;
\r
607 function _close() {
\r
608 $this->_flds = false;
\r
609 @$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
\r
610 $this->_queryID = false;
\r