Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

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