Imported Upstream version 0.8.7g
[debian/cacti] / lib / adodb / drivers / adodb-odbc.inc.php
1 <?php
2 /* 
3 V4.54 5 Nov 2004  (c) 2000-2004 John Lim (jlim#natsoft.com.my). All rights reserved.
4   Released under both BSD license and Lesser GPL library license. 
5   Whenever there is any discrepancy between the two licenses, 
6   the BSD license will take precedence. 
7 Set tabs to 4 for best viewing.
8   
9   Latest version is available at http://adodb.sourceforge.net
10   
11   Requires ODBC. Works on Windows and Unix.
12 */
13 // security - hide paths
14 if (!defined('ADODB_DIR')) die();
15
16   define("_ADODB_ODBC_LAYER", 2 );
17          
18 /*--------------------------------------------------------------------------------------
19 --------------------------------------------------------------------------------------*/
20
21
22 class ADODB_odbc extends ADOConnection {
23         var $databaseType = "odbc";     
24         var $fmtDate = "'Y-m-d'";
25         var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
26         var $replaceQuote = "''"; // string to use to replace quotes
27         var $dataProvider = "odbc";
28         var $hasAffectedRows = true;
29         var $binmode = ODBC_BINMODE_RETURN;
30         var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive
31                                                                 // breaking backward-compat
32         //var $longreadlen = 8000; // default number of chars to return for a Blob/Long field
33         var $_bindInputArray = false;   
34         var $curmode = SQL_CUR_USE_DRIVER; // See sqlext.h, SQL_CUR_DEFAULT == SQL_CUR_USE_DRIVER == 2L
35         var $_genSeqSQL = "create table %s (id integer)";
36         var $_autocommit = true;
37         var $_haserrorfunctions = true;
38         var $_has_stupid_odbc_fetch_api_change = true;
39         var $_lastAffectedRows = 0;
40         var $uCaseTables = true; // for meta* functions, uppercase table names
41         
42         function ADODB_odbc() 
43         {       
44                 $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050;
45                 $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200;
46         }
47         
48                 // returns true or false
49         function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
50         {
51         global $php_errormsg;
52                 
53                 if (!function_exists('odbc_connect')) return null;
54                 
55                 if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') {
56                         ADOConnection::outp("For odbc Connect(), $argDatabasename is not used. Place dsn in 1st parameter.");
57                 }
58                 if (isset($php_errormsg)) $php_errormsg = '';
59                 if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword);
60                 else $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword,$this->curmode);
61                 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
62                 if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
63                 
64                 return $this->_connectionID != false;
65         }
66         
67         // returns true or false
68         function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
69         {
70         global $php_errormsg;
71         
72                 if (!function_exists('odbc_connect')) return null;
73                 
74                 if (isset($php_errormsg)) $php_errormsg = '';
75                 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
76                 if ($this->debug && $argDatabasename) {
77                         ADOConnection::outp("For odbc PConnect(), $argDatabasename is not used. Place dsn in 1st parameter.");
78                 }
79         //      print "dsn=$argDSN u=$argUsername p=$argPassword<br>"; flush();
80                 if ($this->curmode === false) $this->_connectionID = odbc_connect($argDSN,$argUsername,$argPassword);
81                 else $this->_connectionID = odbc_pconnect($argDSN,$argUsername,$argPassword,$this->curmode);
82                 
83                 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
84                 if ($this->_connectionID && $this->autoRollback) @odbc_rollback($this->_connectionID);
85                 if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
86                 
87                 return $this->_connectionID != false;
88         }
89
90         
91         function ServerInfo()
92         {
93         
94                 if (!empty($this->host) && ADODB_PHPVER >= 0x4300) {
95                         $dsn = strtoupper($this->host);
96                         $first = true;
97                         $found = false;
98                         
99                         if (!function_exists('odbc_data_source')) return false;
100                         
101                         while(true) {
102                                 
103                                 $rez = @odbc_data_source($this->_connectionID,
104                                         $first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT);
105                                 $first = false;
106                                 if (!is_array($rez)) break;
107                                 if (strtoupper($rez['server']) == $dsn) {
108                                         $found = true;
109                                         break;
110                                 }
111                         } 
112                         if (!$found) return ADOConnection::ServerInfo();
113                         if (!isset($rez['version'])) $rez['version'] = '';
114                         return $rez;
115                 } else {
116                         return ADOConnection::ServerInfo();
117                 }
118         }
119
120         
121         function CreateSequence($seqname='adodbseq',$start=1)
122         {
123                 if (empty($this->_genSeqSQL)) return false;
124                 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
125                 if (!$ok) return false;
126                 $start -= 1;
127                 return $this->Execute("insert into $seqname values($start)");
128         }
129         
130         var $_dropSeqSQL = 'drop table %s';
131         function DropSequence($seqname)
132         {
133                 if (empty($this->_dropSeqSQL)) return false;
134                 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
135         }
136         
137         /*
138                 This algorithm is not very efficient, but works even if table locking
139                 is not available.
140                 
141                 Will return false if unable to generate an ID after $MAXLOOPS attempts.
142         */
143         function GenID($seq='adodbseq',$start=1)
144         {       
145                 // if you have to modify the parameter below, your database is overloaded,
146                 // or you need to implement generation of id's yourself!
147                 $MAXLOOPS = 100;
148                 //$this->debug=1;
149                 while (--$MAXLOOPS>=0) {
150                         $num = $this->GetOne("select id from $seq");
151                         if ($num === false) {
152                                 $this->Execute(sprintf($this->_genSeqSQL ,$seq));       
153                                 $start -= 1;
154                                 $num = '0';
155                                 $ok = $this->Execute("insert into $seq values($start)");
156                                 if (!$ok) return false;
157                         } 
158                         $this->Execute("update $seq set id=id+1 where id=$num");
159                         
160                         if ($this->affected_rows() > 0) {
161                                 $num += 1;
162                                 $this->genID = $num;
163                                 return $num;
164                         }
165                 }
166                 if ($fn = $this->raiseErrorFn) {
167                         $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num);
168                 }
169                 return false;
170         }
171
172
173         function ErrorMsg()
174         {
175                 if ($this->_haserrorfunctions) {
176                         if ($this->_errorMsg !== false) return $this->_errorMsg;
177                         if (empty($this->_connectionID)) return @odbc_errormsg();
178                         return @odbc_errormsg($this->_connectionID);
179                 } else return ADOConnection::ErrorMsg();
180         }
181         
182         function ErrorNo()
183         {
184                 
185                 if ($this->_haserrorfunctions) {
186                         if ($this->_errorCode !== false) {
187                                 // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
188                                 return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode;
189                         }
190
191                         if (empty($this->_connectionID)) $e = @odbc_error(); 
192                         else $e = @odbc_error($this->_connectionID);
193                         
194                          // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
195                          // so we check and patch
196                         if (strlen($e)<=2) return 0;
197                         return $e;
198                 } else return ADOConnection::ErrorNo();
199         }
200         
201         
202
203         function BeginTrans()
204         {       
205                 if (!$this->hasTransactions) return false;
206                 if ($this->transOff) return true; 
207                 $this->transCnt += 1;
208                 $this->_autocommit = false;
209                 return odbc_autocommit($this->_connectionID,false);
210         }
211         
212         function CommitTrans($ok=true) 
213         { 
214                 if ($this->transOff) return true; 
215                 if (!$ok) return $this->RollbackTrans();
216                 if ($this->transCnt) $this->transCnt -= 1;
217                 $this->_autocommit = true;
218                 $ret = odbc_commit($this->_connectionID);
219                 odbc_autocommit($this->_connectionID,true);
220                 return $ret;
221         }
222         
223         function RollbackTrans()
224         {
225                 if ($this->transOff) return true; 
226                 if ($this->transCnt) $this->transCnt -= 1;
227                 $this->_autocommit = true;
228                 $ret = odbc_rollback($this->_connectionID);
229                 odbc_autocommit($this->_connectionID,true);
230                 return $ret;
231         }
232         
233         function MetaPrimaryKeys($table)
234         {
235         global $ADODB_FETCH_MODE;
236         
237                 if ($this->uCaseTables) $table = strtoupper($table);
238                 $schema = '';
239                 $this->_findschema($table,$schema);
240
241                 $savem = $ADODB_FETCH_MODE;
242                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
243                 $qid = @odbc_primarykeys($this->_connectionID,'',$schema,$table);
244                 
245                 if (!$qid) {
246                         $ADODB_FETCH_MODE = $savem;
247                         return false;
248                 }
249                 $rs = new ADORecordSet_odbc($qid);
250                 $ADODB_FETCH_MODE = $savem;
251                 
252                 if (!$rs) return false;
253                 $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
254                 
255                 $arr =& $rs->GetArray();
256                 $rs->Close();
257                 //print_r($arr);
258                 $arr2 = array();
259                 for ($i=0; $i < sizeof($arr); $i++) {
260                         if ($arr[$i][3]) $arr2[] = $arr[$i][3];
261                 }
262                 return $arr2;
263         }
264         
265         
266         
267         function &MetaTables($ttype=false)
268         {
269         global $ADODB_FETCH_MODE;
270         
271                 $savem = $ADODB_FETCH_MODE;
272                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
273                 $qid = odbc_tables($this->_connectionID);
274                 
275                 $rs = new ADORecordSet_odbc($qid);
276                 
277                 $ADODB_FETCH_MODE = $savem;
278                 if (!$rs) {
279                         $false = false;
280                         return $false;
281                 }
282                 $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
283                 
284                 $arr =& $rs->GetArray();
285                 //print_r($arr);
286                 
287                 $rs->Close();
288                 $arr2 = array();
289                 
290                 if ($ttype) {
291                         $isview = strncmp($ttype,'V',1) === 0;
292                 }
293                 for ($i=0; $i < sizeof($arr); $i++) {
294                         if (!$arr[$i][2]) continue;
295                         $type = $arr[$i][3];
296                         if ($ttype) { 
297                                 if ($isview) {
298                                         if (strncmp($type,'V',1) === 0) $arr2[] = $arr[$i][2];
299                                 } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2];
300                         } else if (strncmp($type,'SYS',3) !== 0) $arr2[] = $arr[$i][2];
301                 }
302                 return $arr2;
303         }
304         
305 /*
306 See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcdatetime_data_type_changes.asp
307 / SQL data type codes /
308 #define SQL_UNKNOWN_TYPE        0
309 #define SQL_CHAR                        1
310 #define SQL_NUMERIC              2
311 #define SQL_DECIMAL              3
312 #define SQL_INTEGER              4
313 #define SQL_SMALLINT            5
314 #define SQL_FLOAT                  6
315 #define SQL_REAL                        7
316 #define SQL_DOUBLE                8
317 #if (ODBCVER >= 0x0300)
318 #define SQL_DATETIME            9
319 #endif
320 #define SQL_VARCHAR             12
321
322
323 / One-parameter shortcuts for date/time data types /
324 #if (ODBCVER >= 0x0300)
325 #define SQL_TYPE_DATE     91
326 #define SQL_TYPE_TIME     92
327 #define SQL_TYPE_TIMESTAMP 93
328
329 #define SQL_UNICODE                             (-95)
330 #define SQL_UNICODE_VARCHAR                     (-96)
331 #define SQL_UNICODE_LONGVARCHAR                 (-97)
332 */
333         function ODBCTypes($t)
334         {
335                 switch ((integer)$t) {
336                 case 1: 
337                 case 12:
338                 case 0:
339                 case -95:
340                 case -96:
341                         return 'C';
342                 case -97:
343                 case -1: //text
344                         return 'X';
345                 case -4: //image
346                         return 'B';
347                                 
348                 case 9: 
349                 case 91:
350                         return 'D';
351                 
352                 case 10:
353                 case 11:
354                 case 92:
355                 case 93:
356                         return 'T';
357                         
358                 case 4:
359                 case 5:
360                 case -6:
361                         return 'I';
362                         
363                 case -11: // uniqidentifier
364                         return 'R';
365                 case -7: //bit
366                         return 'L';
367                 
368                 default:
369                         return 'N';
370                 }
371         }
372         
373         function &MetaColumns($table)
374         {
375         global $ADODB_FETCH_MODE;
376         
377                 $false = false;
378                 if ($this->uCaseTables) $table = strtoupper($table);
379                 $schema = '';
380                 $this->_findschema($table,$schema);
381                 
382                 $savem = $ADODB_FETCH_MODE;
383                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
384         
385                 /*if (false) { // after testing, confirmed that the following does not work becoz of a bug
386                         $qid2 = odbc_tables($this->_connectionID);
387                         $rs = new ADORecordSet_odbc($qid2);             
388                         $ADODB_FETCH_MODE = $savem;
389                         if (!$rs) return false;
390                         $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
391                         $rs->_fetch();
392                         
393                         while (!$rs->EOF) {
394                                 if ($table == strtoupper($rs->fields[2])) {
395                                         $q = $rs->fields[0];
396                                         $o = $rs->fields[1];
397                                         break;
398                                 }
399                                 $rs->MoveNext();
400                         }
401                         $rs->Close();
402                         
403                         $qid = odbc_columns($this->_connectionID,$q,$o,strtoupper($table),'%');
404                 } */
405                 
406                 switch ($this->databaseType) {
407                 case 'access':
408                 case 'vfp':
409                         $qid = odbc_columns($this->_connectionID);#,'%','',strtoupper($table),'%');
410                         break;
411                 
412                 
413                 case 'db2':
414             $colname = "%";
415             $qid = odbc_columns($this->_connectionID, "", $schema, $table, $colname);
416             break;
417                         
418                 default:
419                         $qid = @odbc_columns($this->_connectionID,'%','%',strtoupper($table),'%');
420                         if (empty($qid)) $qid = odbc_columns($this->_connectionID);
421                         break;
422                 }
423                 if (empty($qid)) return $false;
424                 
425                 $rs =& new ADORecordSet_odbc($qid);
426                 $ADODB_FETCH_MODE = $savem;
427                 
428                 if (!$rs) return $false;
429                 $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
430                 $rs->_fetch();
431                 
432                 $retarr = array();
433                 
434                 /*
435                 $rs->fields indices
436                 0 TABLE_QUALIFIER
437                 1 TABLE_SCHEM
438                 2 TABLE_NAME
439                 3 COLUMN_NAME
440                 4 DATA_TYPE
441                 5 TYPE_NAME
442                 6 PRECISION
443                 7 LENGTH
444                 8 SCALE
445                 9 RADIX
446                 10 NULLABLE
447                 11 REMARKS
448                 */
449                 while (!$rs->EOF) {
450                 //      adodb_pr($rs->fields);
451                         if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
452                                 $fld = new ADOFieldObject();
453                                 $fld->name = $rs->fields[3];
454                                 $fld->type = $this->ODBCTypes($rs->fields[4]);
455                                 
456                                 // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
457                                 // access uses precision to store length for char/varchar
458                                 if ($fld->type == 'C' or $fld->type == 'X') {
459                                         if ($this->databaseType == 'access') 
460                                                 $fld->max_length = $rs->fields[6];
461                                         else if ($rs->fields[4] <= -95) // UNICODE
462                                                 $fld->max_length = $rs->fields[7]/2;
463                                         else
464                                                 $fld->max_length = $rs->fields[7];
465                                 } else 
466                                         $fld->max_length = $rs->fields[7];
467                                 $fld->not_null = !empty($rs->fields[10]);
468                                 $fld->scale = $rs->fields[8];
469                                 $retarr[strtoupper($fld->name)] = $fld; 
470                         } else if (sizeof($retarr)>0)
471                                 break;
472                         $rs->MoveNext();
473                 }
474                 $rs->Close(); //-- crashes 4.03pl1 -- why?
475                 
476                 return empty($retarr) ? $false : $retarr;
477         }
478         
479         function Prepare($sql)
480         {
481                 if (! $this->_bindInputArray) return $sql; // no binding
482                 $stmt = odbc_prepare($this->_connectionID,$sql);
483                 if (!$stmt) {
484                         // we don't know whether odbc driver is parsing prepared stmts, so just return sql
485                         return $sql;
486                 }
487                 return array($sql,$stmt,false);
488         }
489
490         /* returns queryID or false */
491         function _query($sql,$inputarr=false) 
492         {
493         GLOBAL $php_errormsg;
494                 if (isset($php_errormsg)) $php_errormsg = '';
495                 $this->_error = '';
496                 
497                 if ($inputarr) {
498                         if (is_array($sql)) {
499                                 $stmtid = $sql[1];
500                         } else {
501                                 $stmtid = odbc_prepare($this->_connectionID,$sql);
502         
503                                 if ($stmtid == false) {
504                                         $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
505                                         return false;
506                                 }
507                         }
508                         
509                         if (! odbc_execute($stmtid,$inputarr)) {
510                                 //@odbc_free_result($stmtid);
511                                 if ($this->_haserrorfunctions) {
512                                         $this->_errorMsg = odbc_errormsg();
513                                         $this->_errorCode = odbc_error();
514                                 }
515                                 return false;
516                         }
517                 
518                 } else if (is_array($sql)) {
519                         $stmtid = $sql[1];
520                         if (!odbc_execute($stmtid)) {
521                                 //@odbc_free_result($stmtid);
522                                 if ($this->_haserrorfunctions) {
523                                         $this->_errorMsg = odbc_errormsg();
524                                         $this->_errorCode = odbc_error();
525                                 }
526                                 return false;
527                         }
528                 } else
529                         $stmtid = odbc_exec($this->_connectionID,$sql);
530                 
531                 $this->_lastAffectedRows = 0;
532                 if ($stmtid) {
533                         if (@odbc_num_fields($stmtid) == 0) {
534                                 $this->_lastAffectedRows = odbc_num_rows($stmtid);
535                                 $stmtid = true;
536                         } else {
537                                 $this->_lastAffectedRows = 0;
538                                 odbc_binmode($stmtid,$this->binmode);
539                                 odbc_longreadlen($stmtid,$this->maxblobsize);
540                         }
541                         
542                         if ($this->_haserrorfunctions) {
543                                 $this->_errorMsg = '';
544                                 $this->_errorCode = 0;
545                         } else
546                                 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
547                 } else {
548                         if ($this->_haserrorfunctions) {
549                                 $this->_errorMsg = odbc_errormsg();
550                                 $this->_errorCode = odbc_error();
551                         } else
552                                 $this->_errorMsg = isset($php_errormsg) ? $php_errormsg : '';
553                 }
554                 return $stmtid;
555         }
556
557         /*
558                 Insert a null into the blob field of the table first.
559                 Then use UpdateBlob to store the blob.
560                 
561                 Usage:
562                  
563                 $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
564                 $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
565         */
566         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
567         {
568                 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
569         }
570         
571         // returns true or false
572         function _close()
573         {
574                 $ret = @odbc_close($this->_connectionID);
575                 $this->_connectionID = false;
576                 return $ret;
577         }
578
579         function _affectedrows()
580         {
581                 return $this->_lastAffectedRows;
582         }
583         
584 }
585         
586 /*--------------------------------------------------------------------------------------
587          Class Name: Recordset
588 --------------------------------------------------------------------------------------*/
589
590 class ADORecordSet_odbc extends ADORecordSet {  
591         
592         var $bind = false;
593         var $databaseType = "odbc";             
594         var $dataProvider = "odbc";
595         var $useFetchArray;
596         var $_has_stupid_odbc_fetch_api_change;
597         
598         function ADORecordSet_odbc($id,$mode=false)
599         {
600                 if ($mode === false) {  
601                         global $ADODB_FETCH_MODE;
602                         $mode = $ADODB_FETCH_MODE;
603                 }
604                 $this->fetchMode = $mode;
605                 
606                 $this->_queryID = $id;
607                 
608                 // the following is required for mysql odbc driver in 4.3.1 -- why?
609                 $this->EOF = false;
610                 $this->_currentRow = -1;
611                 //$this->ADORecordSet($id);
612         }
613
614
615         // returns the field object
616         function &FetchField($fieldOffset = -1) 
617         {
618                 
619                 $off=$fieldOffset+1; // offsets begin at 1
620                 
621                 $o= new ADOFieldObject();
622                 $o->name = @odbc_field_name($this->_queryID,$off);
623                 $o->type = @odbc_field_type($this->_queryID,$off);
624                 $o->max_length = @odbc_field_len($this->_queryID,$off);
625                 if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
626                 else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
627                 return $o;
628         }
629         
630         /* Use associative array to get fields array */
631         function Fields($colname)
632         {
633                 if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
634                 if (!$this->bind) {
635                         $this->bind = array();
636                         for ($i=0; $i < $this->_numOfFields; $i++) {
637                                 $o = $this->FetchField($i);
638                                 $this->bind[strtoupper($o->name)] = $i;
639                         }
640                 }
641
642                  return $this->fields[$this->bind[strtoupper($colname)]];
643         }
644         
645                 
646         function _initrs()
647         {
648         global $ADODB_COUNTRECS;
649                 $this->_numOfRows = ($ADODB_COUNTRECS) ? @odbc_num_rows($this->_queryID) : -1;
650                 $this->_numOfFields = @odbc_num_fields($this->_queryID);
651                 // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
652                 if ($this->_numOfRows == 0) $this->_numOfRows = -1;
653                 //$this->useFetchArray = $this->connection->useFetchArray;
654                 $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200;
655         }       
656         
657         function _seek($row)
658         {
659                 return false;
660         }
661         
662         // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated
663         function &GetArrayLimit($nrows,$offset=-1) 
664         {
665                 if ($offset <= 0) {
666                         $rs =& $this->GetArray($nrows);
667                         return $rs;
668                 }
669                 $savem = $this->fetchMode;
670                 $this->fetchMode = ADODB_FETCH_NUM;
671                 $this->Move($offset);
672                 $this->fetchMode = $savem;
673                 
674                 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
675                         $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE);
676                 }
677                 
678                 $results = array();
679                 $cnt = 0;
680                 while (!$this->EOF && $nrows != $cnt) {
681                         $results[$cnt++] = $this->fields;
682                         $this->MoveNext();
683                 }
684                 
685                 return $results;
686         }
687         
688         
689         function MoveNext() 
690         {
691                 if ($this->_numOfRows != 0 && !$this->EOF) {            
692                         $this->_currentRow++;
693                         
694                         if ($this->_has_stupid_odbc_fetch_api_change)
695                                 $rez = @odbc_fetch_into($this->_queryID,$this->fields);
696                         else {
697                                 $row = 0;
698                                 $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields);
699                         }
700                         if ($rez) {
701                                 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
702                                         $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE);
703                                 }
704                                 return true;
705                         }
706                 }
707                 $this->fields = false;
708                 $this->EOF = true;
709                 return false;
710         }       
711         
712         function _fetch()
713         {
714                 
715                 if ($this->_has_stupid_odbc_fetch_api_change)
716                         $rez = @odbc_fetch_into($this->_queryID,$this->fields,$row);
717                 else {
718                         $row = 0;
719                         $rez = @odbc_fetch_into($this->_queryID,$row,$this->fields);
720                 }
721                 if ($rez) {
722                         if ($this->fetchMode & ADODB_FETCH_ASSOC) {
723                                 $this->fields =& $this->GetRowAssoc(ADODB_ASSOC_CASE);
724                         }
725                         return true;
726                 }
727                 $this->fields = false;
728                 return false;
729         }
730         
731         function _close() 
732         {
733                 return @odbc_free_result($this->_queryID);              
734         }
735
736 }
737
738 ?>