3 @version v5.20.2 27-Dec-2015
4 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
5 @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
6 Released under both BSD license and Lesser GPL library license.
7 Whenever there is any discrepancy between the two licenses,
8 the BSD license will take precedence.
9 Set tabs to 4 for best viewing.
11 Latest version is available at http://adodb.sourceforge.net
13 Native mssql driver. Requires mssql client. Works on Windows.
14 http://www.microsoft.com/sql/technologies/php/default.mspx
15 To configure for Unix, see
16 http://phpbuilder.com/columns/alberto20000919.php3
18 $stream = sqlsrv_get_field($stmt, $index, SQLSRV_SQLTYPE_STREAM(SQLSRV_ENC_BINARY));
19 stream_filter_append($stream, "convert.iconv.ucs-2/utf-8"); // Voila, UTF-8 can be read directly from $stream
23 // security - hide paths
24 if (!defined('ADODB_DIR')) die();
26 if (!function_exists('sqlsrv_configure')) {
27 die("mssqlnative extension not installed");
30 if (!function_exists('sqlsrv_set_error_handling')) {
31 function sqlsrv_set_error_handling($constant) {
32 sqlsrv_configure("WarningsReturnAsErrors", $constant);
35 if (!function_exists('sqlsrv_log_set_severity')) {
36 function sqlsrv_log_set_severity($constant) {
37 sqlsrv_configure("LogSeverity", $constant);
40 if (!function_exists('sqlsrv_log_set_subsystems')) {
41 function sqlsrv_log_set_subsystems($constant) {
42 sqlsrv_configure("LogSubsystems", $constant);
47 //----------------------------------------------------------------
48 // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
49 // and this causes tons of problems because localized versions of
50 // MSSQL will return the dates in dmy or mdy order; and also the
51 // month strings depends on what language has been configured. The
52 // following two variables allow you to control the localization
55 // MORE LOCALIZATION INFO
56 // ----------------------
57 // To configure datetime, look for and modify sqlcommn.loc,
58 // typically found in c:\mssql\install
60 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
62 // CONVERT(char(12),datecol,120)
64 // Also if your month is showing as month-1,
65 // e.g. Jan 13, 2002 is showing as 13/0/2002, then see
66 // http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
67 // it's a localisation problem.
68 //----------------------------------------------------------------
71 // has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
72 if (ADODB_PHPVER
>= 0x4300) {
73 // docs say 4.2.0, but testing shows only since 4.3.0 does it work!
74 ini_set('mssql.datetimeconvert',0);
76 global $ADODB_mssql_mths; // array, months must be upper-case
77 $ADODB_mssql_date_order = 'mdy';
78 $ADODB_mssql_mths = array(
79 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
80 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
83 class ADODB_mssqlnative
extends ADOConnection
{
84 var $databaseType = "mssqlnative";
85 var $dataProvider = "mssqlnative";
86 var $replaceQuote = "''"; // string to use to replace quotes
87 var $fmtDate = "'Y-m-d'";
88 var $fmtTimeStamp = "'Y-m-d\TH:i:s'";
89 var $hasInsertID = true;
90 var $substr = "substring";
92 var $hasAffectedRows = true;
93 var $poorAffectedRows = false;
94 var $metaDatabasesSQL = "select name from sys.sysdatabases where name <> 'master'";
95 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'))";
100 c.xprec as precision,
102 c.isnullable as nullable,
103 c.cdefault as default_value,
105 t.length as type_length,
108 join systypes t on t.xusertype=c.xusertype
109 join sysobjects o on o.id=c.id
110 join sys.tables st on st.name=o.name
111 join sys.columns sc on sc.object_id = st.object_id and sc.name=c.name
113 var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
114 var $hasGenID = true;
115 var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
116 var $sysTimeStamp = 'GetDate()';
117 var $maxParameterLen = 4000;
118 var $arrayClass = 'ADORecordSet_array_mssqlnative';
119 var $uniqueSort = true;
120 var $leftOuter = '*=';
121 var $rightOuter = '=*';
122 var $ansiOuter = true; // for mssql7 or later
123 var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
124 var $uniqueOrderBy = true;
125 var $_bindInputArray = true;
126 var $_dropSeqSQL = "drop table %s";
127 var $connectionInfo = array();
128 var $sequences = false;
129 var $mssql_version = '';
131 function __construct()
134 ADOConnection
::outp("<pre>");
135 sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL
);
136 sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL
);
137 sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL
);
138 sqlsrv_configure('WarningsReturnAsErrors', 0);
140 sqlsrv_set_error_handling(0);
141 sqlsrv_log_set_severity(0);
142 sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL
);
143 sqlsrv_configure('WarningsReturnAsErrors', 0);
146 function ServerVersion() {
147 $data = $this->ServerInfo();
148 if (preg_match('/^09/',$data['version'])){
152 $this->mssql_version
= 9;
153 } elseif (preg_match('/^10/',$data['version'])){
157 $this->mssql_version
= 10;
158 } elseif (preg_match('/^11/',$data['version'])){
162 $this->mssql_version
= 11;
164 die("SQL SERVER VERSION {$data['version']} NOT SUPPORTED IN mssqlnative DRIVER");
167 function ServerInfo() {
168 global $ADODB_FETCH_MODE;
172 if ($this->fetchMode
=== false) {
173 $savem = $ADODB_FETCH_MODE;
174 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
175 } elseif ($this->fetchMode
>=0 && $this->fetchMode
<=2) {
176 $savem = $this->fetchMode
;
178 $savem = $this->SetFetchMode(ADODB_FETCH_NUM
);
180 $arrServerInfo = sqlsrv_server_info($this->_connectionID
);
181 $ADODB_FETCH_MODE = $savem;
182 $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
183 $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
187 function IfNull( $field, $ifNull )
189 return " ISNULL($field, $ifNull) "; // if MS SQL Server
195 // Returns the last IDENTITY value inserted into an IDENTITY column in
196 // the same scope. A scope is a module -- a stored procedure, trigger,
197 // function, or batch. Thus, two statements are in the same scope if
198 // they are in the same stored procedure, function, or batch.
199 return $this->lastInsertID
;
202 function _affectedrows()
205 return sqlsrv_rows_affected($this->_queryID
);
208 function GenID($seq='adodbseq',$start=1) {
209 if (!$this->mssql_version
)
210 $this->ServerVersion();
211 switch($this->mssql_version
){
214 return $this->GenID2008();
217 return $this->GenID2012();
222 function CreateSequence($seq='adodbseq',$start=1)
224 if (!$this->mssql_vesion
)
225 $this->ServerVersion();
227 switch($this->mssql_version
){
230 return $this->CreateSequence2008();
233 return $this->CreateSequence2012();
240 * For Server 2005,2008, duplicate a sequence with an identity table
242 function CreateSequence2008($seq='adodbseq',$start=1)
244 if($this->debug
) ADOConnection
::outp("<hr>CreateSequence($seq,$start)");
245 sqlsrv_begin_transaction($this->_connectionID
);
247 $this->Execute("create table $seq (id int)");//was float(53)
248 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
250 if($this->debug
) ADOConnection
::outp("<hr>Error: ROLLBACK");
251 sqlsrv_rollback($this->_connectionID
);
254 sqlsrv_commit($this->_connectionID
);
259 * Proper Sequences Only available to Server 2012 and up
261 function CreateSequence2012($seq='adodb',$start=1){
262 if (!$this->sequences
){
263 $sql = "SELECT name FROM sys.sequences";
264 $this->sequences
= $this->GetCol($sql);
266 $ok = $this->Execute("CREATE SEQUENCE $seq START WITH $start INCREMENT BY 1");
268 die("CANNOT CREATE SEQUENCE" . print_r(sqlsrv_errors(),true));
269 $this->sequences
[] = $seq;
273 * For Server 2005,2008, duplicate a sequence with an identity table
275 function GenID2008($seq='adodbseq',$start=1)
277 if($this->debug
) ADOConnection
::outp("<hr>CreateSequence($seq,$start)");
278 sqlsrv_begin_transaction($this->_connectionID
);
279 $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
282 $this->Execute("create table $seq (id int)");//was float(53)
283 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
285 if($this->debug
) ADOConnection
::outp("<hr>Error: ROLLBACK");
286 sqlsrv_rollback($this->_connectionID
);
290 $num = $this->GetOne("select id from $seq");
291 sqlsrv_commit($this->_connectionID
);
295 * Only available to Server 2012 and up
296 * Cannot do this the normal adodb way by trapping an error if the
297 * sequence does not exist because sql server will auto create a
298 * sequence with the starting number of -9223372036854775808
300 function GenID2012($seq='adodbseq',$start=1)
304 * First time in create an array of sequence names that we
305 * can use in later requests to see if the sequence exists
306 * the overhead is creating a list of sequences every time
307 * we need access to at least 1. If we really care about
308 * performance, we could maybe flag a 'nocheck' class variable
310 if (!$this->sequences
){
311 $sql = "SELECT name FROM sys.sequences";
312 $this->sequences
= $this->GetCol($sql);
314 if (!is_array($this->sequences
)
315 ||
is_array($this->sequences
) && !in_array($seq,$this->sequences
)){
316 $this->CreateSequence2012($seq='adodbseq',$start=1);
319 $num = $this->GetOne("SELECT NEXT VALUE FOR $seq");
323 // Format date column in sql string given an input format that understands Y M D
324 function SQLDate($fmt, $col=false)
326 if (!$col) $col = $this->sysTimeStamp
;
330 for ($i=0; $i < $len; $i++
) {
336 $s .= "datename(yyyy,$col)";
339 $s .= "convert(char(3),$col,0)";
342 $s .= "replace(str(month($col),2),' ','0')";
346 $s .= "datename(quarter,$col)";
350 $s .= "replace(str(day($col),2),' ','0')";
353 $s .= "substring(convert(char(14),$col,0),13,2)";
357 $s .= "replace(str(datepart(hh,$col),2),' ','0')";
361 $s .= "replace(str(datepart(mi,$col),2),' ','0')";
364 $s .= "replace(str(datepart(ss,$col),2),' ','0')";
368 $s .= "substring(convert(char(19),$col,0),18,2)";
374 $ch = substr($fmt,$i,1);
376 $s .= $this->qstr($ch);
384 function BeginTrans()
386 if ($this->transOff
) return true;
387 $this->transCnt +
= 1;
388 if ($this->debug
) ADOConnection
::outp('<hr>begin transaction');
389 sqlsrv_begin_transaction($this->_connectionID
);
393 function CommitTrans($ok=true)
395 if ($this->transOff
) return true;
396 if ($this->debug
) ADOConnection
::outp('<hr>commit transaction');
397 if (!$ok) return $this->RollbackTrans();
398 if ($this->transCnt
) $this->transCnt
-= 1;
399 sqlsrv_commit($this->_connectionID
);
402 function RollbackTrans()
404 if ($this->transOff
) return true;
405 if ($this->debug
) ADOConnection
::outp('<hr>rollback transaction');
406 if ($this->transCnt
) $this->transCnt
-= 1;
407 sqlsrv_rollback($this->_connectionID
);
411 function SetTransactionMode( $transaction_mode )
413 $this->_transmode
= $transaction_mode;
414 if (empty($transaction_mode)) {
415 $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
418 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
419 $this->Execute("SET TRANSACTION ".$transaction_mode);
426 $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
428 # some operation on both tables table1 and table2
430 $this->CommitTrans();
432 See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
434 function RowLock($tables,$where,$col='1 as adodbignore')
436 if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
437 if (!$this->transCnt
) $this->BeginTrans();
438 return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
441 function SelectDB($dbName)
443 $this->database
= $dbName;
444 $this->databaseName
= $dbName; # obsolete, retained for compat with older adodb versions
445 if ($this->_connectionID
) {
446 $rs = $this->Execute('USE '.$dbName);
456 $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL
);
457 if($retErrors != null) {
458 foreach($retErrors as $arrError) {
459 $this->_errorMsg
.= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
460 $this->_errorMsg
.= "Error Code: ".$arrError[ 'code']."\n";
461 $this->_errorMsg
.= "Message: ".$arrError[ 'message']."\n";
464 $this->_errorMsg
= "No errors found";
466 return $this->_errorMsg
;
471 if ($this->_logsql
&& $this->_errorCode
!== false) return $this->_errorCode
;
472 $err = sqlsrv_errors(SQLSRV_ERR_ALL
);
473 if($err[0]) return $err[0]['code'];
477 // returns true or false
478 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
480 if (!function_exists('sqlsrv_connect')) return null;
481 $connectionInfo = $this->connectionInfo
;
482 $connectionInfo["Database"]=$argDatabasename;
483 $connectionInfo["UID"]=$argUsername;
484 $connectionInfo["PWD"]=$argPassword;
486 foreach ($this->connectionParameters
as $parameter=>$value)
487 $connectionInfo[$parameter] = $value;
489 if ($this->debug
) ADOConnection
::outp("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
490 //if ($this->debug) ADOConnection::outp("<hr>_connectionID before: ".serialize($this->_connectionID));
491 if(!($this->_connectionID
= sqlsrv_connect($argHostname,$connectionInfo))) {
492 if ($this->debug
) ADOConnection
::outp( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
495 //if ($this->debug) ADOConnection::outp(" _connectionID after: ".serialize($this->_connectionID));
496 //if ($this->debug) ADOConnection::outp("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
500 // returns true or false
501 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
503 //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
504 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
507 function Prepare($sql)
509 return $sql; // prepare does not work properly with bind parameters as bind parameters are managed by sqlsrv_prepare!
511 $stmt = sqlsrv_prepare( $this->_connectionID
, $sql);
512 if (!$stmt) return $sql;
513 return array($sql,$stmt);
516 // returns concatenated string
517 // MSSQL requires integers to be cast as strings
518 // automatically cast every datatype to VARCHAR(255)
519 // @author David Rogers (introspectshun)
523 $arr = func_get_args();
525 // Split single record on commas, if possible
526 if (sizeof($arr) == 1) {
527 foreach ($arr as $arg) {
528 $args = explode(',', $arg);
533 array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
534 $s = implode('+',$arr);
535 if (sizeof($arr) > 0) return "$s";
541 Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
542 So all your blobs must be of type "image".
544 Remember to set in php.ini the following...
546 ; Valid range 0 - 2147483647. Default = 4096.
547 mssql.textlimit = 0 ; zero to pass through
549 ; Valid range 0 - 2147483647. Default = 4096.
550 mssql.textsize = 0 ; zero to pass through
552 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
555 if (strtoupper($blobtype) == 'CLOB') {
556 $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
557 return $this->Execute($sql) != false;
559 $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
560 return $this->Execute($sql) != false;
563 // returns query ID if successful, otherwise false
564 function _query($sql,$inputarr=false)
566 $this->_errorMsg
= false;
568 if (is_array($sql)) $sql = $sql[1];
571 // handle native driver flaw for retrieving the last insert ID
572 if(preg_match('/^\W*insert\s(?:(?:(?:\'\')*\'[^\']+\'(?:\'\')*)|[^;\'])*;?$/i', $sql)) {
574 $sql .= '; '.$this->identitySQL
; // select scope_identity()
577 $rez = sqlsrv_query($this->_connectionID
, $sql, $inputarr);
579 $rez = sqlsrv_query($this->_connectionID
,$sql);
582 if ($this->debug
) ADOConnection
::outp("<hr>running query: ".var_export($sql,true)."<hr>input array: ".var_export($inputarr,true)."<hr>result: ".var_export($rez,true));
586 } else if ($insert) {
587 // retrieve the last insert ID (where applicable)
588 while ( sqlsrv_next_result($rez) ) {
590 $this->lastInsertID
= sqlsrv_get_field($rez, 0);
596 // returns true or false
599 if ($this->transCnt
) $this->RollbackTrans();
600 $rez = @sqlsrv_close
($this->_connectionID
);
601 $this->_connectionID
= false;
605 // mssql uses a default date like Dec 30 2000 12:00AM
606 static function UnixDate($v)
608 return ADORecordSet_array_mssqlnative
::UnixDate($v);
611 static function UnixTimeStamp($v)
613 return ADORecordSet_array_mssqlnative
::UnixTimeStamp($v);
616 function MetaIndexes($table,$primary=false, $owner = false)
618 $table = $this->qstr($table);
620 $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
621 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,
622 CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
623 FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
624 INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
625 INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
626 WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
627 ORDER BY O.name, I.Name, K.keyno";
629 global $ADODB_FETCH_MODE;
630 $save = $ADODB_FETCH_MODE;
631 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
632 if ($this->fetchMode
!== FALSE) {
633 $savem = $this->SetFetchMode(FALSE);
636 $rs = $this->Execute($sql);
638 $this->SetFetchMode($savem);
640 $ADODB_FETCH_MODE = $save;
642 if (!is_object($rs)) {
647 while ($row = $rs->FetchRow()) {
648 if (!$primary && $row[5]) continue;
650 $indexes[$row[0]]['unique'] = $row[6];
651 $indexes[$row[0]]['columns'][] = $row[1];
656 function MetaForeignKeys($table, $owner=false, $upper=false)
658 global $ADODB_FETCH_MODE;
660 $save = $ADODB_FETCH_MODE;
661 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
662 $table = $this->qstr(strtoupper($table));
665 "select object_name(constid) as constraint_name,
666 col_name(fkeyid, fkey) as column_name,
667 object_name(rkeyid) as referenced_table_name,
668 col_name(rkeyid, rkey) as referenced_column_name
670 where upper(object_name(fkeyid)) = $table
671 order by constraint_name, referenced_table_name, keyno";
673 $constraints =& $this->GetArray($sql);
675 $ADODB_FETCH_MODE = $save;
678 foreach($constraints as $constr) {
680 $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
682 if (!$arr) return false;
686 foreach($arr as $k => $v) {
687 foreach($v as $a => $b) {
688 if ($upper) $a = strtoupper($a);
695 //From: Fernando Moreira <FMoreira@imediata.pt>
696 function MetaDatabases()
698 $this->SelectDB("master");
699 $rs =& $this->Execute($this->metaDatabasesSQL
);
700 $rows = $rs->GetRows();
702 for($i=0;$i<count($rows);$i++
) {
703 $ret[] = $rows[$i][0];
705 $this->SelectDB($this->database
);
712 // "Stein-Aksel Basma" <basma@accelero.no>
713 // tested with MSSQL 2000
714 function MetaPrimaryKeys($table, $owner=false)
716 global $ADODB_FETCH_MODE;
719 $this->_findschema($table,$schema);
720 if (!$schema) $schema = $this->database
;
721 if ($schema) $schema = "and k.table_catalog like '$schema%'";
723 $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
724 information_schema.table_constraints tc
725 where tc.constraint_name = k.constraint_name and tc.constraint_type =
726 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
728 $savem = $ADODB_FETCH_MODE;
729 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
730 $a = $this->GetCol($sql);
731 $ADODB_FETCH_MODE = $savem;
733 if ($a && sizeof($a)>0) return $a;
739 function MetaTables($ttype=false,$showSchema=false,$mask=false)
742 $save = $this->metaTablesSQL
;
743 $mask = $this->qstr(($mask));
744 $this->metaTablesSQL
.= " AND name like $mask";
746 $ret = ADOConnection
::MetaTables($ttype,$showSchema);
749 $this->metaTablesSQL
= $save;
753 function MetaColumns($table, $upper=true, $schema=false){
756 static $cached_columns = array();
757 if ($this->cachedSchemaFlush
)
758 $cached_columns = array();
760 if (array_key_exists($table,$cached_columns)){
761 return $cached_columns[$table];
765 if (!$this->mssql_version
)
766 $this->ServerVersion();
768 $this->_findschema($table,$schema);
770 $dbName = $this->database
;
771 $this->SelectDB($schema);
773 global $ADODB_FETCH_MODE;
774 $save = $ADODB_FETCH_MODE;
775 $ADODB_FETCH_MODE = ADODB_FETCH_NUM
;
777 if ($this->fetchMode
!== false) $savem = $this->SetFetchMode(false);
778 $rs = $this->Execute(sprintf($this->metaColumnsSQL
,$table));
781 $this->SelectDB($dbName);
784 if (isset($savem)) $this->SetFetchMode($savem);
785 $ADODB_FETCH_MODE = $save;
786 if (!is_object($rs)) {
794 $fld = new ADOFieldObject();
795 if (array_key_exists(0,$rs->fields
)) {
796 $fld->name
= $rs->fields
[0];
797 $fld->type
= $rs->fields
[1];
798 $fld->max_length
= $rs->fields
[2];
799 $fld->precision
= $rs->fields
[3];
800 $fld->scale
= $rs->fields
[4];
801 $fld->not_null
=!$rs->fields
[5];
802 $fld->has_default
= $rs->fields
[6];
803 $fld->xtype
= $rs->fields
[7];
804 $fld->type_length
= $rs->fields
[8];
805 $fld->auto_increment
= $rs->fields
[9];
807 $fld->name
= $rs->fields
['name'];
808 $fld->type
= $rs->fields
['type'];
809 $fld->max_length
= $rs->fields
['length'];
810 $fld->precision
= $rs->fields
['precision'];
811 $fld->scale
= $rs->fields
['scale'];
812 $fld->not_null
=!$rs->fields
['nullable'];
813 $fld->has_default
= $rs->fields
['default_value'];
814 $fld->xtype
= $rs->fields
['xtype'];
815 $fld->type_length
= $rs->fields
['type_length'];
816 $fld->auto_increment
= $rs->fields
['is_identity'];
819 if ($save == ADODB_FETCH_NUM
)
822 $retarr[strtoupper($fld->name
)] = $fld;
829 $cached_columns[$table] = $retarr;
836 /*--------------------------------------------------------------------------------------
837 Class Name: Recordset
838 --------------------------------------------------------------------------------------*/
840 class ADORecordset_mssqlnative
extends ADORecordSet
{
842 var $databaseType = "mssqlnative";
843 var $canSeek = false;
844 var $fieldOffset = 0;
845 // _mths works only in non-localised system
847 function __construct($id,$mode=false)
849 if ($mode === false) {
850 global $ADODB_FETCH_MODE;
851 $mode = $ADODB_FETCH_MODE;
854 $this->fetchMode
= $mode;
855 return parent
::__construct($id,$mode);
861 global $ADODB_COUNTRECS;
862 # KMN # if ($this->connection->debug) ADOConnection::outp("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
863 /*$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."
864 ADOConnection::outp("rowsaff: ".serialize($retRowsAff));
865 $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
866 $this->_numOfRows
= -1;//not supported
867 $fieldmeta = sqlsrv_field_metadata($this->_queryID
);
868 $this->_numOfFields
= ($fieldmeta)?
count($fieldmeta):-1;
869 # KMN # if ($this->connection->debug) ADOConnection::outp("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
871 * Copy the oracle method and cache the metadata at init time
873 if ($this->_numOfFields
>0) {
874 $this->_fieldobjs
= array();
875 $max = $this->_numOfFields
;
876 for ($i=0;$i<$max; $i++
) $this->_fieldobjs
[] = $this->_FetchField($i);
882 //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
883 // get next resultset - requires PHP 4.0.5 or later
884 function NextRecordSet()
886 if (!sqlsrv_next_result($this->_queryID
)) return false;
887 $this->_inited
= false;
889 $this->_currentRow
= -1;
894 /* Use associative array to get fields array */
895 function Fields($colname)
897 if ($this->fetchMode
!= ADODB_FETCH_NUM
) return $this->fields
[$colname];
899 $this->bind
= array();
900 for ($i=0; $i < $this->_numOfFields
; $i++
) {
901 $o = $this->FetchField($i);
902 $this->bind
[strtoupper($o->name
)] = $i;
906 return $this->fields
[$this->bind
[strtoupper($colname)]];
909 /* Returns: an object containing field information.
910 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
911 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
912 fetchField() is retrieved.
913 Designed By jcortinap#jc.com.mx
915 function _FetchField($fieldOffset = -1)
917 $_typeConversion = array(
918 -155 => 'datetimeoffset',
922 -11 => 'uniqueidentifier',
945 $fa = @sqlsrv_field_metadata
($this->_queryID
);
946 if ($fieldOffset != -1) {
947 $fa = $fa[$fieldOffset];
951 $f = false;//PHP Notice: Only variable references should be returned by reference
955 // Convert to an object
956 $fa = array_change_key_case($fa, CASE_LOWER
);
958 if ($fieldOffset != -1)
961 'name' => $fa['name'],
962 'max_length' => $fa['size'],
963 'column_source' => $fa['name'],
964 'type' => $_typeConversion[$fa['type']]
969 foreach ($fa as $key => $value)
972 'name' => $value['name'],
973 'max_length' => $value['size'],
974 'column_source' => $value['name'],
975 'type' => $_typeConversion[$value['type']]
985 * Fetchfield copies the oracle method, it loads the field information
986 * into the _fieldobjs array once, to save multiple calls to the
987 * sqlsrv_field_metadata function
992 function FetchField($fieldOffset = -1)
994 return $this->_fieldobjs
[$fieldOffset];
999 return false;//There is no support for cursors in the driver at this time. All data is returned via forward-only streams.
1005 //# KMN # if ($this->connection->debug) ADOConnection::outp("movenext()");
1006 //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (beginning): ".$this->EOF);
1007 if ($this->EOF
) return false;
1009 $this->_currentRow++
;
1010 // # KMN # if ($this->connection->debug) ADOConnection::outp("_currentRow: ".$this->_currentRow);
1012 if ($this->_fetch()) return true;
1014 //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (end): ".$this->EOF);
1020 // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
1021 // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
1022 function _fetch($ignore_fields=false)
1024 # KMN # if ($this->connection->debug) ADOConnection::outp("_fetch()");
1025 if ($this->fetchMode
& ADODB_FETCH_ASSOC
) {
1026 if ($this->fetchMode
& ADODB_FETCH_NUM
) {
1027 //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: both");
1028 $this->fields
= @sqlsrv_fetch_array
($this->_queryID
,SQLSRV_FETCH_BOTH
);
1030 //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: assoc");
1031 $this->fields
= @sqlsrv_fetch_array
($this->_queryID
,SQLSRV_FETCH_ASSOC
);
1034 if (is_array($this->fields
)) {
1035 if (ADODB_ASSOC_CASE
== 0) {
1036 foreach($this->fields
as $k=>$v) {
1037 $this->fields
[strtolower($k)] = $v;
1039 } else if (ADODB_ASSOC_CASE
== 1) {
1040 foreach($this->fields
as $k=>$v) {
1041 $this->fields
[strtoupper($k)] = $v;
1046 //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: num");
1047 $this->fields
= @sqlsrv_fetch_array
($this->_queryID
,SQLSRV_FETCH_NUMERIC
);
1049 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
1050 $arrFixed = array();
1051 foreach($this->fields
as $key=>$value) {
1052 if(is_numeric($key)) {
1053 $arrFixed[$key-1] = $value;
1055 $arrFixed[$key] = $value;
1058 //if($this->connection->debug) ADOConnection::outp("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
1059 $this->fields
= $arrFixed;
1061 if(is_array($this->fields
)) {
1062 foreach($this->fields
as $key=>$value) {
1063 if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
1064 $this->fields
[$key] = $value->format("Y-m-d\TH:i:s\Z");
1068 if($this->fields
=== null) $this->fields
= false;
1069 # KMN # if ($this->connection->debug) ADOConnection::outp("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
1070 return $this->fields
;
1073 /* close() only needs to be called if you are worried about using too much memory while your script
1074 is running. All associated result memory for the specified result identifier will automatically be freed. */
1077 if($this->_queryID
) {
1078 $rez = sqlsrv_free_stmt($this->_queryID
);
1080 $this->_queryID
= false;
1084 // mssql uses a default date like Dec 30 2000 12:00AM
1085 static function UnixDate($v)
1087 return ADORecordSet_array_mssqlnative
::UnixDate($v);
1090 static function UnixTimeStamp($v)
1092 return ADORecordSet_array_mssqlnative
::UnixTimeStamp($v);
1097 class ADORecordSet_array_mssqlnative
extends ADORecordSet_array
{
1098 function __construct($id=-1,$mode=false)
1100 parent
::__construct($id,$mode);
1103 // mssql uses a default date like Dec 30 2000 12:00AM
1104 static function UnixDate($v)
1107 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER
>= 0x4200) return parent
::UnixDate($v);
1109 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1111 //Dec 30 2000 12:00AM
1112 if ($ADODB_mssql_date_order == 'dmy') {
1113 if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1114 return parent
::UnixDate($v);
1116 if ($rr[3] <= TIMESTAMP_FIRST_YEAR
) return 0;
1119 $themth = substr(strtoupper($rr[2]),0,3);
1121 if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1122 return parent
::UnixDate($v);
1124 if ($rr[3] <= TIMESTAMP_FIRST_YEAR
) return 0;
1127 $themth = substr(strtoupper($rr[1]),0,3);
1129 $themth = $ADODB_mssql_mths[$themth];
1130 if ($themth <= 0) return false;
1132 return adodb_mktime(0,0,0,$themth,$theday,$rr[3]);
1135 static function UnixTimeStamp($v)
1138 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER
>= 0x4200) return parent
::UnixTimeStamp($v);
1140 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1142 //Dec 30 2000 12:00AM
1143 if ($ADODB_mssql_date_order == 'dmy') {
1144 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})|"
1145 ,$v, $rr)) return parent
::UnixTimeStamp($v);
1146 if ($rr[3] <= TIMESTAMP_FIRST_YEAR
) return 0;
1149 $themth = substr(strtoupper($rr[2]),0,3);
1151 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})|"
1152 ,$v, $rr)) return parent
::UnixTimeStamp($v);
1153 if ($rr[3] <= TIMESTAMP_FIRST_YEAR
) return 0;
1156 $themth = substr(strtoupper($rr[1]),0,3);
1159 $themth = $ADODB_mssql_mths[$themth];
1160 if ($themth <= 0) return false;
1162 switch (strtoupper($rr[6])) {
1164 if ($rr[4]<12) $rr[4] +
= 12;
1167 if ($rr[4]==12) $rr[4] = 0;
1173 return adodb_mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
1180 select object_name(constid) as constraint_name,
1181 object_name(fkeyid) as table_name,
1182 col_name(fkeyid, fkey) as column_name,
1183 object_name(rkeyid) as referenced_table_name,
1184 col_name(rkeyid, rkey) as referenced_column_name
1186 where object_name(fkeyid) = x
1187 order by constraint_name, table_name, referenced_table_name, keyno
1190 select constraint_name,
1193 from information_schema.key_column_usage
1194 where constraint_catalog = db_name()
1196 order by constraint_name, ordinal_position
1198 http://www.databasejournal.com/scripts/article.php/1440551