Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}