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