source: tmp/version-2_5-test/data/module/adodb/drivers/adodb-mssqlnative.inc.php @ 18609

Revision 18609, 29.2 KB checked in by kajiwara, 14 years ago (diff)

正式版にナイトリービルド版をマージしてみるテスト

Line 
1<?php
2/*
3  v4.992 10 Nov 2009  (c) 2000-2009 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    http://www.microsoft.com/sql/technologies/php/default.mspx
13  To configure for Unix, see
14    http://phpbuilder.com/columns/alberto20000919.php3
15
16    $stream = sqlsrv_get_field($stmt, $index, SQLSRV_SQLTYPE_STREAM(SQLSRV_ENC_BINARY));
17    stream_filter_append($stream, "convert.iconv.ucs-2/utf-8"); // Voila, UTF-8 can be read directly from $stream
18
19*/
20
21// security - hide paths
22if (!defined('ADODB_DIR')) die();
23
24
25//----------------------------------------------------------------
26// MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
27// and this causes tons of problems because localized versions of
28// MSSQL will return the dates in dmy or  mdy order; and also the
29// month strings depends on what language has been configured. The
30// following two variables allow you to control the localization
31// settings - Ugh.
32//
33// MORE LOCALIZATION INFO
34// ----------------------
35// To configure datetime, look for and modify sqlcommn.loc,
36//      typically found in c:\mssql\install
37// Also read :
38//   http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
39// Alternatively use:
40//     CONVERT(char(12),datecol,120)
41//
42// Also if your month is showing as month-1,
43//   e.g. Jan 13, 2002 is showing as 13/0/2002, then see
44//     http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
45//   it's a localisation problem.
46//----------------------------------------------------------------
47
48
49// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
50if (ADODB_PHPVER >= 0x4300) {
51// docs say 4.2.0, but testing shows only since 4.3.0 does it work!
52    ini_set('mssql.datetimeconvert',0);
53} else {
54    global $ADODB_mssql_mths;       // array, months must be upper-case
55    $ADODB_mssql_date_order = 'mdy';
56    $ADODB_mssql_mths = array(
57        'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
58        'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
59}
60
61//---------------------------------------------------------------------------
62// Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
63// just after you connect to the database. Supports mdy and dmy only.
64// Not required for PHP 4.2.0 and above.
65function AutoDetect_MSSQL_Date_Order($conn)
66{
67    global $ADODB_mssql_date_order;
68    $adate = $conn->GetOne('select getdate()');
69    if ($adate) {
70        $anum = (int) $adate;
71        if ($anum > 0) {
72            if ($anum > 31) {
73                //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
74            } else
75                $ADODB_mssql_date_order = 'dmy';
76        } else
77            $ADODB_mssql_date_order = 'mdy';
78    }
79}
80
81class ADODB_mssqlnative extends ADOConnection {
82    var $databaseType = "mssqlnative"; 
83    var $dataProvider = "mssqlnative";
84    var $replaceQuote = "''"; // string to use to replace quotes
85    var $fmtDate = "'Y-m-d'";
86    var $fmtTimeStamp = "'Y-m-d H:i:s'";
87    var $hasInsertID = true;
88    var $substr = "substring";
89    var $length = 'len';
90    var $hasAffectedRows = true;
91    var $poorAffectedRows = false;
92    var $metaDatabasesSQL = "select name from sys.sysdatabases where name <> 'master'";
93    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'))";
94    var $metaColumnsSQL = # xtype==61 is datetime
95        "select c.name,t.name,c.length,
96        (case when c.xusertype=61 then 0 else c.xprec end),
97        (case when c.xusertype=61 then 0 else c.xscale end)
98        from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
99    var $hasTop = 'top';        // support mssql SELECT TOP 10 * FROM TABLE
100    var $hasGenID = true;
101    var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
102    var $sysTimeStamp = 'GetDate()';
103    var $maxParameterLen = 4000;
104    var $arrayClass = 'ADORecordSet_array_mssqlnative';
105    var $uniqueSort = true;
106    var $leftOuter = '*=';
107    var $rightOuter = '=*';
108    var $ansiOuter = true; // for mssql7 or later
109    var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
110    var $uniqueOrderBy = true;
111    var $_bindInputArray = true;
112    var $_dropSeqSQL = "drop table %s";
113   
114    function ADODB_mssqlnative()
115    {       
116        if ($this->debug) {
117            error_log("<pre>");
118            sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL );
119            sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL );
120            sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
121            sqlsrv_configure('warnings_return_as_errors', 0);
122        } else {
123            sqlsrv_set_error_handling(0);
124            sqlsrv_log_set_severity(0);
125            sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
126            sqlsrv_configure('warnings_return_as_errors', 0);
127        }
128    }
129
130    function ServerInfo()
131    {
132        global $ADODB_FETCH_MODE;
133        if ($this->fetchMode === false) {
134            $savem = $ADODB_FETCH_MODE;
135            $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
136        } else
137            $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
138        $arrServerInfo = sqlsrv_server_info($this->_connectionID);
139        $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
140        $arr['version'] = $arrServerInfo['SQLServerVersion'];//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        return $this->GetOne($this->identitySQL);
157    }
158
159    function _affectedrows()
160    {
161        return sqlsrv_rows_affected($this->_queryID);
162    }
163   
164    function CreateSequence($seq='adodbseq',$start=1)
165    {
166        if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
167        sqlsrv_begin_transaction($this->_connectionID);
168        $start -= 1;
169        $this->Execute("create table $seq (id int)");//was float(53)
170        $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
171        if (!$ok) {
172            if($this->debug) error_log("<hr>Error: ROLLBACK");
173            sqlsrv_rollback($this->_connectionID);
174            return false;
175        }
176        sqlsrv_commit($this->_connectionID);
177        return true;
178    }
179
180    function GenID($seq='adodbseq',$start=1)
181    {
182        if($this->debug) error_log("<hr>GenID($seq,$start)");
183        sqlsrv_begin_transaction($this->_connectionID);
184        $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
185        if (!$ok) {
186            $this->Execute("create table $seq (id int)");
187            $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
188            if (!$ok) {
189                if($this->debug) error_log("<hr>Error: ROLLBACK");
190                sqlsrv_rollback($this->_connectionID);
191                return false;
192            }
193            sqlsrv_commit($this->_connectionID);
194            return $start;
195        }
196        $num = $this->GetOne("select id from $seq");
197        sqlsrv_commit($this->_connectionID);
198        if($this->debug) error_log(" Returning: $num");
199        return $num;
200    }
201   
202    // Format date column in sql string given an input format that understands Y M D
203    function SQLDate($fmt, $col=false)
204    {   
205        if (!$col) $col = $this->sysTimeStamp;
206        $s = '';
207       
208        $len = strlen($fmt);
209        for ($i=0; $i < $len; $i++) {
210            if ($s) $s .= '+';
211            $ch = $fmt[$i];
212            switch($ch) {
213            case 'Y':
214            case 'y':
215                $s .= "datename(yyyy,$col)";
216                break;
217            case 'M':
218                $s .= "convert(char(3),$col,0)";
219                break;
220            case 'm':
221                $s .= "replace(str(month($col),2),' ','0')";
222                break;
223            case 'Q':
224            case 'q':
225                $s .= "datename(quarter,$col)";
226                break;
227            case 'D':
228            case 'd':
229                $s .= "replace(str(day($col),2),' ','0')";
230                break;
231            case 'h':
232                $s .= "substring(convert(char(14),$col,0),13,2)";
233                break;
234           
235            case 'H':
236                $s .= "replace(str(datepart(hh,$col),2),' ','0')";
237                break;
238               
239            case 'i':
240                $s .= "replace(str(datepart(mi,$col),2),' ','0')";
241                break;
242            case 's':
243                $s .= "replace(str(datepart(ss,$col),2),' ','0')";
244                break;
245            case 'a':
246            case 'A':
247                $s .= "substring(convert(char(19),$col,0),18,2)";
248                break;
249               
250            default:
251                if ($ch == '\\') {
252                    $i++;
253                    $ch = substr($fmt,$i,1);
254                }
255                $s .= $this->qstr($ch);
256                break;
257            }
258        }
259        return $s;
260    }
261
262   
263    function BeginTrans()
264    {
265        if ($this->transOff) return true;
266        $this->transCnt += 1;
267        if ($this->debug) error_log('<hr>begin transaction');
268        sqlsrv_begin_transaction($this->_connectionID);
269        return true;
270    }
271       
272    function CommitTrans($ok=true)
273    {
274        if ($this->transOff) return true;
275        if ($this->debug) error_log('<hr>commit transaction');
276        if (!$ok) return $this->RollbackTrans();
277        if ($this->transCnt) $this->transCnt -= 1;
278        sqlsrv_commit($this->_connectionID);
279        return true;
280    }
281    function RollbackTrans()
282    {
283        if ($this->transOff) return true;
284        if ($this->debug) error_log('<hr>rollback transaction');
285        if ($this->transCnt) $this->transCnt -= 1;
286        sqlsrv_rollback($this->_connectionID);
287        return true;
288    }
289   
290    function SetTransactionMode( $transaction_mode )
291    {
292        $this->_transmode  = $transaction_mode;
293        if (empty($transaction_mode)) {
294            $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
295            return;
296        }
297        if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
298        $this->Execute("SET TRANSACTION ".$transaction_mode);
299    }
300   
301    /*
302        Usage:
303       
304        $this->BeginTrans();
305        $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
306       
307        # some operation on both tables table1 and table2
308       
309        $this->CommitTrans();
310       
311        See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
312    */
313    function RowLock($tables,$where,$flds='top 1 null as ignore')
314    {
315        if (!$this->transCnt) $this->BeginTrans();
316        return $this->GetOne("select $flds from $tables with (ROWLOCK,HOLDLOCK) where $where");
317    }
318     
319    function SelectDB($dbName)
320    {
321        $this->database = $dbName;
322        $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
323        if ($this->_connectionID) {
324            $rs = $this->Execute('USE '.$dbName);
325            if($rs) {
326                return true;
327            } else return false;       
328        }
329        else return false; 
330    }
331   
332    function ErrorMsg()
333    {
334        $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
335        if($retErrors != null) {
336            foreach($retErrors as $arrError) {
337                $this->_errorMsg .= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
338                $this->_errorMsg .= "Error Code: ".$arrError[ 'code']."\n";
339                $this->_errorMsg .= "Message: ".$arrError[ 'message']."\n";
340            }
341        } else {
342            $this->_errorMsg = "No errors found";
343        }
344        return $this->_errorMsg;
345    }
346   
347    function ErrorNo()
348    {
349        if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
350        $err = sqlsrv_errors(SQLSRV_ERR_ALL);
351        if($err[0]) return $err[0]['code'];
352        else return -1;
353    }
354   
355    // returns true or false
356    function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
357    {
358        if (!function_exists('sqlsrv_connect')) return null;
359        $connectionInfo = array("Database"=>$argDatabasename,'UID'=>$argUsername,'PWD'=>$argPassword);
360        if ($this->debug) error_log("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
361        //if ($this->debug) error_log("<hr>_connectionID before: ".serialize($this->_connectionID));
362        if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) {
363            if ($this->debug) error_log( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
364            return false;
365        }
366        //if ($this->debug) error_log(" _connectionID after: ".serialize($this->_connectionID));
367        //if ($this->debug) error_log("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
368        return true;   
369    }
370   
371    // returns true or false
372    function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
373    {
374        //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
375        return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
376    }
377   
378    function Prepare($sql)
379    {
380        $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
381        if (!$stmt)  return $sql;
382        return array($sql,$stmt);
383    }
384   
385    // returns concatenated string
386    // MSSQL requires integers to be cast as strings
387    // automatically cast every datatype to VARCHAR(255)
388    // @author David Rogers (introspectshun)
389    function Concat()
390    {
391        $s = "";
392        $arr = func_get_args();
393
394        // Split single record on commas, if possible
395        if (sizeof($arr) == 1) {
396            foreach ($arr as $arg) {
397                $args = explode(',', $arg);
398            }
399            $arr = $args;
400        }
401
402        array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
403        $s = implode('+',$arr);
404        if (sizeof($arr) > 0) return "$s";
405       
406        return '';
407    }
408   
409    /*
410        Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
411        So all your blobs must be of type "image".
412       
413        Remember to set in php.ini the following...
414       
415        ; Valid range 0 - 2147483647. Default = 4096.
416        mssql.textlimit = 0 ; zero to pass through
417
418        ; Valid range 0 - 2147483647. Default = 4096.
419        mssql.textsize = 0 ; zero to pass through
420    */
421    function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
422    {
423   
424        if (strtoupper($blobtype) == 'CLOB') {
425            $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
426            return $this->Execute($sql) != false;
427        }
428        $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
429        return $this->Execute($sql) != false;
430    }
431   
432    // returns query ID if successful, otherwise false
433    function _query($sql,$inputarr=false)
434    {
435        $this->_errorMsg = false;
436        if (is_array($inputarr)) {
437            $rez = sqlsrv_query($this->_connectionID,$sql,$inputarr);
438        } else if (is_array($sql)) {
439            $rez = sqlsrv_query($this->_connectionID,$sql[1],$inputarr);
440        } else {
441            $rez = sqlsrv_query($this->_connectionID,$sql);
442        }
443        if ($this->debug) error_log("<hr>running query: ".var_export($sql,true)."<hr>input array: ".var_export($inputarr,true)."<hr>result: ".var_export($rez,true));//"<hr>connection: ".serialize($this->_connectionID)
444        //fix for returning true on anything besides select statements
445        if (is_array($sql)) $sql = $sql[1];
446        $sql = ltrim($sql);
447        if(stripos($sql, 'SELECT') !== 0 && $rez !== false) {
448            if ($this->debug) error_log(" isn't a select query, returning boolean true");
449            return true;
450        }
451        //end fix
452        if(!$rez) $rez = false;
453        return $rez;
454    }
455   
456    // returns true or false
457    function _close()
458    {
459        if ($this->transCnt) $this->RollbackTrans();
460        $rez = @sqlsrv_close($this->_connectionID);
461        $this->_connectionID = false;
462        return $rez;
463    }
464   
465    // mssql uses a default date like Dec 30 2000 12:00AM
466    function UnixDate($v)
467    {
468        return ADORecordSet_array_mssql::UnixDate($v);
469    }
470   
471    function UnixTimeStamp($v)
472    {
473        return ADORecordSet_array_mssql::UnixTimeStamp($v);
474    }   
475
476    function &MetaIndexes($table,$primary=false)
477    {
478        $table = $this->qstr($table);
479
480        $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
481            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,
482            CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
483            FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
484            INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
485            INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
486            WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
487            ORDER BY O.name, I.Name, K.keyno";
488
489        global $ADODB_FETCH_MODE;
490        $save = $ADODB_FETCH_MODE;
491        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
492        if ($this->fetchMode !== FALSE) {
493            $savem = $this->SetFetchMode(FALSE);
494        }
495       
496        $rs = $this->Execute($sql);
497        if (isset($savem)) {
498            $this->SetFetchMode($savem);
499        }
500        $ADODB_FETCH_MODE = $save;
501
502        if (!is_object($rs)) {
503            return FALSE;
504        }
505
506        $indexes = array();
507        while ($row = $rs->FetchRow()) {
508            if (!$primary && $row[5]) continue;
509           
510            $indexes[$row[0]]['unique'] = $row[6];
511            $indexes[$row[0]]['columns'][] = $row[1];
512        }
513        return $indexes;
514    }
515   
516    function MetaForeignKeys($table, $owner=false, $upper=false)
517    {
518        global $ADODB_FETCH_MODE;
519   
520        $save = $ADODB_FETCH_MODE;
521        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
522        $table = $this->qstr(strtoupper($table));
523       
524        $sql =
525            "select object_name(constid) as constraint_name,
526                col_name(fkeyid, fkey) as column_name,
527                object_name(rkeyid) as referenced_table_name,
528                col_name(rkeyid, rkey) as referenced_column_name
529            from sysforeignkeys
530            where upper(object_name(fkeyid)) = $table
531            order by constraint_name, referenced_table_name, keyno";
532       
533        $constraints =& $this->GetArray($sql);
534       
535        $ADODB_FETCH_MODE = $save;
536       
537        $arr = false;
538        foreach($constraints as $constr) {
539            //print_r($constr);
540            $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
541        }
542        if (!$arr) return false;
543       
544        $arr2 = false;
545       
546        foreach($arr as $k => $v) {
547            foreach($v as $a => $b) {
548                if ($upper) $a = strtoupper($a);
549                $arr2[$a] = $b;
550            }
551        }
552        return $arr2;
553    }
554
555    //From: Fernando Moreira <FMoreira@imediata.pt>
556    function MetaDatabases()
557    {
558        $this->SelectDB("master");
559        $rs =& $this->Execute($this->metaDatabasesSQL);
560        $rows = $rs->GetRows();
561        $ret = array();
562        for($i=0;$i<count($rows);$i++) {
563            $ret[] = $rows[$i][0];
564        }
565        $this->SelectDB($this->database);
566        if($ret)
567            return $ret;
568        else
569            return false;
570    }
571
572    // "Stein-Aksel Basma" <basma@accelero.no>
573    // tested with MSSQL 2000
574    function &MetaPrimaryKeys($table)
575    {
576        global $ADODB_FETCH_MODE;
577   
578        $schema = '';
579        $this->_findschema($table,$schema);
580        if (!$schema) $schema = $this->database;
581        if ($schema) $schema = "and k.table_catalog like '$schema%'";
582
583        $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
584        information_schema.table_constraints tc
585        where tc.constraint_name = k.constraint_name and tc.constraint_type =
586        'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
587       
588        $savem = $ADODB_FETCH_MODE;
589        $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
590        $a = $this->GetCol($sql);
591        $ADODB_FETCH_MODE = $savem;
592       
593        if ($a && sizeof($a)>0) return $a;
594        $false = false;
595        return $false;   
596    }
597
598   
599    function &MetaTables($ttype=false,$showSchema=false,$mask=false)
600    {
601        if ($mask) {
602            $save = $this->metaTablesSQL;
603            $mask = $this->qstr(($mask));
604            $this->metaTablesSQL .= " AND name like $mask";
605        }
606        $ret =& ADOConnection::MetaTables($ttype,$showSchema);
607
608        if ($mask) {
609            $this->metaTablesSQL = $save;
610        }
611        return $ret;
612    }
613}
614   
615/*--------------------------------------------------------------------------------------
616     Class Name: Recordset
617--------------------------------------------------------------------------------------*/
618
619class ADORecordset_mssqlnative extends ADORecordSet {   
620
621    var $databaseType = "mssqlnative";
622    var $canSeek = false;
623    var $fieldOffset = 0;
624    // _mths works only in non-localised system
625   
626    function ADORecordset_mssqlnative($id,$mode=false)
627    {
628        if ($mode === false) {
629            global $ADODB_FETCH_MODE;
630            $mode = $ADODB_FETCH_MODE;
631
632        }
633        $this->fetchMode = $mode;
634        return $this->ADORecordSet($id,$mode);
635    }
636   
637   
638    function _initrs()
639    {
640        global $ADODB_COUNTRECS;   
641        if ($this->connection->debug) error_log("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
642        /*$retRowsAff = sqlsrv_rows_affected($this->_queryID);//"If you need to determine the number of rows a query will return before retrieving the actual results, appending a SELECT COUNT ... query would let you get that information, and then a call to next_result would move you to the "real" results."
643        error_log("rowsaff: ".serialize($retRowsAff));
644        $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
645        $this->_numOfRows = -1;//not supported
646        $fieldmeta = sqlsrv_field_metadata($this->_queryID);
647        $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
648        if ($this->connection->debug) error_log("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
649    }
650   
651
652    //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
653    // get next resultset - requires PHP 4.0.5 or later
654    function NextRecordSet()
655    {
656        if (!sqlsrv_next_result($this->_queryID)) return false;
657        $this->_inited = false;
658        $this->bind = false;
659        $this->_currentRow = -1;
660        $this->Init();
661        return true;
662    }
663
664    /* Use associative array to get fields array */
665    function Fields($colname)
666    {
667        if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
668        if (!$this->bind) {
669            $this->bind = array();
670            for ($i=0; $i < $this->_numOfFields; $i++) {
671                $o = $this->FetchField($i);
672                $this->bind[strtoupper($o->name)] = $i;
673            }
674        }
675       
676        return $this->fields[$this->bind[strtoupper($colname)]];
677    }
678   
679    /*  Returns: an object containing field information.
680        Get column information in the Recordset object. fetchField() can be used in order to obtain information about
681        fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
682        fetchField() is retrieved.  */
683
684    function &FetchField($fieldOffset = -1)
685    {
686        if ($this->connection->debug) error_log("<hr>fetchfield: $fieldOffset, fetch array: <pre>".print_r($this->fields,true)."</pre> backtrace: ".adodb_backtrace(false));
687        if ($fieldOffset != -1) $this->fieldOffset = $fieldOffset;
688        $arrKeys = array_keys($this->fields);
689        if(array_key_exists($this->fieldOffset,$arrKeys) && !array_key_exists($arrKeys[$this->fieldOffset],$this->fields)) {
690            $f = false;
691        } else {
692            $f = $this->fields[ $arrKeys[$this->fieldOffset] ];
693            if($fieldOffset == -1) $this->fieldOffset++;
694        }
695
696        if (empty($f)) {
697            $f = false;//PHP Notice: Only variable references should be returned by reference
698        }
699        return $f;
700    }
701   
702    function _seek($row)
703    {
704        return false;//There is no support for cursors in the driver at this time.  All data is returned via forward-only streams.
705    }
706
707    // speedup
708    function MoveNext()
709    {
710        if ($this->connection->debug) error_log("movenext()");
711        //if ($this->connection->debug) error_log("eof (beginning): ".$this->EOF);
712        if ($this->EOF) return false;
713       
714        $this->_currentRow++;
715        if ($this->connection->debug) error_log("_currentRow: ".$this->_currentRow);
716       
717        if ($this->_fetch()) return true;
718        $this->EOF = true;
719        //if ($this->connection->debug) error_log("eof (end): ".$this->EOF);
720       
721        return false;
722    }
723
724   
725    // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
726    // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
727    function _fetch($ignore_fields=false)
728    {
729        if ($this->connection->debug) error_log("_fetch()");
730        if ($this->fetchMode & ADODB_FETCH_ASSOC) {
731            if ($this->fetchMode & ADODB_FETCH_NUM) {
732                if ($this->connection->debug) error_log("fetch mode: both");
733                $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
734            } else {
735                if ($this->connection->debug) error_log("fetch mode: assoc");
736                $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
737            }
738           
739            if (ADODB_ASSOC_CASE == 0) {
740                foreach($this->fields as $k=>$v) {
741                    $this->fields[strtolower($k)] = $v;
742                }
743            } else if (ADODB_ASSOC_CASE == 1) {
744                foreach($this->fields as $k=>$v) {
745                    $this->fields[strtoupper($k)] = $v;
746                }
747            }
748        } else {
749            if ($this->connection->debug) error_log("fetch mode: num");
750            $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
751        }
752        if(is_array($this->fields) && array_key_exists(1,$this->fields) && !array_key_exists(0,$this->fields)) {//fix fetch numeric keys since they're not 0 based
753            $arrFixed = array();
754            foreach($this->fields as $key=>$value) {
755                if(is_numeric($key)) {
756                    $arrFixed[$key-1] = $value;
757                } else {
758                    $arrFixed[$key] = $value;
759                }
760            }
761            //if($this->connection->debug) error_log("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
762            $this->fields = $arrFixed;
763        }
764        if(is_array($this->fields)) {
765            foreach($this->fields as $key=>$value) {
766                if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
767                    $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
768                }
769            }
770        }
771        if($this->fields === null) $this->fields = false;
772        if ($this->connection->debug) error_log("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
773        return $this->fields;
774    }
775   
776    /*  close() only needs to be called if you are worried about using too much memory while your script
777        is running. All associated result memory for the specified result identifier will automatically be freed.   */
778    function _close()
779    {
780        $rez = sqlsrv_free_stmt($this->_queryID);   
781        $this->_queryID = false;
782        return $rez;
783    }
784
785    // mssql uses a default date like Dec 30 2000 12:00AM
786    function UnixDate($v)
787    {
788        return ADORecordSet_array_mssqlnative::UnixDate($v);
789    }
790   
791    function UnixTimeStamp($v)
792    {
793        return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
794    }
795}
796
797
798class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
799    function ADORecordSet_array_mssqlnative($id=-1,$mode=false)
800    {
801        $this->ADORecordSet_array($id,$mode);
802    }
803   
804        // mssql uses a default date like Dec 30 2000 12:00AM
805    function UnixDate($v)
806    {
807   
808        if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
809       
810        global $ADODB_mssql_mths,$ADODB_mssql_date_order;
811   
812        //Dec 30 2000 12:00AM
813        if ($ADODB_mssql_date_order == 'dmy') {
814            if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
815                return parent::UnixDate($v);
816            }
817            if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
818           
819            $theday = $rr[1];
820            $themth =  substr(strtoupper($rr[2]),0,3);
821        } else {
822            if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
823                return parent::UnixDate($v);
824            }
825            if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
826           
827            $theday = $rr[2];
828            $themth = substr(strtoupper($rr[1]),0,3);
829        }
830        $themth = $ADODB_mssql_mths[$themth];
831        if ($themth <= 0) return false;
832        // h-m-s-MM-DD-YY
833        return  mktime(0,0,0,$themth,$theday,$rr[3]);
834    }
835   
836    function UnixTimeStamp($v)
837    {
838   
839        if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
840       
841        global $ADODB_mssql_mths,$ADODB_mssql_date_order;
842   
843        //Dec 30 2000 12:00AM
844         if ($ADODB_mssql_date_order == 'dmy') {
845             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})|"
846            ,$v, $rr)) return parent::UnixTimeStamp($v);
847            if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
848       
849            $theday = $rr[1];
850            $themth =  substr(strtoupper($rr[2]),0,3);
851        } else {
852            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})|"
853            ,$v, $rr)) return parent::UnixTimeStamp($v);
854            if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
855       
856            $theday = $rr[2];
857            $themth = substr(strtoupper($rr[1]),0,3);
858        }
859       
860        $themth = $ADODB_mssql_mths[$themth];
861        if ($themth <= 0) return false;
862       
863        switch (strtoupper($rr[6])) {
864        case 'P':
865            if ($rr[4]<12) $rr[4] += 12;
866            break;
867        case 'A':
868            if ($rr[4]==12) $rr[4] = 0;
869            break;
870        default:
871            break;
872        }
873        // h-m-s-MM-DD-YY
874        return  mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
875    }
876}
877
878/*
879Code Example 1:
880
881select  object_name(constid) as constraint_name,
882        object_name(fkeyid) as table_name,
883        col_name(fkeyid, fkey) as column_name,
884    object_name(rkeyid) as referenced_table_name,
885    col_name(rkeyid, rkey) as referenced_column_name
886from sysforeignkeys
887where object_name(fkeyid) = x
888order by constraint_name, table_name, referenced_table_name,  keyno
889
890Code Example 2:
891select  constraint_name,
892    column_name,
893    ordinal_position
894from information_schema.key_column_usage
895where constraint_catalog = db_name()
896and table_name = x
897order by constraint_name, ordinal_position
898
899http://www.databasejournal.com/scripts/article.php/1440551
900*/
901
902?>
Note: See TracBrowser for help on using the repository browser.