Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 /** 3 * MSSQL driver via ODBC 4 * 5 * This file is part of ADOdb, a Database Abstraction Layer library for PHP. 6 * 7 * @package ADOdb 8 * @link https://adodb.org Project's web site and documentation 9 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker 10 * 11 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause 12 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, 13 * any later version. This means you can use it in proprietary products. 14 * See the LICENSE.md file distributed with this source code for details. 15 * @license BSD-3-Clause 16 * @license LGPL-2.1-or-later 17 * 18 * @copyright 2000-2013 John Lim 19 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community 20 */ 21 22 // security - hide paths 23 if (!defined('ADODB_DIR')) die(); 24 25 if (!defined('_ADODB_ODBC_LAYER')) { 26 include_once(ADODB_DIR."/drivers/adodb-odbc.inc.php"); 27 } 28 29 30 class ADODB_odbc_mssql extends ADODB_odbc { 31 var $databaseType = 'odbc_mssql'; 32 var $fmtDate = "'Y-m-d'"; 33 var $fmtTimeStamp = "'Y-m-d\TH:i:s'"; 34 var $_bindInputArray = true; 35 var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'"; 36 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'))"; 37 var $metaColumnsSQL = # xtype==61 is datetime 38 "select c.name,t.name,c.length,c.isnullable, c.status, 39 (case when c.xusertype=61 then 0 else c.xprec end), 40 (case when c.xusertype=61 then 0 else c.xscale end) 41 from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'"; 42 var $hasTop = 'top'; // support mssql/interbase SELECT TOP 10 * FROM TABLE 43 var $sysDate = 'GetDate()'; 44 var $sysTimeStamp = 'GetDate()'; 45 var $leftOuter = '*='; 46 var $rightOuter = '=*'; 47 var $substr = 'substring'; 48 var $length = 'len'; 49 var $ansiOuter = true; // for mssql7 or later 50 var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000 51 var $hasInsertID = true; 52 /** 53 * When SET CONCAT_NULL_YIELDS_NULL is ON, concatenating a null value with 54 * a string yields a NULL result. 55 * @var string SQL statement executed after successful connection. 56 */ 57 public $connectStmt = 'SET CONCAT_NULL_YIELDS_NULL OFF'; # 58 59 // crashes php... 60 function ServerInfo() 61 { 62 global $ADODB_FETCH_MODE; 63 $save = $ADODB_FETCH_MODE; 64 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 65 $row = $this->GetRow("execute sp_server_info 2"); 66 $ADODB_FETCH_MODE = $save; 67 if (!is_array($row)) return false; 68 $arr['description'] = $row[2]; 69 $arr['version'] = ADOConnection::_findvers($arr['description']); 70 return $arr; 71 } 72 73 function IfNull( $field, $ifNull ) 74 { 75 return " ISNULL($field, $ifNull) "; // if MS SQL Server 76 } 77 78 protected function _insertID($table = '', $column = '') 79 { 80 // SCOPE_IDENTITY() 81 // Returns the last IDENTITY value inserted into an IDENTITY column in 82 // the same scope. A scope is a module -- a stored procedure, trigger, 83 // function, or batch. Thus, two statements are in the same scope if 84 // they are in the same stored procedure, function, or batch. 85 return $this->GetOne($this->identitySQL); 86 } 87 88 public function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) 89 { 90 global $ADODB_FETCH_MODE; 91 92 $save = $ADODB_FETCH_MODE; 93 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 94 $table = $this->qstr(strtoupper($table)); 95 96 $sql = 97 "select object_name(constid) as constraint_name, 98 col_name(fkeyid, fkey) as column_name, 99 object_name(rkeyid) as referenced_table_name, 100 col_name(rkeyid, rkey) as referenced_column_name 101 from sysforeignkeys 102 where upper(object_name(fkeyid)) = $table 103 order by constraint_name, referenced_table_name, keyno"; 104 105 $constraints = $this->GetArray($sql); 106 107 $ADODB_FETCH_MODE = $save; 108 109 $arr = false; 110 foreach($constraints as $constr) { 111 //print_r($constr); 112 $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3]; 113 } 114 if (!$arr) return false; 115 116 $arr2 = false; 117 118 foreach($arr as $k => $v) { 119 foreach($v as $a => $b) { 120 if ($upper) $a = strtoupper($a); 121 $arr2[$a] = $b; 122 } 123 } 124 return $arr2; 125 } 126 127 function MetaTables($ttype=false,$showSchema=false,$mask=false) 128 { 129 if ($mask) {//$this->debug=1; 130 $save = $this->metaTablesSQL; 131 $mask = $this->qstr($mask); 132 $this->metaTablesSQL .= " AND name like $mask"; 133 } 134 $ret = ADOConnection::MetaTables($ttype,$showSchema); 135 136 if ($mask) { 137 $this->metaTablesSQL = $save; 138 } 139 return $ret; 140 } 141 142 function MetaColumns($table, $normalize=true) 143 { 144 145 $this->_findschema($table,$schema); 146 if ($schema) { 147 $dbName = $this->database; 148 $this->SelectDB($schema); 149 } 150 global $ADODB_FETCH_MODE; 151 $save = $ADODB_FETCH_MODE; 152 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 153 154 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 155 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); 156 157 if ($schema) { 158 $this->SelectDB($dbName); 159 } 160 161 if (isset($savem)) $this->SetFetchMode($savem); 162 $ADODB_FETCH_MODE = $save; 163 if (!is_object($rs)) { 164 $false = false; 165 return $false; 166 } 167 168 $retarr = array(); 169 while (!$rs->EOF){ 170 $fld = new ADOFieldObject(); 171 $fld->name = $rs->fields[0]; 172 $fld->type = $rs->fields[1]; 173 174 $fld->not_null = (!$rs->fields[3]); 175 $fld->auto_increment = ($rs->fields[4] == 128); // sys.syscolumns status field. 0x80 = 128 ref: http://msdn.microsoft.com/en-us/library/ms186816.aspx 176 177 178 if (isset($rs->fields[5]) && $rs->fields[5]) { 179 if ($rs->fields[5]>0) $fld->max_length = $rs->fields[5]; 180 $fld->scale = $rs->fields[6]; 181 if ($fld->scale>0) $fld->max_length += 1; 182 } else 183 $fld->max_length = $rs->fields[2]; 184 185 186 if ($save == ADODB_FETCH_NUM) { 187 $retarr[] = $fld; 188 } else { 189 $retarr[strtoupper($fld->name)] = $fld; 190 } 191 $rs->MoveNext(); 192 } 193 194 $rs->Close(); 195 return $retarr; 196 197 } 198 199 200 function MetaIndexes($table,$primary=false, $owner=false) 201 { 202 $table = $this->qstr($table); 203 204 $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno, 205 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, 206 CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique 207 FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id 208 INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid 209 INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid 210 WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table 211 ORDER BY O.name, I.Name, K.keyno"; 212 213 global $ADODB_FETCH_MODE; 214 $save = $ADODB_FETCH_MODE; 215 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 216 if ($this->fetchMode !== FALSE) { 217 $savem = $this->SetFetchMode(FALSE); 218 } 219 220 $rs = $this->Execute($sql); 221 if (isset($savem)) { 222 $this->SetFetchMode($savem); 223 } 224 $ADODB_FETCH_MODE = $save; 225 226 if (!is_object($rs)) { 227 return FALSE; 228 } 229 230 $indexes = array(); 231 while ($row = $rs->FetchRow()) { 232 if (!$primary && $row[5]) continue; 233 234 $indexes[$row[0]]['unique'] = $row[6]; 235 $indexes[$row[0]]['columns'][] = $row[1]; 236 } 237 return $indexes; 238 } 239 240 function _query($sql,$inputarr=false) 241 { 242 if (is_string($sql)) $sql = str_replace('||','+',$sql); 243 return ADODB_odbc::_query($sql,$inputarr); 244 } 245 246 function SetTransactionMode( $transaction_mode ) 247 { 248 $this->_transmode = $transaction_mode; 249 if (empty($transaction_mode)) { 250 $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); 251 return; 252 } 253 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; 254 $this->Execute("SET TRANSACTION ".$transaction_mode); 255 } 256 257 // "Stein-Aksel Basma" <basma@accelero.no> 258 // tested with MSSQL 2000 259 function MetaPrimaryKeys($table, $owner = false) 260 { 261 global $ADODB_FETCH_MODE; 262 263 $schema = ''; 264 $this->_findschema($table,$schema); 265 //if (!$schema) $schema = $this->database; 266 if ($schema) $schema = "and k.table_catalog like '$schema%'"; 267 268 $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k, 269 information_schema.table_constraints tc 270 where tc.constraint_name = k.constraint_name and tc.constraint_type = 271 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position "; 272 273 $savem = $ADODB_FETCH_MODE; 274 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 275 $a = $this->GetCol($sql); 276 $ADODB_FETCH_MODE = $savem; 277 278 if ($a && sizeof($a)>0) return $a; 279 $false = false; 280 return $false; 281 } 282 283 function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) 284 { 285 $nrows = (int) $nrows; 286 $offset = (int) $offset; 287 if ($nrows > 0 && $offset <= 0) { 288 $sql = preg_replace( 289 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql); 290 $rs = $this->Execute($sql,$inputarr); 291 } else 292 $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache); 293 294 return $rs; 295 } 296 297 // Format date column in sql string given an input format that understands Y M D 298 function SQLDate($fmt, $col=false) 299 { 300 if (!$col) $col = $this->sysTimeStamp; 301 $s = ''; 302 303 $len = strlen($fmt); 304 for ($i=0; $i < $len; $i++) { 305 if ($s) $s .= '+'; 306 $ch = $fmt[$i]; 307 switch($ch) { 308 case 'Y': 309 case 'y': 310 $s .= "datename(yyyy,$col)"; 311 break; 312 case 'M': 313 $s .= "convert(char(3),$col,0)"; 314 break; 315 case 'm': 316 $s .= "replace(str(month($col),2),' ','0')"; 317 break; 318 case 'Q': 319 case 'q': 320 $s .= "datename(quarter,$col)"; 321 break; 322 case 'D': 323 case 'd': 324 $s .= "replace(str(day($col),2),' ','0')"; 325 break; 326 case 'h': 327 $s .= "substring(convert(char(14),$col,0),13,2)"; 328 break; 329 330 case 'H': 331 $s .= "replace(str(datepart(hh,$col),2),' ','0')"; 332 break; 333 334 case 'i': 335 $s .= "replace(str(datepart(mi,$col),2),' ','0')"; 336 break; 337 case 's': 338 $s .= "replace(str(datepart(ss,$col),2),' ','0')"; 339 break; 340 case 'a': 341 case 'A': 342 $s .= "substring(convert(char(19),$col,0),18,2)"; 343 break; 344 345 default: 346 if ($ch == '\\') { 347 $i++; 348 $ch = substr($fmt,$i,1); 349 } 350 $s .= $this->qstr($ch); 351 break; 352 } 353 } 354 return $s; 355 } 356 357 /** 358 * Returns a substring of a varchar type field 359 * 360 * The SQL server version varies because the length is mandatory, so 361 * we append a reasonable string length 362 * 363 * @param string $fld The field to sub-string 364 * @param int $start The start point 365 * @param int $length An optional length 366 * 367 * @return The SQL text 368 */ 369 function substr($fld,$start,$length=0) 370 { 371 if ($length == 0) 372 /* 373 * The length available to varchar is 2GB, but that makes no 374 * sense in a substring, so I'm going to arbitrarily limit 375 * the length to 1K, but you could change it if you want 376 */ 377 $length = 1024; 378 379 $text = "SUBSTRING($fld,$start,$length)"; 380 return $text; 381 } 382 383 /** 384 * Returns the maximum size of a MetaType C field. Because of the 385 * database design, SQL Server places no limits on the size of data inserted 386 * Although the actual limit is 2^31-1 bytes. 387 * 388 * @return int 389 */ 390 function charMax() 391 { 392 return ADODB_STRINGMAX_NOLIMIT; 393 } 394 395 /** 396 * Returns the maximum size of a MetaType X field. Because of the 397 * database design, SQL Server places no limits on the size of data inserted 398 * Although the actual limit is 2^31-1 bytes. 399 * 400 * @return int 401 */ 402 function textMax() 403 { 404 return ADODB_STRINGMAX_NOLIMIT; 405 } 406 407 // returns concatenated string 408 // MSSQL requires integers to be cast as strings 409 // automatically cast every datatype to VARCHAR(255) 410 // @author David Rogers (introspectshun) 411 function Concat() 412 { 413 $s = ""; 414 $arr = func_get_args(); 415 416 // Split single record on commas, if possible 417 if (sizeof($arr) == 1) { 418 foreach ($arr as $arg) { 419 $args = explode(',', $arg); 420 } 421 $arr = $args; 422 } 423 424 array_walk( 425 $arr, 426 function(&$value, $key) { 427 $value = "CAST(" . $value . " AS VARCHAR(255))"; 428 } 429 ); 430 $s = implode('+',$arr); 431 if (sizeof($arr) > 0) return "$s"; 432 433 return ''; 434 } 435 436 } 437 438 class ADORecordSet_odbc_mssql extends ADORecordSet_odbc { 439 440 var $databaseType = 'odbc_mssql'; 441 442 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body