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