source: sandbox/2.5.1-evolucao/phpgwapi/inc/adodb/drivers/adodb-mssql.inc.php @ 8222

Revision 8222, 33.8 KB checked in by angelo, 11 years ago (diff)

Ticket #3491 - Compatibilizar Expresso com novas versoes do PHP

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