Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  /*
   3    (c) 2000-2014 John Lim (jlim#natsoft.com.my). All rights reserved.
   4    Portions Copyright (c) 2007-2009, iAnywhere Solutions, Inc.
   5    All rights reserved. All unpublished rights reserved.
   6  
   7    Released under both BSD license and Lesser GPL library license.
   8    Whenever there is any discrepancy between the two licenses,
   9    the BSD license will take precedence.
  10  
  11  Set tabs to 4 for best viewing.
  12  
  13  
  14  NOTE: This driver requires the Advantage PHP client libraries, which
  15        can be downloaded for free via:
  16        http://devzone.advantagedatabase.com/dz/content.aspx?key=20
  17  
  18  DELPHI FOR PHP USERS:
  19        The following steps can be taken to utilize this driver from the
  20        CodeGear Delphi for PHP product:
  21          1 - See note above, download and install the Advantage PHP client.
  22          2 - Copy the following files to the Delphi for PHP\X.X\php\ext directory:
  23                ace32.dll
  24                axcws32.dll
  25                adsloc32.dll
  26                php_advantage.dll (rename the existing php_advantage.dll.5.x.x file)
  27          3 - Add the following line to the Delphi for PHP\X.X\php\php.ini.template file:
  28                extension=php_advantage.dll
  29          4 - To use: enter "ads" as the DriverName on a connection component, and set
  30              a Host property similar to "DataDirectory=c:\". See the Advantage PHP
  31              help file topic for ads_connect for details on connection path options
  32              and formatting.
  33          5 - (optional) - Modify the Delphi for PHP\X.X\vcl\packages\database.packages.php
  34              file and add ads to the list of strings returned when registering the
  35              Database object's DriverName property.
  36  
  37  */
  38  // security - hide paths
  39  if (!defined('ADODB_DIR')) die();
  40  
  41    define("_ADODB_ADS_LAYER", 2 );
  42  
  43  /*--------------------------------------------------------------------------------------
  44  --------------------------------------------------------------------------------------*/
  45  
  46  
  47  class ADODB_ads extends ADOConnection {
  48    var $databaseType = "ads";
  49    var $fmt = "'m-d-Y'";
  50    var $fmtTimeStamp = "'Y-m-d H:i:s'";
  51          var $concat_operator = '';
  52    var $replaceQuote = "''"; // string to use to replace quotes
  53    var $dataProvider = "ads";
  54    var $hasAffectedRows = true;
  55    var $binmode = ODBC_BINMODE_RETURN;
  56    var $useFetchArray = false; // setting this to true will make array elements in FETCH_ASSOC mode case-sensitive
  57                          // breaking backward-compat
  58    //var $longreadlen = 8000; // default number of chars to return for a Blob/Long field
  59    var $_bindInputArray = false;
  60    var $curmode = SQL_CUR_USE_DRIVER; // See sqlext.h, SQL_CUR_DEFAULT == SQL_CUR_USE_DRIVER == 2L
  61    var $_genSeqSQL = "create table %s (id integer)";
  62    var $_autocommit = true;
  63    var $_haserrorfunctions = true;
  64    var $_has_stupid_odbc_fetch_api_change = true;
  65    var $_lastAffectedRows = 0;
  66    var $uCaseTables = true; // for meta* functions, uppercase table names
  67  
  68  
  69    function __construct()
  70    {
  71      $this->_haserrorfunctions = ADODB_PHPVER >= 0x4050;
  72      $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200;
  73    }
  74  
  75    // returns true or false
  76    function _connect($argDSN, $argUsername, $argPassword, $argDatabasename)
  77    {
  78      if (!function_exists('ads_connect')) return null;
  79  
  80      if ($this->debug && $argDatabasename && $this->databaseType != 'vfp') {
  81        ADOConnection::outp("For Advantage Connect(), $argDatabasename is not used. Place dsn in 1st parameter.");
  82      }
  83      $last_php_error = $this->resetLastError();
  84      if ($this->curmode === false) $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword);
  85      else $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword,$this->curmode);
  86      $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
  87      if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
  88  
  89      return $this->_connectionID != false;
  90    }
  91  
  92    // returns true or false
  93    function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
  94    {
  95      if (!function_exists('ads_connect')) return null;
  96  
  97      $last_php_error = $this->resetLastError();
  98      $this->_errorMsg = '';
  99      if ($this->debug && $argDatabasename) {
 100              ADOConnection::outp("For PConnect(), $argDatabasename is not used. Place dsn in 1st parameter.");
 101      }
 102    //  print "dsn=$argDSN u=$argUsername p=$argPassword<br>"; flush();
 103      if ($this->curmode === false) $this->_connectionID = ads_connect($argDSN,$argUsername,$argPassword);
 104      else $this->_connectionID = ads_pconnect($argDSN,$argUsername,$argPassword,$this->curmode);
 105  
 106      $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 107      if ($this->_connectionID && $this->autoRollback) @ads_rollback($this->_connectionID);
 108      if (isset($this->connectStmt)) $this->Execute($this->connectStmt);
 109  
 110      return $this->_connectionID != false;
 111    }
 112  
 113    // returns the Server version and Description
 114    function ServerInfo()
 115    {
 116  
 117      if (!empty($this->host) && ADODB_PHPVER >= 0x4300) {
 118        $stmt = $this->Prepare('EXECUTE PROCEDURE sp_mgGetInstallInfo()');
 119                          $res =  $this->Execute($stmt);
 120                          if(!$res)
 121                                  print $this->ErrorMsg();
 122                          else{
 123                                  $ret["version"]= $res->fields[3];
 124                                  $ret["description"]="Advantage Database Server";
 125                                  return $ret;
 126                          }
 127                  }
 128                  else {
 129              return ADOConnection::ServerInfo();
 130      }
 131    }
 132  
 133  
 134          // returns true or false
 135          function CreateSequence($seqname = 'adodbseq', $start = 1)
 136    {
 137                  $res =  $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE");
 138                  if(!$res){
 139                          print $this->ErrorMsg();
 140                          return false;
 141                  }
 142                  else
 143                          return true;
 144  
 145          }
 146  
 147          // returns true or false
 148          function DropSequence($seqname = 'adodbseq')
 149    {
 150                  $res = $this->Execute("DROP TABLE $seqname");
 151                  if(!$res){
 152                          print $this->ErrorMsg();
 153                          return false;
 154                  }
 155                  else
 156                          return true;
 157          }
 158  
 159  
 160    // returns the generated ID or false
 161          // checks if the table already exists, else creates the table and inserts a record into the table
 162          // and gets the ID number of the last inserted record.
 163          function GenID($seqname = 'adodbseq', $start = 1)
 164          {
 165                  $go = $this->Execute("select * from $seqname");
 166                  if (!$go){
 167                          $res = $this->Execute("CREATE TABLE $seqname ( ID autoinc( 1 ) ) IN DATABASE");
 168                          if(!res){
 169                                  print $this->ErrorMsg();
 170                                  return false;
 171                          }
 172                  }
 173                  $res = $this->Execute("INSERT INTO $seqname VALUES( DEFAULT )");
 174                  if(!$res){
 175                          print $this->ErrorMsg();
 176                          return false;
 177                  }
 178                  else{
 179                          $gen = $this->Execute("SELECT LastAutoInc( STATEMENT ) FROM system.iota");
 180                          $ret = $gen->fields[0];
 181                          return $ret;
 182                  }
 183  
 184          }
 185  
 186  
 187  
 188  
 189    function ErrorMsg()
 190    {
 191      if ($this->_haserrorfunctions) {
 192        if ($this->_errorMsg !== false) return $this->_errorMsg;
 193        if (empty($this->_connectionID)) return @ads_errormsg();
 194        return @ads_errormsg($this->_connectionID);
 195      } else return ADOConnection::ErrorMsg();
 196    }
 197  
 198  
 199    function ErrorNo()
 200    {
 201  
 202                  if ($this->_haserrorfunctions) {
 203        if ($this->_errorCode !== false) {
 204          // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
 205          return (strlen($this->_errorCode)<=2) ? 0 : $this->_errorCode;
 206        }
 207  
 208        if (empty($this->_connectionID)) $e = @ads_error();
 209        else $e = @ads_error($this->_connectionID);
 210  
 211         // bug in 4.0.6, error number can be corrupted string (should be 6 digits)
 212         // so we check and patch
 213        if (strlen($e)<=2) return 0;
 214        return $e;
 215      } else return ADOConnection::ErrorNo();
 216    }
 217  
 218  
 219  
 220    function BeginTrans()
 221    {
 222      if (!$this->hasTransactions) return false;
 223      if ($this->transOff) return true;
 224      $this->transCnt += 1;
 225      $this->_autocommit = false;
 226      return ads_autocommit($this->_connectionID,false);
 227    }
 228  
 229    function CommitTrans($ok=true)
 230    {
 231      if ($this->transOff) return true;
 232      if (!$ok) return $this->RollbackTrans();
 233      if ($this->transCnt) $this->transCnt -= 1;
 234      $this->_autocommit = true;
 235      $ret = ads_commit($this->_connectionID);
 236      ads_autocommit($this->_connectionID,true);
 237      return $ret;
 238    }
 239  
 240    function RollbackTrans()
 241    {
 242      if ($this->transOff) return true;
 243      if ($this->transCnt) $this->transCnt -= 1;
 244      $this->_autocommit = true;
 245      $ret = ads_rollback($this->_connectionID);
 246      ads_autocommit($this->_connectionID,true);
 247      return $ret;
 248    }
 249  
 250  
 251    // Returns tables,Views or both on succesfull execution. Returns
 252          // tables by default on succesfull execustion.
 253    function &MetaTables($ttype = false, $showSchema = false, $mask = false)
 254    {
 255            $recordSet1 = $this->Execute("select * from system.tables");
 256                  if(!$recordSet1){
 257                          print $this->ErrorMsg();
 258                          return false;
 259                  }
 260                  $recordSet2 = $this->Execute("select * from system.views");
 261                  if(!$recordSet2){
 262                          print $this->ErrorMsg();
 263                          return false;
 264                  }
 265                  $i=0;
 266                  while (!$recordSet1->EOF){
 267                                   $arr["$i"] = $recordSet1->fields[0];
 268                                   $recordSet1->MoveNext();
 269                                   $i=$i+1;
 270                  }
 271                  if($ttype=='FALSE'){
 272                          while (!$recordSet2->EOF){
 273                                  $arr["$i"] = $recordSet2->fields[0];
 274                                  $recordSet2->MoveNext();
 275                                  $i=$i+1;
 276                          }
 277                          return $arr;
 278                  }
 279                  elseif($ttype=='VIEWS'){
 280                          while (!$recordSet2->EOF){
 281                                  $arrV["$i"] = $recordSet2->fields[0];
 282                                  $recordSet2->MoveNext();
 283                                  $i=$i+1;
 284                          }
 285                          return $arrV;
 286                  }
 287                  else{
 288                          return $arr;
 289                  }
 290  
 291    }
 292  
 293          function &MetaPrimaryKeys($table, $owner = false)
 294    {
 295            $recordSet = $this->Execute("select table_primary_key from system.tables where name='$table'");
 296                  if(!$recordSet){
 297                          print $this->ErrorMsg();
 298                          return false;
 299                  }
 300                  $i=0;
 301                  while (!$recordSet->EOF){
 302                                   $arr["$i"] = $recordSet->fields[0];
 303                                   $recordSet->MoveNext();
 304                                   $i=$i+1;
 305                  }
 306                  return $arr;
 307          }
 308  
 309  /*
 310  See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcdatetime_data_type_changes.asp
 311  / SQL data type codes /
 312  #define SQL_UNKNOWN_TYPE  0
 313  #define SQL_CHAR      1
 314  #define SQL_NUMERIC    2
 315  #define SQL_DECIMAL    3
 316  #define SQL_INTEGER    4
 317  #define SQL_SMALLINT    5
 318  #define SQL_FLOAT      6
 319  #define SQL_REAL      7
 320  #define SQL_DOUBLE      8
 321  #if (ODBCVER >= 0x0300)
 322  #define SQL_DATETIME    9
 323  #endif
 324  #define SQL_VARCHAR   12
 325  
 326  
 327  / One-parameter shortcuts for date/time data types /
 328  #if (ODBCVER >= 0x0300)
 329  #define SQL_TYPE_DATE   91
 330  #define SQL_TYPE_TIME   92
 331  #define SQL_TYPE_TIMESTAMP 93
 332  
 333  #define SQL_UNICODE                             (-95)
 334  #define SQL_UNICODE_VARCHAR                     (-96)
 335  #define SQL_UNICODE_LONGVARCHAR                 (-97)
 336  */
 337    function ODBCTypes($t)
 338    {
 339      switch ((integer)$t) {
 340      case 1:
 341      case 12:
 342      case 0:
 343      case -95:
 344      case -96:
 345        return 'C';
 346      case -97:
 347      case -1: //text
 348        return 'X';
 349      case -4: //image
 350        return 'B';
 351  
 352      case 9:
 353      case 91:
 354        return 'D';
 355  
 356      case 10:
 357      case 11:
 358      case 92:
 359      case 93:
 360        return 'T';
 361  
 362      case 4:
 363      case 5:
 364      case -6:
 365        return 'I';
 366  
 367      case -11: // uniqidentifier
 368        return 'R';
 369      case -7: //bit
 370        return 'L';
 371  
 372      default:
 373        return 'N';
 374      }
 375    }
 376  
 377    function &MetaColumns($table, $normalize = true)
 378    {
 379    global $ADODB_FETCH_MODE;
 380  
 381      $false = false;
 382      if ($this->uCaseTables) $table = strtoupper($table);
 383      $schema = '';
 384      $this->_findschema($table,$schema);
 385  
 386      $savem = $ADODB_FETCH_MODE;
 387      $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 388  
 389      /*if (false) { // after testing, confirmed that the following does not work becoz of a bug
 390        $qid2 = ads_tables($this->_connectionID);
 391        $rs = new ADORecordSet_ads($qid2);
 392        $ADODB_FETCH_MODE = $savem;
 393        if (!$rs) return false;
 394        $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
 395        $rs->_fetch();
 396  
 397        while (!$rs->EOF) {
 398          if ($table == strtoupper($rs->fields[2])) {
 399            $q = $rs->fields[0];
 400            $o = $rs->fields[1];
 401            break;
 402          }
 403          $rs->MoveNext();
 404        }
 405        $rs->Close();
 406  
 407        $qid = ads_columns($this->_connectionID,$q,$o,strtoupper($table),'%');
 408      } */
 409  
 410      switch ($this->databaseType) {
 411      case 'access':
 412      case 'vfp':
 413        $qid = ads_columns($this->_connectionID);#,'%','',strtoupper($table),'%');
 414        break;
 415  
 416  
 417      case 'db2':
 418              $colname = "%";
 419              $qid = ads_columns($this->_connectionID, "", $schema, $table, $colname);
 420              break;
 421  
 422      default:
 423        $qid = @ads_columns($this->_connectionID,'%','%',strtoupper($table),'%');
 424        if (empty($qid)) $qid = ads_columns($this->_connectionID);
 425        break;
 426      }
 427      if (empty($qid)) return $false;
 428  
 429      $rs = new ADORecordSet_ads($qid);
 430      $ADODB_FETCH_MODE = $savem;
 431  
 432      if (!$rs) return $false;
 433      $rs->_has_stupid_odbc_fetch_api_change = $this->_has_stupid_odbc_fetch_api_change;
 434      $rs->_fetch();
 435  
 436      $retarr = array();
 437  
 438      /*
 439      $rs->fields indices
 440      0 TABLE_QUALIFIER
 441      1 TABLE_SCHEM
 442      2 TABLE_NAME
 443      3 COLUMN_NAME
 444      4 DATA_TYPE
 445      5 TYPE_NAME
 446      6 PRECISION
 447      7 LENGTH
 448      8 SCALE
 449      9 RADIX
 450      10 NULLABLE
 451      11 REMARKS
 452      */
 453      while (!$rs->EOF) {
 454      //  adodb_pr($rs->fields);
 455        if (strtoupper(trim($rs->fields[2])) == $table && (!$schema || strtoupper($rs->fields[1]) == $schema)) {
 456          $fld = new ADOFieldObject();
 457          $fld->name = $rs->fields[3];
 458          $fld->type = $this->ODBCTypes($rs->fields[4]);
 459  
 460          // ref: http://msdn.microsoft.com/library/default.asp?url=/archive/en-us/dnaraccgen/html/msdn_odk.asp
 461          // access uses precision to store length for char/varchar
 462          if ($fld->type == 'C' or $fld->type == 'X') {
 463            if ($this->databaseType == 'access')
 464              $fld->max_length = $rs->fields[6];
 465            else if ($rs->fields[4] <= -95) // UNICODE
 466              $fld->max_length = $rs->fields[7]/2;
 467            else
 468              $fld->max_length = $rs->fields[7];
 469          } else
 470            $fld->max_length = $rs->fields[7];
 471          $fld->not_null = !empty($rs->fields[10]);
 472          $fld->scale = $rs->fields[8];
 473          $retarr[strtoupper($fld->name)] = $fld;
 474        } else if (sizeof($retarr)>0)
 475          break;
 476        $rs->MoveNext();
 477      }
 478      $rs->Close(); //-- crashes 4.03pl1 -- why?
 479  
 480      if (empty($retarr)) $retarr = false;
 481      return $retarr;
 482    }
 483  
 484          // Returns an array of columns names for a given table
 485          function &MetaColumnNames($table, $numIndexes = false, $useattnum = false)
 486          {
 487                  $recordSet = $this->Execute("select name from system.columns where parent='$table'");
 488                  if(!$recordSet){
 489                          print $this->ErrorMsg();
 490                          return false;
 491                  }
 492                  else{
 493                          $i=0;
 494                          while (!$recordSet->EOF){
 495                                  $arr["FIELD$i"] = $recordSet->fields[0];
 496                                  $recordSet->MoveNext();
 497                                  $i=$i+1;
 498                          }
 499                          return $arr;
 500                  }
 501          }
 502  
 503  
 504    function Prepare($sql)
 505    {
 506      if (! $this->_bindInputArray) return $sql; // no binding
 507      $stmt = ads_prepare($this->_connectionID,$sql);
 508      if (!$stmt) {
 509        // we don't know whether odbc driver is parsing prepared stmts, so just return sql
 510        return $sql;
 511      }
 512      return array($sql,$stmt,false);
 513    }
 514  
 515    /* returns queryID or false */
 516    function _query($sql,$inputarr=false)
 517    {
 518      $last_php_error = $this->resetLastError();
 519      $this->_errorMsg = '';
 520  
 521                  if ($inputarr) {
 522        if (is_array($sql)) {
 523          $stmtid = $sql[1];
 524        } else {
 525          $stmtid = ads_prepare($this->_connectionID,$sql);
 526  
 527          if ($stmtid == false) {
 528            $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 529            return false;
 530          }
 531        }
 532  
 533        if (! ads_execute($stmtid,$inputarr)) {
 534          //@ads_free_result($stmtid);
 535          if ($this->_haserrorfunctions) {
 536            $this->_errorMsg = ads_errormsg();
 537            $this->_errorCode = ads_error();
 538          }
 539          return false;
 540        }
 541  
 542      } else if (is_array($sql)) {
 543        $stmtid = $sql[1];
 544        if (!ads_execute($stmtid)) {
 545          //@ads_free_result($stmtid);
 546          if ($this->_haserrorfunctions) {
 547            $this->_errorMsg = ads_errormsg();
 548            $this->_errorCode = ads_error();
 549          }
 550          return false;
 551        }
 552      } else
 553                          {
 554  
 555        $stmtid = ads_exec($this->_connectionID,$sql);
 556  
 557                          }
 558  
 559                  $this->_lastAffectedRows = 0;
 560  
 561      if ($stmtid) {
 562  
 563        if (@ads_num_fields($stmtid) == 0) {
 564          $this->_lastAffectedRows = ads_num_rows($stmtid);
 565          $stmtid = true;
 566  
 567        } else {
 568  
 569          $this->_lastAffectedRows = 0;
 570          ads_binmode($stmtid,$this->binmode);
 571          ads_longreadlen($stmtid,$this->maxblobsize);
 572  
 573        }
 574  
 575        if ($this->_haserrorfunctions) {
 576          $this->_errorMsg = '';
 577          $this->_errorCode = 0;
 578        } else {
 579            $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 580        }
 581      } else {
 582        if ($this->_haserrorfunctions) {
 583          $this->_errorMsg = ads_errormsg();
 584          $this->_errorCode = ads_error();
 585        } else {
 586            $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 587        }
 588      }
 589  
 590      return $stmtid;
 591  
 592    }
 593  
 594    /*
 595      Insert a null into the blob field of the table first.
 596      Then use UpdateBlob to store the blob.
 597  
 598      Usage:
 599  
 600      $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
 601      $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
 602     */
 603    function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
 604    {
 605                  $last_php_error = $this->resetLastError();
 606                  $sql = "UPDATE $table SET $column=? WHERE $where";
 607                  $stmtid = ads_prepare($this->_connectionID,$sql);
 608                  if ($stmtid == false){
 609                    $this->_errorMsg = $this->getChangedErrorMsg($last_php_error);
 610                    return false;
 611            }
 612                  if (! ads_execute($stmtid,array($val),array(SQL_BINARY) )){
 613                          if ($this->_haserrorfunctions){
 614                                  $this->_errorMsg = ads_errormsg();
 615                      $this->_errorCode = ads_error();
 616              }
 617                          return false;
 618             }
 619                   return TRUE;
 620          }
 621  
 622    // returns true or false
 623    function _close()
 624    {
 625      $ret = @ads_close($this->_connectionID);
 626      $this->_connectionID = false;
 627      return $ret;
 628    }
 629  
 630    function _affectedrows()
 631    {
 632      return $this->_lastAffectedRows;
 633    }
 634  
 635  }
 636  
 637  /*--------------------------------------------------------------------------------------
 638     Class Name: Recordset
 639  --------------------------------------------------------------------------------------*/
 640  
 641  class ADORecordSet_ads extends ADORecordSet {
 642  
 643    var $bind = false;
 644    var $databaseType = "ads";
 645    var $dataProvider = "ads";
 646    var $useFetchArray;
 647    var $_has_stupid_odbc_fetch_api_change;
 648  
 649    function __construct($id,$mode=false)
 650    {
 651      if ($mode === false) {
 652        global $ADODB_FETCH_MODE;
 653        $mode = $ADODB_FETCH_MODE;
 654      }
 655      $this->fetchMode = $mode;
 656  
 657      $this->_queryID = $id;
 658  
 659      // the following is required for mysql odbc driver in 4.3.1 -- why?
 660      $this->EOF = false;
 661      $this->_currentRow = -1;
 662      //parent::__construct($id);
 663    }
 664  
 665  
 666    // returns the field object
 667    function &FetchField($fieldOffset = -1)
 668    {
 669  
 670      $off=$fieldOffset+1; // offsets begin at 1
 671  
 672      $o= new ADOFieldObject();
 673      $o->name = @ads_field_name($this->_queryID,$off);
 674      $o->type = @ads_field_type($this->_queryID,$off);
 675      $o->max_length = @ads_field_len($this->_queryID,$off);
 676      if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
 677      else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
 678      return $o;
 679    }
 680  
 681    /* Use associative array to get fields array */
 682    function Fields($colname)
 683    {
 684      if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
 685      if (!$this->bind) {
 686        $this->bind = array();
 687        for ($i=0; $i < $this->_numOfFields; $i++) {
 688          $o = $this->FetchField($i);
 689          $this->bind[strtoupper($o->name)] = $i;
 690        }
 691      }
 692  
 693       return $this->fields[$this->bind[strtoupper($colname)]];
 694    }
 695  
 696  
 697    function _initrs()
 698    {
 699    global $ADODB_COUNTRECS;
 700      $this->_numOfRows = ($ADODB_COUNTRECS) ? @ads_num_rows($this->_queryID) : -1;
 701      $this->_numOfFields = @ads_num_fields($this->_queryID);
 702      // some silly drivers such as db2 as/400 and intersystems cache return _numOfRows = 0
 703      if ($this->_numOfRows == 0) $this->_numOfRows = -1;
 704      //$this->useFetchArray = $this->connection->useFetchArray;
 705      $this->_has_stupid_odbc_fetch_api_change = ADODB_PHPVER >= 0x4200;
 706    }
 707  
 708    function _seek($row)
 709    {
 710      return false;
 711    }
 712  
 713    // speed up SelectLimit() by switching to ADODB_FETCH_NUM as ADODB_FETCH_ASSOC is emulated
 714    function &GetArrayLimit($nrows,$offset=-1)
 715    {
 716      if ($offset <= 0) {
 717        $rs =& $this->GetArray($nrows);
 718        return $rs;
 719      }
 720      $savem = $this->fetchMode;
 721      $this->fetchMode = ADODB_FETCH_NUM;
 722      $this->Move($offset);
 723      $this->fetchMode = $savem;
 724  
 725      if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 726        $this->fields =& $this->GetRowAssoc();
 727      }
 728  
 729      $results = array();
 730      $cnt = 0;
 731      while (!$this->EOF && $nrows != $cnt) {
 732        $results[$cnt++] = $this->fields;
 733        $this->MoveNext();
 734      }
 735  
 736      return $results;
 737    }
 738  
 739  
 740    function MoveNext()
 741    {
 742      if ($this->_numOfRows != 0 && !$this->EOF) {
 743        $this->_currentRow++;
 744        if( $this->_fetch() ) {
 745            return true;
 746        }
 747      }
 748      $this->fields = false;
 749      $this->EOF = true;
 750      return false;
 751    }
 752  
 753    function _fetch()
 754    {
 755      $this->fields = false;
 756      if ($this->_has_stupid_odbc_fetch_api_change)
 757        $rez = @ads_fetch_into($this->_queryID,$this->fields);
 758      else {
 759        $row = 0;
 760        $rez = @ads_fetch_into($this->_queryID,$row,$this->fields);
 761      }
 762      if ($rez) {
 763        if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 764          $this->fields =& $this->GetRowAssoc();
 765        }
 766        return true;
 767      }
 768      return false;
 769    }
 770  
 771    function _close()
 772    {
 773      return @ads_free_result($this->_queryID);
 774    }
 775  
 776  }