Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   1  <?php
   2  /**
   3   * Native MSSQL driver.
   4   *
   5   * Requires mssql client. Works on Windows.
   6   *
   7   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
   8   *
   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
  12   *
  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
  19   *
  20   * @copyright 2000-2013 John Lim
  21   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  22   */
  23  
  24  // security - hide paths
  25  if (!defined('ADODB_DIR')) die();
  26  
  27  //----------------------------------------------------------------
  28  // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
  29  // and this causes tons of problems because localized versions of
  30  // MSSQL will return the dates in dmy or  mdy order; and also the
  31  // month strings depends on what language has been configured. The
  32  // following two variables allow you to control the localization
  33  // settings - Ugh.
  34  //
  35  // MORE LOCALIZATION INFO
  36  // ----------------------
  37  // To configure datetime, look for and modify sqlcommn.loc,
  38  //  	 typically found in c:\mssql\install
  39  // Also read :
  40  //	  http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
  41  // Alternatively use:
  42  // 	    CONVERT(char(12),datecol,120)
  43  //----------------------------------------------------------------
  44  
  45  
  46  ini_set('mssql.datetimeconvert',0);
  47  
  48  class ADODB_mssql extends ADOConnection {
  49  	 var $databaseType = "mssql";
  50  	 var $dataProvider = "mssql";
  51  	 var $replaceQuote = "''"; // string to use to replace quotes
  52  	 var $fmtDate = "'Y-m-d'";
  53  	 var $fmtTimeStamp = "'Y-m-d\TH:i:s'";
  54  	 var $hasInsertID = true;
  55  	 var $substr = "substring";
  56  	 var $length = 'len';
  57  	 var $hasAffectedRows = true;
  58  	 var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'";
  59  	 var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))";
  60  	 var $metaColumnsSQL = # xtype==61 is datetime
  61  	 "select c.name,t.name,c.length,c.isnullable, c.status,
  62  	 	 (case when c.xusertype=61 then 0 else c.xprec end),
  63  	 	 (case when c.xusertype=61 then 0 else c.xscale end)
  64  	 from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
  65  	 var $hasTop = 'top';	 	 // support mssql SELECT TOP 10 * FROM TABLE
  66  	 var $hasGenID = true;
  67  	 var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
  68  	 var $sysTimeStamp = 'GetDate()';
  69  	 var $maxParameterLen = 4000;
  70  	 var $arrayClass = 'ADORecordSet_array_mssql';
  71  	 var $uniqueSort = true;
  72  	 var $leftOuter = '*=';
  73  	 var $rightOuter = '=*';
  74  	 var $ansiOuter = true; // for mssql7 or later
  75  	 var $poorAffectedRows = true;
  76  	 var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
  77  	 var $uniqueOrderBy = true;
  78  	 var $_bindInputArray = true;
  79  	 var $forceNewConnect = false;
  80  
  81  	function ServerInfo()
  82  	 {
  83  	 global $ADODB_FETCH_MODE;
  84  
  85  
  86  	 	 if ($this->fetchMode === false) {
  87  	 	 	 $savem = $ADODB_FETCH_MODE;
  88  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  89  	 	 } else
  90  	 	 	 $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
  91  
  92  	 	 if (0) {
  93  	 	 	 $stmt = $this->PrepareSP('sp_server_info');
  94  	 	 	 $val = 2;
  95  	 	 	 $this->Parameter($stmt,$val,'attribute_id');
  96  	 	 	 $row = $this->GetRow($stmt);
  97  	 	 }
  98  
  99  	 	 $row = $this->GetRow("execute sp_server_info 2");
 100  
 101  
 102  	 	 if ($this->fetchMode === false) {
 103  	 	 	 $ADODB_FETCH_MODE = $savem;
 104  	 	 } else
 105  	 	 	 $this->SetFetchMode($savem);
 106  
 107  	 	 $arr['description'] = $row[2];
 108  	 	 $arr['version'] = ADOConnection::_findvers($arr['description']);
 109  	 	 return $arr;
 110  	 }
 111  
 112  	function IfNull( $field, $ifNull )
 113  	 {
 114  	 	 return " ISNULL($field, $ifNull) "; // if MS SQL Server
 115  	 }
 116  
 117  	protected function _insertID($table = '', $column = '')
 118  	 {
 119  	 // SCOPE_IDENTITY()
 120  	 // Returns the last IDENTITY value inserted into an IDENTITY column in
 121  	 // the same scope. A scope is a module -- a stored procedure, trigger,
 122  	 // function, or batch. Thus, two statements are in the same scope if
 123  	 // they are in the same stored procedure, function, or batch.
 124  	 	 if ($this->lastInsID !== false) {
 125  	 	 	 return $this->lastInsID; // InsID from sp_executesql call
 126  	 	 } else {
 127  	 	 	 return $this->GetOne($this->identitySQL);
 128  	 	 }
 129  	 }
 130  
 131  
 132  
 133  	 /**
 134  	  * Correctly quotes a string so that all strings are escaped.
 135  	  * We prefix and append to the string single-quotes.
 136  	  * An example is  $db->qstr("Don't bother");
 137  	  *
 138  	  * @param string $s            The string to quote
 139  	  * @param bool   $magic_quotes This param is not used since 5.21.0.
 140  	  *                             It remains for backwards compatibility.
 141  	  *
 142  	  * @return string Quoted string to be sent back to database
 143  	  *
 144  	  * @noinspection PhpUnusedParameterInspection
 145  	  */
 146  	function qStr($s, $magic_quotes=false)
 147  	 {
 148  	 	 return  "'" . str_replace("'", $this->replaceQuote, $s) . "'";
 149  	 }
 150  
 151  	function _affectedrows()
 152  	 {
 153  	 	 return $this->GetOne('select @@rowcount');
 154  	 }
 155  
 156  	 var $_dropSeqSQL = "drop table %s";
 157  
 158  	function CreateSequence($seq='adodbseq',$start=1)
 159  	 {
 160  
 161  	 	 $this->Execute('BEGIN TRANSACTION adodbseq');
 162  	 	 $start -= 1;
 163  	 	 $this->Execute("create table $seq (id float(53))");
 164  	 	 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
 165  	 	 if (!$ok) {
 166  	 	 	 	 $this->Execute('ROLLBACK TRANSACTION adodbseq');
 167  	 	 	 	 return false;
 168  	 	 }
 169  	 	 $this->Execute('COMMIT TRANSACTION adodbseq');
 170  	 	 return true;
 171  	 }
 172  
 173  	function GenID($seq='adodbseq',$start=1)
 174  	 {
 175  	 	 //$this->debug=1;
 176  	 	 $this->Execute('BEGIN TRANSACTION adodbseq');
 177  	 	 $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
 178  	 	 if (!$ok) {
 179  	 	 	 $this->Execute("create table $seq (id float(53))");
 180  	 	 	 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
 181  	 	 	 if (!$ok) {
 182  	 	 	 	 $this->Execute('ROLLBACK TRANSACTION adodbseq');
 183  	 	 	 	 return false;
 184  	 	 	 }
 185  	 	 	 $this->Execute('COMMIT TRANSACTION adodbseq');
 186  	 	 	 return $start;
 187  	 	 }
 188  	 	 $num = $this->GetOne("select id from $seq");
 189  	 	 $this->Execute('COMMIT TRANSACTION adodbseq');
 190  	 	 return $num;
 191  
 192  	 	 // in old implementation, pre 1.90, we returned GUID...
 193  	 	 //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'");
 194  	 }
 195  
 196  
 197  	function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
 198  	 {
 199  	 	 $nrows = (int) $nrows;
 200  	 	 $offset = (int) $offset;
 201  	 	 if ($nrows > 0 && $offset <= 0) {
 202  	 	 	 $sql = preg_replace(
 203  	 	 	 	 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql);
 204  
 205  	 	 	 if ($secs2cache)
 206  	 	 	 	 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
 207  	 	 	 else
 208  	 	 	 	 $rs = $this->Execute($sql,$inputarr);
 209  	 	 } else
 210  	 	 	 $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
 211  
 212  	 	 return $rs;
 213  	 }
 214  
 215  
 216  	 // Format date column in sql string given an input format that understands Y M D
 217  	function SQLDate($fmt, $col=false)
 218  	 {
 219  	 	 if (!$col) $col = $this->sysTimeStamp;
 220  	 	 $s = '';
 221  
 222  	 	 $len = strlen($fmt);
 223  	 	 for ($i=0; $i < $len; $i++) {
 224  	 	 	 if ($s) $s .= '+';
 225  	 	 	 $ch = $fmt[$i];
 226  	 	 	 switch($ch) {
 227  	 	 	 case 'Y':
 228  	 	 	 case 'y':
 229  	 	 	 	 $s .= "datename(yyyy,$col)";
 230  	 	 	 	 break;
 231  	 	 	 case 'M':
 232  	 	 	 	 $s .= "convert(char(3),$col,0)";
 233  	 	 	 	 break;
 234  	 	 	 case 'm':
 235  	 	 	 	 $s .= "replace(str(month($col),2),' ','0')";
 236  	 	 	 	 break;
 237  	 	 	 case 'Q':
 238  	 	 	 case 'q':
 239  	 	 	 	 $s .= "datename(quarter,$col)";
 240  	 	 	 	 break;
 241  	 	 	 case 'D':
 242  	 	 	 case 'd':
 243  	 	 	 	 $s .= "replace(str(day($col),2),' ','0')";
 244  	 	 	 	 break;
 245  	 	 	 case 'h':
 246  	 	 	 	 $s .= "substring(convert(char(14),$col,0),13,2)";
 247  	 	 	 	 break;
 248  
 249  	 	 	 case 'H':
 250  	 	 	 	 $s .= "replace(str(datepart(hh,$col),2),' ','0')";
 251  	 	 	 	 break;
 252  
 253  	 	 	 case 'i':
 254  	 	 	 	 $s .= "replace(str(datepart(mi,$col),2),' ','0')";
 255  	 	 	 	 break;
 256  	 	 	 case 's':
 257  	 	 	 	 $s .= "replace(str(datepart(ss,$col),2),' ','0')";
 258  	 	 	 	 break;
 259  	 	 	 case 'a':
 260  	 	 	 case 'A':
 261  	 	 	 	 $s .= "substring(convert(char(19),$col,0),18,2)";
 262  	 	 	 	 break;
 263  	 	 	 case 'l':
 264  	 	 	 	 $s .= "datename(dw,$col)";
 265  	 	 	 	 break;
 266  	 	 	 default:
 267  	 	 	 	 if ($ch == '\\') {
 268  	 	 	 	 	 $i++;
 269  	 	 	 	 	 $ch = substr($fmt,$i,1);
 270  	 	 	 	 }
 271  	 	 	 	 $s .= $this->qstr($ch);
 272  	 	 	 	 break;
 273  	 	 	 }
 274  	 	 }
 275  	 	 return $s;
 276  	 }
 277  
 278  
 279  	function BeginTrans()
 280  	 {
 281  	 	 if ($this->transOff) return true;
 282  	 	 $this->transCnt += 1;
 283  	 	 $ok = $this->Execute('BEGIN TRAN');
 284  	 	 return $ok;
 285  	 }
 286  
 287  	function CommitTrans($ok=true)
 288  	 {
 289  	 	 if ($this->transOff) return true;
 290  	 	 if (!$ok) return $this->RollbackTrans();
 291  	 	 if ($this->transCnt) $this->transCnt -= 1;
 292  	 	 $ok = $this->Execute('COMMIT TRAN');
 293  	 	 return $ok;
 294  	 }
 295  	function RollbackTrans()
 296  	 {
 297  	 	 if ($this->transOff) return true;
 298  	 	 if ($this->transCnt) $this->transCnt -= 1;
 299  	 	 $ok = $this->Execute('ROLLBACK TRAN');
 300  	 	 return $ok;
 301  	 }
 302  
 303  	function SetTransactionMode( $transaction_mode )
 304  	 {
 305  	 	 $this->_transmode  = $transaction_mode;
 306  	 	 if (empty($transaction_mode)) {
 307  	 	 	 $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
 308  	 	 	 return;
 309  	 	 }
 310  	 	 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
 311  	 	 $this->Execute("SET TRANSACTION ".$transaction_mode);
 312  	 }
 313  
 314  	 /*
 315  	 	 Usage:
 316  
 317  	 	 $this->BeginTrans();
 318  	 	 $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
 319  
 320  	 	 # some operation on both tables table1 and table2
 321  
 322  	 	 $this->CommitTrans();
 323  
 324  	 	 See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
 325  	 */
 326  	function RowLock($tables,$where,$col='1 as adodbignore')
 327  	 {
 328  	 	 if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
 329  	 	 if (!$this->transCnt) $this->BeginTrans();
 330  	 	 return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
 331  	 }
 332  
 333  
 334  	function MetaColumns($table, $normalize=true)
 335  	 {
 336  //	 	 $arr = ADOConnection::MetaColumns($table);
 337  //	 	 return $arr;
 338  
 339  	 	 $this->_findschema($table,$schema);
 340  	 	 if ($schema) {
 341  	 	 	 $dbName = $this->database;
 342  	 	 	 $this->SelectDB($schema);
 343  	 	 }
 344  	 	 global $ADODB_FETCH_MODE;
 345  	 	 $save = $ADODB_FETCH_MODE;
 346  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 347  
 348  	 	 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
 349  	 	 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
 350  
 351  	 	 if ($schema) {
 352  	 	 	 $this->SelectDB($dbName);
 353  	 	 }
 354  
 355  	 	 if (isset($savem)) $this->SetFetchMode($savem);
 356  	 	 $ADODB_FETCH_MODE = $save;
 357  	 	 if (!is_object($rs)) {
 358  	 	 	 $false = false;
 359  	 	 	 return $false;
 360  	 	 }
 361  
 362  	 	 $retarr = array();
 363  	 	 while (!$rs->EOF){
 364  	 	 	 $fld = new ADOFieldObject();
 365  	 	 	 $fld->name = $rs->fields[0];
 366  	 	 	 $fld->type = $rs->fields[1];
 367  
 368  	 	 	 $fld->not_null = (!$rs->fields[3]);
 369  	 	 	 $fld->auto_increment = ($rs->fields[4] == 128);	 	 // sys.syscolumns status field. 0x80 = 128 ref: http://msdn.microsoft.com/en-us/library/ms186816.aspx
 370  
 371  	 	 	 if (isset($rs->fields[5]) && $rs->fields[5]) {
 372  	 	 	 	 if ($rs->fields[5]>0) $fld->max_length = $rs->fields[5];
 373  	 	 	 	 $fld->scale = $rs->fields[6];
 374  	 	 	 	 if ($fld->scale>0) $fld->max_length += 1;
 375  	 	 	 } else
 376  	 	 	 	 $fld->max_length = $rs->fields[2];
 377  
 378  	 	 	 if ($save == ADODB_FETCH_NUM) {
 379  	 	 	 	 $retarr[] = $fld;
 380  	 	 	 } else {
 381  	 	 	 	 $retarr[strtoupper($fld->name)] = $fld;
 382  	 	 	 }
 383  	 	 	 	 $rs->MoveNext();
 384  	 	 	 }
 385  
 386  	 	 	 $rs->Close();
 387  	 	 	 return $retarr;
 388  
 389  	 }
 390  
 391  
 392  	function MetaIndexes($table,$primary=false, $owner=false)
 393  	 {
 394  	 	 $table = $this->qstr($table);
 395  
 396  	 	 $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
 397  	 	 	 CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
 398  	 	 	 CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
 399  	 	 	 FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
 400  	 	 	 INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
 401  	 	 	 INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
 402  	 	 	 WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
 403  	 	 	 ORDER BY O.name, I.Name, K.keyno";
 404  
 405  	 	 global $ADODB_FETCH_MODE;
 406  	 	 $save = $ADODB_FETCH_MODE;
 407  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 408  	 	 if ($this->fetchMode !== FALSE) {
 409  	 	 	 $savem = $this->SetFetchMode(FALSE);
 410  	 	 }
 411  
 412  	 	 $rs = $this->Execute($sql);
 413  	 	 if (isset($savem)) {
 414  	 	 	 $this->SetFetchMode($savem);
 415  	 	 }
 416  	 	 $ADODB_FETCH_MODE = $save;
 417  
 418  	 	 if (!is_object($rs)) {
 419  	 	 	 return FALSE;
 420  	 	 }
 421  
 422  	 	 $indexes = array();
 423  	 	 while ($row = $rs->FetchRow()) {
 424  	 	 	 if ($primary && !$row[5]) continue;
 425  
 426  	 	 	 $indexes[$row[0]]['unique'] = $row[6];
 427  	 	 	 $indexes[$row[0]]['columns'][] = $row[1];
 428  	 	 }
 429  	 	 return $indexes;
 430  	 }
 431  
 432  	public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false)
 433  	 {
 434  	 global $ADODB_FETCH_MODE;
 435  
 436  	 	 $save = $ADODB_FETCH_MODE;
 437  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 438  	 	 $table = $this->qstr(strtoupper($table));
 439  
 440  	 	 $sql =
 441  "select object_name(constid) as constraint_name,
 442  	 col_name(fkeyid, fkey) as column_name,
 443  	 object_name(rkeyid) as referenced_table_name,
 444  	 col_name(rkeyid, rkey) as referenced_column_name
 445  from sysforeignkeys
 446  where upper(object_name(fkeyid)) = $table
 447  order by constraint_name, referenced_table_name, keyno";
 448  
 449  	 	 $constraints = $this->GetArray($sql);
 450  
 451  	 	 $ADODB_FETCH_MODE = $save;
 452  
 453  	 	 $arr = false;
 454  	 	 foreach($constraints as $constr) {
 455  	 	 	 //print_r($constr);
 456  	 	 	 $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
 457  	 	 }
 458  	 	 if (!$arr) return false;
 459  
 460  	 	 $arr2 = false;
 461  
 462  	 	 foreach($arr as $k => $v) {
 463  	 	 	 foreach($v as $a => $b) {
 464  	 	 	 	 if ($upper) $a = strtoupper($a);
 465  	 	 	 	 if (is_array($arr2[$a])) {	 // a previous foreign key was define for this reference table, we merge the new one
 466  	 	 	 	 	 $arr2[$a] = array_merge($arr2[$a], $b);
 467  	 	 	 	 } else {
 468  	 	 	 	 	 $arr2[$a] = $b;
 469  	 	 	 	 }
 470  	 	 	 }
 471  	 	 }
 472  	 	 return $arr2;
 473  	 }
 474  
 475  	 //From: Fernando Moreira <FMoreira@imediata.pt>
 476  	function MetaDatabases()
 477  	 {
 478  	 	 if(@mssql_select_db("master")) {
 479  	 	 	 $qry = $this->metaDatabasesSQL;
 480  	 	 	 if($rs = @mssql_query($qry,$this->_connectionID)) {
 481  	 	 	 	 $tmpAr = $ar = array();
 482  	 	 	 	 while($tmpAr = @mssql_fetch_row($rs)) {
 483  	 	 	 	 	 $ar[]=$tmpAr[0];
 484  	 	 	 	 }
 485  	 	 	 	 @mssql_select_db($this->database);
 486  	 	 	 	 if(sizeof($ar)) {
 487  	 	 	 	 	 return($ar);
 488  	 	 	 	 } else {
 489  	 	 	 	 	 return(false);
 490  	 	 	 	 }
 491  	 	 	 } else {
 492  	 	 	 	 @mssql_select_db($this->database);
 493  	 	 	 	 return(false);
 494  	 	 	 }
 495  	 	 }
 496  	 	 return(false);
 497  	 }
 498  
 499  	 // "Stein-Aksel Basma" <basma@accelero.no>
 500  	 // tested with MSSQL 2000
 501  	function MetaPrimaryKeys($table, $owner=false)
 502  	 {
 503  	 global $ADODB_FETCH_MODE;
 504  
 505  	 	 $schema = '';
 506  	 	 $this->_findschema($table,$schema);
 507  	 	 if (!$schema) $schema = $this->database;
 508  	 	 if ($schema) $schema = "and k.table_catalog like '$schema%'";
 509  
 510  	 	 $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
 511  	 	 information_schema.table_constraints tc
 512  	 	 where tc.constraint_name = k.constraint_name and tc.constraint_type =
 513  	 	 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
 514  
 515  	 	 $savem = $ADODB_FETCH_MODE;
 516  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 517  	 	 $a = $this->GetCol($sql);
 518  	 	 $ADODB_FETCH_MODE = $savem;
 519  
 520  	 	 if ($a && sizeof($a)>0) return $a;
 521  	 	 $false = false;
 522  	 	 return $false;
 523  	 }
 524  
 525  
 526  	function MetaTables($ttype=false,$showSchema=false,$mask=false)
 527  	 {
 528  	 	 if ($mask) {
 529  	 	 	 $save = $this->metaTablesSQL;
 530  	 	 	 $mask = $this->qstr(($mask));
 531  	 	 	 $this->metaTablesSQL .= " AND name like $mask";
 532  	 	 }
 533  	 	 $ret = ADOConnection::MetaTables($ttype,$showSchema);
 534  
 535  	 	 if ($mask) {
 536  	 	 	 $this->metaTablesSQL = $save;
 537  	 	 }
 538  	 	 return $ret;
 539  	 }
 540  
 541  	function SelectDB($dbName)
 542  	 {
 543  	 	 $this->database = $dbName;
 544  	 	 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
 545  	 	 if ($this->_connectionID) {
 546  	 	 	 return @mssql_select_db($dbName);
 547  	 	 }
 548  	 	 else return false;
 549  	 }
 550  
 551  	function ErrorMsg()
 552  	 {
 553  	 	 if (empty($this->_errorMsg)){
 554  	 	 	 $this->_errorMsg = mssql_get_last_message();
 555  	 	 }
 556  	 	 return $this->_errorMsg;
 557  	 }
 558  
 559  	function ErrorNo()
 560  	 {
 561  	 	 if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
 562  	 	 if (empty($this->_errorMsg)) {
 563  	 	 	 $this->_errorMsg = mssql_get_last_message();
 564  	 	 }
 565  	 	 $id = @mssql_query("select @@ERROR",$this->_connectionID);
 566  	 	 if (!$id) return false;
 567  	 	 $arr = mssql_fetch_array($id);
 568  	 	 @mssql_free_result($id);
 569  	 	 if (is_array($arr)) {
 570  	 	 	 return $arr[0];
 571  	 	 } else {
 572  	 	 	 return -1;
 573  	 	 }
 574  	 }
 575  
 576  	 // returns true or false, newconnect supported since php 5.1.0.
 577  	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false)
 578  	 {
 579  	 	 if (!function_exists('mssql_pconnect')) return null;
 580  	 	 if (!empty($this->port)) $argHostname .= ":".$this->port;
 581  	 	 $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect);
 582  	 	 if ($this->_connectionID === false) return false;
 583  	 	 if ($argDatabasename) return $this->SelectDB($argDatabasename);
 584  	 	 return true;
 585  	 }
 586  
 587  
 588  	 // returns true or false
 589  	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
 590  	 {
 591  	 	 if (!function_exists('mssql_pconnect')) return null;
 592  	 	 if (!empty($this->port)) $argHostname .= ":".$this->port;
 593  	 	 $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword);
 594  	 	 if ($this->_connectionID === false) return false;
 595  
 596  	 	 // persistent connections can forget to rollback on crash, so we do it here.
 597  	 	 if ($this->autoRollback) {
 598  	 	 	 $cnt = $this->GetOne('select @@TRANCOUNT');
 599  	 	 	 while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN');
 600  	 	 }
 601  	 	 if ($argDatabasename) return $this->SelectDB($argDatabasename);
 602  	 	 return true;
 603  	 }
 604  
 605  	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
 606  	 {
 607  	 	 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
 608  	 }
 609  
 610  	function Prepare($sql)
 611  	 {
 612  	 	 $sqlarr = explode('?',$sql);
 613  	 	 if (sizeof($sqlarr) <= 1) return $sql;
 614  	 	 $sql2 = $sqlarr[0];
 615  	 	 for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) {
 616  	 	 	 $sql2 .=  '@P'.($i-1) . $sqlarr[$i];
 617  	 	 }
 618  	 	 return array($sql,$this->qstr($sql2),$max,$sql2);
 619  	 }
 620  
 621  	function PrepareSP($sql,$param=true)
 622  	 {
 623  	 	 $stmt = mssql_init($sql,$this->_connectionID);
 624  	 	 if (!$stmt)  return $sql;
 625  	 	 return array($sql,$stmt);
 626  	 }
 627  
 628  	 // returns concatenated string
 629  	 // MSSQL requires integers to be cast as strings
 630  	 // automatically cast every datatype to VARCHAR(255)
 631  	 // @author David Rogers (introspectshun)
 632  	function Concat()
 633  	 {
 634  	 	 	 $s = "";
 635  	 	 	 $arr = func_get_args();
 636  
 637  	 	 	 // Split single record on commas, if possible
 638  	 	 	 if (sizeof($arr) == 1) {
 639  	 	 	 	 foreach ($arr as $arg) {
 640  	 	 	 	 	 $args = explode(',', $arg);
 641  	 	 	 	 }
 642  	 	 	 	 $arr = $args;
 643  	 	 	 }
 644  
 645  	 	 	 array_walk(
 646  	 	 	 	 $arr,
 647  	 	 	 	 function(&$value, $key) {
 648  	 	 	 	 	 $value = "CAST(" . $value . " AS VARCHAR(255))";
 649  	 	 	 	 }
 650  	 	 	 );
 651  	 	 	 $s = implode('+',$arr);
 652  	 	 	 if (sizeof($arr) > 0) return "$s";
 653  
 654  	 	 	 return '';
 655  	 }
 656  
 657  	 /*
 658  	 Usage:
 659  	 	 $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group
 660  
 661  	 	 # note that the parameter does not have @ in front!
 662  	 	 $db->Parameter($stmt,$id,'myid');
 663  	 	 $db->Parameter($stmt,$group,'group',false,64);
 664  	 	 $db->Execute($stmt);
 665  
 666  	 	 @param $stmt Statement returned by Prepare() or PrepareSP().
 667  	 	 @param $var PHP variable to bind to. Can set to null (for isNull support).
 668  	 	 @param $name Name of stored procedure variable name to bind to.
 669  	 	 @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in oci8.
 670  	 	 @param [$maxLen] Holds an maximum length of the variable.
 671  	 	 @param [$type] The data type of $var. Legal values depend on driver.
 672  
 673  	 	 See mssql_bind documentation at php.net.
 674  	 */
 675  	function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false)
 676  	 {
 677  	 	 $isNull = is_null($var); // php 4.0.4 and above...
 678  
 679  	 	 if ($type === false)
 680  	 	 	 switch(gettype($var)) {
 681  	 	 	 default:
 682  	 	 	 case 'string': $type = SQLVARCHAR; break;
 683  	 	 	 case 'double': $type = SQLFLT8; break;
 684  	 	 	 case 'integer': $type = SQLINT4; break;
 685  	 	 	 case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0
 686  	 	 }
 687  
 688  	 	 if  ($this->debug) {
 689  	 	 	 $prefix = ($isOutput) ? 'Out' : 'In';
 690  	 	 	 $ztype = (empty($type)) ? 'false' : $type;
 691  	 	 	 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
 692  	 	 }
 693  	 	 /*
 694  	 	 	 See PHPLens Issue No: 7231
 695  
 696  	 	 	 RETVAL is HARD CODED into php_mssql extension:
 697  	 	 	 The return value (a long integer value) is treated like a special OUTPUT parameter,
 698  	 	 	 called "RETVAL" (without the @). See the example at mssql_execute to
 699  	 	 	 see how it works. - type: one of this new supported PHP constants.
 700  	 	 	 	 SQLTEXT, SQLVARCHAR,SQLCHAR, SQLINT1,SQLINT2, SQLINT4, SQLBIT,SQLFLT8
 701  	 	 */
 702  	 	 if ($name !== 'RETVAL') $name = '@'.$name;
 703  	 	 return mssql_bind($stmt[1], $name, $var, $type, $isOutput, $isNull, $maxLen);
 704  	 }
 705  
 706  	 /*
 707  	 	 Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
 708  	 	 So all your blobs must be of type "image".
 709  
 710  	 	 Remember to set in php.ini the following...
 711  
 712  	 	 ; Valid range 0 - 2147483647. Default = 4096.
 713  	 	 mssql.textlimit = 0 ; zero to pass through
 714  
 715  	 	 ; Valid range 0 - 2147483647. Default = 4096.
 716  	 	 mssql.textsize = 0 ; zero to pass through
 717  	 */
 718  	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
 719  	 {
 720  
 721  	 	 if (strtoupper($blobtype) == 'CLOB') {
 722  	 	 	 $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
 723  	 	 	 return $this->Execute($sql) != false;
 724  	 	 }
 725  	 	 $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
 726  	 	 return $this->Execute($sql) != false;
 727  	 }
 728  
 729  	 // returns query ID if successful, otherwise false
 730  	function _query($sql,$inputarr=false)
 731  	 {
 732  	 	 $this->_errorMsg = false;
 733  	 	 if (is_array($inputarr)) {
 734  
 735  	 	 	 # bind input params with sp_executesql:
 736  	 	 	 # see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm
 737  	 	 	 # works only with sql server 7 and newer
 738  	 	 	 $getIdentity = false;
 739  	 	 	 if (!is_array($sql) && preg_match('/^\\s*insert/i', $sql)) {
 740  	 	 	 	 $getIdentity = true;
 741  	 	 	 	 $sql .= (preg_match('/;\\s*$/i', $sql) ? ' ' : '; ') . $this->identitySQL;
 742  	 	 	 }
 743  	 	 	 if (!is_array($sql)) $sql = $this->Prepare($sql);
 744  	 	 	 $params = '';
 745  	 	 	 $decl = '';
 746  	 	 	 $i = 0;
 747  	 	 	 foreach($inputarr as $v) {
 748  	 	 	 	 if ($decl) {
 749  	 	 	 	 	 $decl .= ', ';
 750  	 	 	 	 	 $params .= ', ';
 751  	 	 	 	 }
 752  	 	 	 	 if (is_string($v)) {
 753  	 	 	 	 	 $len = strlen($v);
 754  	 	 	 	 	 if ($len == 0) $len = 1;
 755  
 756  	 	 	 	 	 if ($len > 4000 ) {
 757  	 	 	 	 	 	 // NVARCHAR is max 4000 chars. Let's use NTEXT
 758  	 	 	 	 	 	 $decl .= "@P$i NTEXT";
 759  	 	 	 	 	 } else {
 760  	 	 	 	 	 	 $decl .= "@P$i NVARCHAR($len)";
 761  	 	 	 	 	 }
 762  
 763  	 	 	 	 	 if(substr($v,0,1) == "'" && substr($v,-1,1) == "'")
 764  	 	 	 	 	 	 /*
 765  	 	 	 	 	 	 * String is already fully quoted
 766  	 	 	 	 	 	 */
 767  	 	 	 	 	 	 $inputVar = $v;
 768  	 	 	 	 	 else
 769  	 	 	 	 	 	 $inputVar = $db->this($v);
 770  
 771  	 	 	 	 	 $params .= "@P$i=N" . $inputVar;
 772  
 773  	 	 	 	 } else if (is_integer($v)) {
 774  	 	 	 	 	 $decl .= "@P$i INT";
 775  	 	 	 	 	 $params .= "@P$i=".$v;
 776  	 	 	 	 } else if (is_float($v)) {
 777  	 	 	 	 	 $decl .= "@P$i FLOAT";
 778  	 	 	 	 	 $params .= "@P$i=".$v;
 779  	 	 	 	 } else if (is_bool($v)) {
 780  	 	 	 	 	 $decl .= "@P$i INT"; # Used INT just in case BIT in not supported on the user's MSSQL version. It will cast appropriately.
 781  	 	 	 	 	 $params .= "@P$i=".(($v)?'1':'0'); # True == 1 in MSSQL BIT fields and acceptable for storing logical true in an int field
 782  	 	 	 	 } else {
 783  	 	 	 	 	 $decl .= "@P$i CHAR"; # Used char because a type is required even when the value is to be NULL.
 784  	 	 	 	 	 $params .= "@P$i=NULL";
 785  	 	 	 	 	 }
 786  	 	 	 	 $i += 1;
 787  	 	 	 }
 788  	 	 	 $decl = $this->qstr($decl);
 789  	 	 	 if ($this->debug) ADOConnection::outp("<font size=-1>sp_executesql N{$sql[1]},N$decl,$params</font>");
 790  	 	 	 $rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params", $this->_connectionID);
 791  	 	 	 if ($getIdentity) {
 792  	 	 	 	 $arr = @mssql_fetch_row($rez);
 793  	 	 	 	 $this->lastInsID = isset($arr[0]) ? $arr[0] : false;
 794  	 	 	 	 @mssql_data_seek($rez, 0);
 795  	 	 	 }
 796  
 797  	 	 } else if (is_array($sql)) {
 798  	 	 	 # PrepareSP()
 799  	 	 	 $rez = mssql_execute($sql[1]);
 800  	 	 	 $this->lastInsID = false;
 801  
 802  	 	 } else {
 803  	 	 	 $rez = mssql_query($sql,$this->_connectionID);
 804  	 	 	 $this->lastInsID = false;
 805  	 	 }
 806  	 	 return $rez;
 807  	 }
 808  
 809  	 // returns true or false
 810  	function _close()
 811  	 {
 812  	 	 if ($this->transCnt) {
 813  	 	 	 $this->RollbackTrans();
 814  	 	 }
 815  	 	 if($this->_connectionID) {
 816  	 	 	 $rez = mssql_close($this->_connectionID);
 817  	 	 }
 818  	 	 $this->_connectionID = false;
 819  	 	 return $rez;
 820  	 }
 821  
 822  
 823  
 824  	 /**
 825  	 * Returns a substring of a varchar type field
 826  	 *
 827  	 * The SQL server version varies because the length is mandatory, so
 828  	 * we append a reasonable string length
 829  	 *
 830  	 * @param	 string	 $fld	 The field to sub-string
 831  	 * @param	 int	 	 $start	 The start point
 832  	 * @param	 int	 	 $length	 An optional length
 833  	 *
 834  	 * @return	 The SQL text
 835  	 */
 836  	function substr($fld,$start,$length=0)
 837  	 {
 838  	 	 if ($length == 0)
 839  	 	 	 /*
 840  	 	      * The length available to varchar is 2GB, but that makes no
 841  	 	 	  * sense in a substring, so I'm going to arbitrarily limit
 842  	 	 	  * the length to 1K, but you could change it if you want
 843  	 	 	  */
 844  	 	 	 $length = 1024;
 845  
 846  	 	 $text = "SUBSTRING($fld,$start,$length)";
 847  	 	 return $text;
 848  	 }
 849  }
 850  
 851  /*--------------------------------------------------------------------------------------
 852  	 Class Name: Recordset
 853  --------------------------------------------------------------------------------------*/
 854  
 855  class ADORecordset_mssql extends ADORecordSet {
 856  
 857  	 var $databaseType = "mssql";
 858  	 var $canSeek = true;
 859  	 var $hasFetchAssoc; // see PHPLens Issue No: 6083
 860  	 // _mths works only in non-localised system
 861  
 862  	function __construct($id,$mode=false)
 863  	 {
 864  	 	 // freedts check...
 865  	 	 $this->hasFetchAssoc = function_exists('mssql_fetch_assoc');
 866  
 867  	 	 if ($mode === false) {
 868  	 	 	 global $ADODB_FETCH_MODE;
 869  	 	 	 $mode = $ADODB_FETCH_MODE;
 870  
 871  	 	 }
 872  	 	 $this->fetchMode = $mode;
 873  	 	 return parent::__construct($id);
 874  	 }
 875  
 876  
 877  	function _initrs()
 878  	 {
 879  	 GLOBAL $ADODB_COUNTRECS;
 880  	 	 $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1;
 881  	 	 $this->_numOfFields = @mssql_num_fields($this->_queryID);
 882  	 }
 883  
 884  
 885  	 //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
 886  	 // get next resultset - requires PHP 4.0.5 or later
 887  	function NextRecordSet()
 888  	 {
 889  	 	 if (!mssql_next_result($this->_queryID)) return false;
 890  	 	 $this->_inited = false;
 891  	 	 $this->bind = false;
 892  	 	 $this->_currentRow = -1;
 893  	 	 $this->Init();
 894  	 	 return true;
 895  	 }
 896  
 897  	 /* Use associative array to get fields array */
 898  	function Fields($colname)
 899  	 {
 900  	 	 if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
 901  	 	 if (!$this->bind) {
 902  	 	 	 $this->bind = array();
 903  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
 904  	 	 	 	 $o = $this->FetchField($i);
 905  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
 906  	 	 	 }
 907  	 	 }
 908  
 909  	 	 return $this->fields[$this->bind[strtoupper($colname)]];
 910  	 }
 911  
 912  	 /*	 Returns: an object containing field information.
 913  	 	 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
 914  	 	 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
 915  	 	 fetchField() is retrieved.	 */
 916  
 917  	function FetchField($fieldOffset = -1)
 918  	 {
 919  	 	 if ($fieldOffset != -1) {
 920  	 	 	 $f = @mssql_fetch_field($this->_queryID, $fieldOffset);
 921  	 	 }
 922  	 	 else if ($fieldOffset == -1) {	 /*	 The $fieldOffset argument is not provided thus its -1 	 */
 923  	 	 	 $f = @mssql_fetch_field($this->_queryID);
 924  	 	 }
 925  	 	 $false = false;
 926  	 	 if (empty($f)) return $false;
 927  	 	 return $f;
 928  	 }
 929  
 930  	function _seek($row)
 931  	 {
 932  	 	 return @mssql_data_seek($this->_queryID, $row);
 933  	 }
 934  
 935  	 // speedup
 936  	function MoveNext()
 937  	 {
 938  	 	 if ($this->EOF) return false;
 939  
 940  	 	 $this->_currentRow++;
 941  
 942  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 943  	 	 	 if ($this->fetchMode & ADODB_FETCH_NUM) {
 944  	 	 	 	 //ADODB_FETCH_BOTH mode
 945  	 	 	 	 $this->fields = @mssql_fetch_array($this->_queryID);
 946  	 	 	 }
 947  	 	 	 else {
 948  	 	 	 	 if ($this->hasFetchAssoc) {// only for PHP 4.2.0 or later
 949  	 	 	 	 	 $this->fields = @mssql_fetch_assoc($this->_queryID);
 950  	 	 	 	 } else {
 951  	 	 	 	 	 $flds = @mssql_fetch_array($this->_queryID);
 952  	 	 	 	 	 if (is_array($flds)) {
 953  	 	 	 	 	 	 $fassoc = array();
 954  	 	 	 	 	 	 foreach($flds as $k => $v) {
 955  	 	 	 	 	 	 	 if (is_numeric($k)) continue;
 956  	 	 	 	 	 	 	 $fassoc[$k] = $v;
 957  	 	 	 	 	 	 }
 958  	 	 	 	 	 	 $this->fields = $fassoc;
 959  	 	 	 	 	 } else
 960  	 	 	 	 	 	 $this->fields = false;
 961  	 	 	 	 }
 962  	 	 	 }
 963  
 964  	 	 	 if (is_array($this->fields)) {
 965  	 	 	 	 if (ADODB_ASSOC_CASE == 0) {
 966  	 	 	 	 	 foreach($this->fields as $k=>$v) {
 967  	 	 	 	 	 	 $kn = strtolower($k);
 968  	 	 	 	 	 	 if ($kn <> $k) {
 969  	 	 	 	 	 	 	 unset($this->fields[$k]);
 970  	 	 	 	 	 	 	 $this->fields[$kn] = $v;
 971  	 	 	 	 	 	 }
 972  	 	 	 	 	 }
 973  	 	 	 	 } else if (ADODB_ASSOC_CASE == 1) {
 974  	 	 	 	 	 foreach($this->fields as $k=>$v) {
 975  	 	 	 	 	 	 $kn = strtoupper($k);
 976  	 	 	 	 	 	 if ($kn <> $k) {
 977  	 	 	 	 	 	 	 unset($this->fields[$k]);
 978  	 	 	 	 	 	 	 $this->fields[$kn] = $v;
 979  	 	 	 	 	 	 }
 980  	 	 	 	 	 }
 981  	 	 	 	 }
 982  	 	 	 }
 983  	 	 } else {
 984  	 	 	 $this->fields = @mssql_fetch_row($this->_queryID);
 985  	 	 }
 986  	 	 if ($this->fields) return true;
 987  	 	 $this->EOF = true;
 988  
 989  	 	 return false;
 990  	 }
 991  
 992  
 993  	 // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
 994  	 // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
 995  	function _fetch($ignore_fields=false)
 996  	 {
 997  	 	 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
 998  	 	 	 if ($this->fetchMode & ADODB_FETCH_NUM) {
 999  	 	 	 	 //ADODB_FETCH_BOTH mode
1000  	 	 	 	 $this->fields = @mssql_fetch_array($this->_queryID);
1001  	 	 	 } else {
1002  	 	 	 	 if ($this->hasFetchAssoc) // only for PHP 4.2.0 or later
1003  	 	 	 	 	 $this->fields = @mssql_fetch_assoc($this->_queryID);
1004  	 	 	 	 else {
1005  	 	 	 	 	 $this->fields = @mssql_fetch_array($this->_queryID);
1006  	 	 	 	 	 if (@is_array($this->fields)) {
1007  	 	 	 	 	 	 $fassoc = array();
1008  	 	 	 	 	 	 foreach($this->fields as $k => $v) {
1009  	 	 	 	 	 	 	 if (is_integer($k)) continue;
1010  	 	 	 	 	 	 	 $fassoc[$k] = $v;
1011  	 	 	 	 	 	 }
1012  	 	 	 	 	 	 $this->fields = $fassoc;
1013  	 	 	 	 	 }
1014  	 	 	 	 }
1015  	 	 	 }
1016  
1017  	 	 	 if (!$this->fields) {
1018  	 	 	 } else if (ADODB_ASSOC_CASE == 0) {
1019  	 	 	 	 foreach($this->fields as $k=>$v) {
1020  	 	 	 	 	 $kn = strtolower($k);
1021  	 	 	 	 	 if ($kn <> $k) {
1022  	 	 	 	 	 	 unset($this->fields[$k]);
1023  	 	 	 	 	 	 $this->fields[$kn] = $v;
1024  	 	 	 	 	 }
1025  	 	 	 	 }
1026  	 	 	 } else if (ADODB_ASSOC_CASE == 1) {
1027  	 	 	 	 foreach($this->fields as $k=>$v) {
1028  	 	 	 	 	 $kn = strtoupper($k);
1029  	 	 	 	 	 if ($kn <> $k) {
1030  	 	 	 	 	 	 unset($this->fields[$k]);
1031  	 	 	 	 	 	 $this->fields[$kn] = $v;
1032  	 	 	 	 	 }
1033  	 	 	 	 }
1034  	 	 	 }
1035  	 	 } else {
1036  	 	 	 $this->fields = @mssql_fetch_row($this->_queryID);
1037  	 	 }
1038  	 	 return $this->fields;
1039  	 }
1040  
1041  	 /*	 close() only needs to be called if you are worried about using too much memory while your script
1042  	 	 is running. All associated result memory for the specified result identifier will automatically be freed.	 */
1043  
1044  	function _close()
1045  	 {
1046  	 	 if($this->_queryID) {
1047  	 	 	 $rez = mssql_free_result($this->_queryID);
1048  	 	 	 $this->_queryID = false;
1049  	 	 	 return $rez;
1050  	 	 }
1051  	 	 return true;
1052  	 }
1053  
1054  	 /**
1055  	 * Returns the maximum size of a MetaType C field. Because of the
1056  	 * database design, SQL Server places no limits on the size of data inserted
1057  	 * Although the actual limit is 2^31-1 bytes.
1058  	 *
1059  	 * @return int
1060  	 */
1061  	function charMax()
1062  	 {
1063  	 	 return ADODB_STRINGMAX_NOLIMIT;
1064  	 }
1065  
1066  	 /**
1067  	 * Returns the maximum size of a MetaType X field. Because of the
1068  	 * database design, SQL Server places no limits on the size of data inserted
1069  	 * Although the actual limit is 2^31-1 bytes.
1070  	 *
1071  	 * @return int
1072  	 */
1073  	function textMax()
1074  	 {
1075  	 	 return ADODB_STRINGMAX_NOLIMIT;
1076  	 }
1077  
1078  }
1079  
1080  
1081  class ADORecordSet_array_mssql extends ADORecordSet_array {}
1082  
1083  /*
1084  Code Example 1:
1085  
1086  select	 object_name(constid) as constraint_name,
1087  	 	 object_name(fkeyid) as table_name,
1088  	 	 col_name(fkeyid, fkey) as column_name,
1089  	 object_name(rkeyid) as referenced_table_name,
1090  	 col_name(rkeyid, rkey) as referenced_column_name
1091  from sysforeignkeys
1092  where object_name(fkeyid) = x
1093  order by constraint_name, table_name, referenced_table_name,  keyno
1094  
1095  Code Example 2:
1096  select 	 constraint_name,
1097  	 column_name,
1098  	 ordinal_position
1099  from information_schema.key_column_usage
1100  where constraint_catalog = db_name()
1101  and table_name = x
1102  order by constraint_name, ordinal_position
1103  
1104  http://www.databasejournal.com/scripts/article.php/1440551
1105  */