3 V5.19 23-Apr-2014 (c) 2000-2014 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.
7 Set tabs to 4 for best viewing.
9 Latest version is available at http://adodb.sourceforge.net
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
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
21 // security - hide paths
22 if (!defined('ADODB_DIR')) die();
24 if (!function_exists('sqlsrv_configure')) {
25 die("mssqlnative extension not installed");
28 if (!function_exists('sqlsrv_set_error_handling')) {
29 function sqlsrv_set_error_handling($constant) {
30 sqlsrv_configure("WarningsReturnAsErrors", $constant);
33 if (!function_exists('sqlsrv_log_set_severity')) {
34 function sqlsrv_log_set_severity($constant) {
35 sqlsrv_configure("LogSeverity", $constant);
38 if (!function_exists('sqlsrv_log_set_subsystems')) {
39 function sqlsrv_log_set_subsystems($constant) {
40 sqlsrv_configure("LogSubsystems", $constant);
45 //----------------------------------------------------------------
46 // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
47 // and this causes tons of problems because localized versions of
48 // MSSQL will return the dates in dmy or mdy order; and also the
49 // month strings depends on what language has been configured. The
50 // following two variables allow you to control the localization
53 // MORE LOCALIZATION INFO
54 // ----------------------
55 // To configure datetime, look for and modify sqlcommn.loc,
56 // typically found in c:\mssql\install
58 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
60 // CONVERT(char(12),datecol,120)
62 // Also if your month is showing as month-1,
63 // e.g. Jan 13, 2002 is showing as 13/0/2002, then see
64 // http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
65 // it's a localisation problem.
66 //----------------------------------------------------------------
69 // has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
70 if (ADODB_PHPVER >= 0x4300) {
71 // docs say 4.2.0, but testing shows only since 4.3.0 does it work!
72 ini_set('mssql.datetimeconvert',0);
74 global $ADODB_mssql_mths; // array, months must be upper-case
75 $ADODB_mssql_date_order = 'mdy';
76 $ADODB_mssql_mths = array(
77 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
78 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
81 class 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\TH:i:s'";
87 var $hasInsertID = true;
88 var $substr = "substring";
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'))";
100 c.isnullable as nullable,
101 c.cdefault as default_value,
103 t.length as type_length,
106 join systypes t on t.xusertype=c.xusertype
107 join sysobjects o on o.id=c.id
108 join sys.tables st on st.name=o.name
109 join sys.columns sc on sc.object_id = st.object_id and sc.name=c.name
111 var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
112 var $hasGenID = true;
113 var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
114 var $sysTimeStamp = 'GetDate()';
115 var $maxParameterLen = 4000;
116 var $arrayClass = 'ADORecordSet_array_mssqlnative';
117 var $uniqueSort = true;
118 var $leftOuter = '*=';
119 var $rightOuter = '=*';
120 var $ansiOuter = true; // for mssql7 or later
121 var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
122 var $uniqueOrderBy = true;
123 var $_bindInputArray = true;
124 var $_dropSeqSQL = "drop table %s";
125 var $connectionInfo = array();
126 var $sequences = false;
127 var $mssql_version = '';
129 function ADODB_mssqlnative()
133 sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL );
134 sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL );
135 sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
136 sqlsrv_configure('warnings_return_as_errors', 0);
138 sqlsrv_set_error_handling(0);
139 sqlsrv_log_set_severity(0);
140 sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
141 sqlsrv_configure('warnings_return_as_errors', 0);
144 function ServerVersion() {
145 $data = $this->ServerInfo();
146 if (preg_match('/^09/',$data['version'])){
150 $this->mssql_version = 9;
151 } elseif (preg_match('/^10/',$data['version'])){
155 $this->mssql_version = 10;
156 } elseif (preg_match('/^11/',$data['version'])){
160 $this->mssql_version = 11;
162 die("SQL SERVER VERSION {$data['version']} NOT SUPPORTED IN mssqlnative DRIVER");
165 function ServerInfo() {
166 global $ADODB_FETCH_MODE;
170 if ($this->fetchMode === false) {
171 $savem = $ADODB_FETCH_MODE;
172 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
173 } elseif ($this->fetchMode >=0 && $this->fetchMode <=2) {
174 $savem = $this->fetchMode;
176 $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
178 $arrServerInfo = sqlsrv_server_info($this->_connectionID);
179 $ADODB_FETCH_MODE = $savem;
180 $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
181 $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
185 function IfNull( $field, $ifNull )
187 return " ISNULL($field, $ifNull) "; // if MS SQL Server
193 // Returns the last IDENTITY value inserted into an IDENTITY column in
194 // the same scope. A scope is a module -- a stored procedure, trigger,
195 // function, or batch. Thus, two statements are in the same scope if
196 // they are in the same stored procedure, function, or batch.
197 return $this->lastInsertID;
200 function _affectedrows()
203 return sqlsrv_rows_affected($this->_queryID);
206 function GenID($seq='adodbseq',$start=1) {
207 if (!$this->mssql_version)
208 $this->ServerVersion();
209 switch($this->mssql_version){
212 return $this->GenID2008();
215 return $this->GenID2012();
220 function CreateSequence($seq='adodbseq',$start=1)
222 if (!$this->mssql_vesion)
223 $this->ServerVersion();
225 switch($this->mssql_version){
228 return $this->CreateSequence2008();
231 return $this->CreateSequence2012();
238 * For Server 2005,2008, duplicate a sequence with an identity table
240 function CreateSequence2008($seq='adodbseq',$start=1)
242 if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
243 sqlsrv_begin_transaction($this->_connectionID);
245 $this->Execute("create table $seq (id int)");//was float(53)
246 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
248 if($this->debug) error_log("<hr>Error: ROLLBACK");
249 sqlsrv_rollback($this->_connectionID);
252 sqlsrv_commit($this->_connectionID);
257 * Proper Sequences Only available to Server 2012 and up
259 function CreateSequence2012($seq='adodb',$start=1){
260 if (!$this->sequences){
261 $sql = "SELECT name FROM sys.sequences";
262 $this->sequences = $this->GetCol($sql);
264 $ok = $this->Execute("CREATE SEQUENCE $seq START WITH $start INCREMENT BY 1");
266 die("CANNOT CREATE SEQUENCE" . print_r(sqlsrv_errors(),true));
267 $this->sequences[] = $seq;
271 * For Server 2005,2008, duplicate a sequence with an identity table
273 function GenID2008($seq='adodbseq',$start=1)
275 if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
276 sqlsrv_begin_transaction($this->_connectionID);
277 $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
280 $this->Execute("create table $seq (id int)");//was float(53)
281 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
283 if($this->debug) error_log("<hr>Error: ROLLBACK");
284 sqlsrv_rollback($this->_connectionID);
288 $num = $this->GetOne("select id from $seq");
289 sqlsrv_commit($this->_connectionID);
293 * Only available to Server 2012 and up
294 * Cannot do this the normal adodb way by trapping an error if the
295 * sequence does not exist because sql server will auto create a
296 * sequence with the starting number of -9223372036854775808
298 function GenID2012($seq='adodbseq',$start=1)
302 * First time in create an array of sequence names that we
303 * can use in later requests to see if the sequence exists
304 * the overhead is creating a list of sequences every time
305 * we need access to at least 1. If we really care about
306 * performance, we could maybe flag a 'nocheck' class variable
308 if (!$this->sequences){
309 $sql = "SELECT name FROM sys.sequences";
310 $this->sequences = $this->GetCol($sql);
312 if (!is_array($this->sequences)
313 || is_array($this->sequences) && !in_array($seq,$this->sequences)){
314 $this->CreateSequence2012($seq='adodbseq',$start=1);
317 $num = $this->GetOne("SELECT NEXT VALUE FOR $seq");
321 // Format date column in sql string given an input format that understands Y M D
322 function SQLDate($fmt, $col=false)
324 if (!$col) $col = $this->sysTimeStamp;
328 for ($i=0; $i < $len; $i++) {
334 $s .= "datename(yyyy,$col)";
337 $s .= "convert(char(3),$col,0)";
340 $s .= "replace(str(month($col),2),' ','0')";
344 $s .= "datename(quarter,$col)";
348 $s .= "replace(str(day($col),2),' ','0')";
351 $s .= "substring(convert(char(14),$col,0),13,2)";
355 $s .= "replace(str(datepart(hh,$col),2),' ','0')";
359 $s .= "replace(str(datepart(mi,$col),2),' ','0')";
362 $s .= "replace(str(datepart(ss,$col),2),' ','0')";
366 $s .= "substring(convert(char(19),$col,0),18,2)";
372 $ch = substr($fmt,$i,1);
374 $s .= $this->qstr($ch);
382 function BeginTrans()
384 if ($this->transOff) return true;
385 $this->transCnt += 1;
386 if ($this->debug) error_log('<hr>begin transaction');
387 sqlsrv_begin_transaction($this->_connectionID);
391 function CommitTrans($ok=true)
393 if ($this->transOff) return true;
394 if ($this->debug) error_log('<hr>commit transaction');
395 if (!$ok) return $this->RollbackTrans();
396 if ($this->transCnt) $this->transCnt -= 1;
397 sqlsrv_commit($this->_connectionID);
400 function RollbackTrans()
402 if ($this->transOff) return true;
403 if ($this->debug) error_log('<hr>rollback transaction');
404 if ($this->transCnt) $this->transCnt -= 1;
405 sqlsrv_rollback($this->_connectionID);
409 function SetTransactionMode( $transaction_mode )
411 $this->_transmode = $transaction_mode;
412 if (empty($transaction_mode)) {
413 $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
416 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
417 $this->Execute("SET TRANSACTION ".$transaction_mode);
424 $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
426 # some operation on both tables table1 and table2
428 $this->CommitTrans();
430 See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
432 function RowLock($tables,$where,$col='1 as adodbignore')
434 if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
435 if (!$this->transCnt) $this->BeginTrans();
436 return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
439 function SelectDB($dbName)
441 $this->database = $dbName;
442 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
443 if ($this->_connectionID) {
444 $rs = $this->Execute('USE '.$dbName);
454 $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
455 if($retErrors != null) {
456 foreach($retErrors as $arrError) {
457 $this->_errorMsg .= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
458 $this->_errorMsg .= "Error Code: ".$arrError[ 'code']."\n";
459 $this->_errorMsg .= "Message: ".$arrError[ 'message']."\n";
462 $this->_errorMsg = "No errors found";
464 return $this->_errorMsg;
469 if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
470 $err = sqlsrv_errors(SQLSRV_ERR_ALL);
471 if($err[0]) return $err[0]['code'];
475 // returns true or false
476 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
478 if (!function_exists('sqlsrv_connect')) return null;
479 $connectionInfo = $this->connectionInfo;
480 $connectionInfo["Database"]=$argDatabasename;
481 $connectionInfo["UID"]=$argUsername;
482 $connectionInfo["PWD"]=$argPassword;
483 if ($this->debug) error_log("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
484 //if ($this->debug) error_log("<hr>_connectionID before: ".serialize($this->_connectionID));
485 if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) {
486 if ($this->debug) error_log( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
489 //if ($this->debug) error_log(" _connectionID after: ".serialize($this->_connectionID));
490 //if ($this->debug) error_log("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
494 // returns true or false
495 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
497 //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
498 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
501 function Prepare($sql)
503 return $sql; // prepare does not work properly with bind parameters as bind parameters are managed by sqlsrv_prepare!
505 $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
506 if (!$stmt) return $sql;
507 return array($sql,$stmt);
510 // returns concatenated string
511 // MSSQL requires integers to be cast as strings
512 // automatically cast every datatype to VARCHAR(255)
513 // @author David Rogers (introspectshun)
517 $arr = func_get_args();
519 // Split single record on commas, if possible
520 if (sizeof($arr) == 1) {
521 foreach ($arr as $arg) {
522 $args = explode(',', $arg);
527 array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
528 $s = implode('+',$arr);
529 if (sizeof($arr) > 0) return "$s";
535 Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
536 So all your blobs must be of type "image".
538 Remember to set in php.ini the following...
540 ; Valid range 0 - 2147483647. Default = 4096.
541 mssql.textlimit = 0 ; zero to pass through
543 ; Valid range 0 - 2147483647. Default = 4096.
544 mssql.textsize = 0 ; zero to pass through
546 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
549 if (strtoupper($blobtype) == 'CLOB') {
550 $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
551 return $this->Execute($sql) != false;
553 $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
554 return $this->Execute($sql) != false;
557 // returns query ID if successful, otherwise false
558 function _query($sql,$inputarr=false)
560 $this->_errorMsg = false;
562 if (is_array($sql)) $sql = $sql[1];
565 // handle native driver flaw for retrieving the last insert ID
566 if(preg_match('/^\W*(insert [^;]+);?$/i', $sql)) {
568 $sql .= '; '.$this->identitySQL; // select scope_identity()
571 $rez = sqlsrv_query($this->_connectionID, $sql, $inputarr);
573 $rez = sqlsrv_query($this->_connectionID,$sql);
576 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));
580 } else if ($insert) {
581 // retrieve the last insert ID (where applicable)
582 sqlsrv_next_result($rez);
584 $this->lastInsertID = sqlsrv_get_field($rez, 0);
589 // returns true or false
592 if ($this->transCnt) $this->RollbackTrans();
593 $rez = @sqlsrv_close($this->_connectionID);
594 $this->_connectionID = false;
598 // mssql uses a default date like Dec 30 2000 12:00AM
599 static function UnixDate($v)
601 return ADORecordSet_array_mssqlnative::UnixDate($v);
604 static function UnixTimeStamp($v)
606 return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
609 function MetaIndexes($table,$primary=false, $owner = false)
611 $table = $this->qstr($table);
613 $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
614 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,
615 CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
616 FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
617 INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
618 INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
619 WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
620 ORDER BY O.name, I.Name, K.keyno";
622 global $ADODB_FETCH_MODE;
623 $save = $ADODB_FETCH_MODE;
624 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
625 if ($this->fetchMode !== FALSE) {
626 $savem = $this->SetFetchMode(FALSE);
629 $rs = $this->Execute($sql);
631 $this->SetFetchMode($savem);
633 $ADODB_FETCH_MODE = $save;
635 if (!is_object($rs)) {
640 while ($row = $rs->FetchRow()) {
641 if (!$primary && $row[5]) continue;
643 $indexes[$row[0]]['unique'] = $row[6];
644 $indexes[$row[0]]['columns'][] = $row[1];
649 function MetaForeignKeys($table, $owner=false, $upper=false)
651 global $ADODB_FETCH_MODE;
653 $save = $ADODB_FETCH_MODE;
654 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
655 $table = $this->qstr(strtoupper($table));
658 "select object_name(constid) as constraint_name,
659 col_name(fkeyid, fkey) as column_name,
660 object_name(rkeyid) as referenced_table_name,
661 col_name(rkeyid, rkey) as referenced_column_name
663 where upper(object_name(fkeyid)) = $table
664 order by constraint_name, referenced_table_name, keyno";
666 $constraints =& $this->GetArray($sql);
668 $ADODB_FETCH_MODE = $save;
671 foreach($constraints as $constr) {
673 $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
675 if (!$arr) return false;
679 foreach($arr as $k => $v) {
680 foreach($v as $a => $b) {
681 if ($upper) $a = strtoupper($a);
688 //From: Fernando Moreira <FMoreira@imediata.pt>
689 function MetaDatabases()
691 $this->SelectDB("master");
692 $rs =& $this->Execute($this->metaDatabasesSQL);
693 $rows = $rs->GetRows();
695 for($i=0;$i<count($rows);$i++) {
696 $ret[] = $rows[$i][0];
698 $this->SelectDB($this->database);
705 // "Stein-Aksel Basma" <basma@accelero.no>
706 // tested with MSSQL 2000
707 function MetaPrimaryKeys($table, $owner=false)
709 global $ADODB_FETCH_MODE;
712 $this->_findschema($table,$schema);
713 if (!$schema) $schema = $this->database;
714 if ($schema) $schema = "and k.table_catalog like '$schema%'";
716 $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
717 information_schema.table_constraints tc
718 where tc.constraint_name = k.constraint_name and tc.constraint_type =
719 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
721 $savem = $ADODB_FETCH_MODE;
722 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
723 $a = $this->GetCol($sql);
724 $ADODB_FETCH_MODE = $savem;
726 if ($a && sizeof($a)>0) return $a;
732 function MetaTables($ttype=false,$showSchema=false,$mask=false)
735 $save = $this->metaTablesSQL;
736 $mask = $this->qstr(($mask));
737 $this->metaTablesSQL .= " AND name like $mask";
739 $ret = ADOConnection::MetaTables($ttype,$showSchema);
742 $this->metaTablesSQL = $save;
746 function MetaColumns($table, $upper=true, $schema=false){
749 static $cached_columns = array();
750 if ($this->cachedSchemaFlush)
751 $cached_columns = array();
753 if (array_key_exists($table,$cached_columns)){
754 return $cached_columns[$table];
758 if (!$this->mssql_version)
759 $this->ServerVersion();
761 $this->_findschema($table,$schema);
763 $dbName = $this->database;
764 $this->SelectDB($schema);
766 global $ADODB_FETCH_MODE;
767 $save = $ADODB_FETCH_MODE;
768 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
770 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
771 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
774 $this->SelectDB($dbName);
777 if (isset($savem)) $this->SetFetchMode($savem);
778 $ADODB_FETCH_MODE = $save;
779 if (!is_object($rs)) {
787 $fld = new ADOFieldObject();
788 if (array_key_exists(0,$rs->fields)) {
789 $fld->name = $rs->fields[0];
790 $fld->type = $rs->fields[1];
791 $fld->max_length = $rs->fields[2];
792 $fld->precision = $rs->fields[3];
793 $fld->scale = $rs->fields[4];
794 $fld->not_null =!$rs->fields[5];
795 $fld->has_default = $rs->fields[6];
796 $fld->xtype = $rs->fields[7];
797 $fld->type_length = $rs->fields[8];
798 $fld->auto_increment= $rs->fields[9];
800 $fld->name = $rs->fields['name'];
801 $fld->type = $rs->fields['type'];
802 $fld->max_length = $rs->fields['length'];
803 $fld->precision = $rs->fields['precision'];
804 $fld->scale = $rs->fields['scale'];
805 $fld->not_null =!$rs->fields['nullable'];
806 $fld->has_default = $rs->fields['default_value'];
807 $fld->xtype = $rs->fields['xtype'];
808 $fld->type_length = $rs->fields['type_length'];
809 $fld->auto_increment= $rs->fields['is_identity'];
812 if ($save == ADODB_FETCH_NUM)
815 $retarr[strtoupper($fld->name)] = $fld;
822 $cached_columns[$table] = $retarr;
829 /*--------------------------------------------------------------------------------------
830 Class Name: Recordset
831 --------------------------------------------------------------------------------------*/
833 class ADORecordset_mssqlnative extends ADORecordSet {
835 var $databaseType = "mssqlnative";
836 var $canSeek = false;
837 var $fieldOffset = 0;
838 // _mths works only in non-localised system
840 function ADORecordset_mssqlnative($id,$mode=false)
842 if ($mode === false) {
843 global $ADODB_FETCH_MODE;
844 $mode = $ADODB_FETCH_MODE;
847 $this->fetchMode = $mode;
848 return $this->ADORecordSet($id,$mode);
854 global $ADODB_COUNTRECS;
855 # KMN # if ($this->connection->debug) error_log("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
856 /*$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."
857 error_log("rowsaff: ".serialize($retRowsAff));
858 $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
859 $this->_numOfRows = -1;//not supported
860 $fieldmeta = sqlsrv_field_metadata($this->_queryID);
861 $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
862 # KMN # if ($this->connection->debug) error_log("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
864 * Copy the oracle method and cache the metadata at init time
866 if ($this->_numOfFields>0) {
867 $this->_fieldobjs = array();
868 $max = $this->_numOfFields;
869 for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
875 //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
876 // get next resultset - requires PHP 4.0.5 or later
877 function NextRecordSet()
879 if (!sqlsrv_next_result($this->_queryID)) return false;
880 $this->_inited = false;
882 $this->_currentRow = -1;
887 /* Use associative array to get fields array */
888 function Fields($colname)
890 if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
892 $this->bind = array();
893 for ($i=0; $i < $this->_numOfFields; $i++) {
894 $o = $this->FetchField($i);
895 $this->bind[strtoupper($o->name)] = $i;
899 return $this->fields[$this->bind[strtoupper($colname)]];
902 /* Returns: an object containing field information.
903 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
904 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
905 fetchField() is retrieved.
906 Designed By jcortinap#jc.com.mx
908 function _FetchField($fieldOffset = -1)
910 $_typeConversion = array(
911 -155 => 'datetimeoffset',
915 -11 => 'uniqueidentifier',
938 $fa = @sqlsrv_field_metadata($this->_queryID);
939 if ($fieldOffset != -1) {
940 $fa = $fa[$fieldOffset];
944 $f = false;//PHP Notice: Only variable references should be returned by reference
948 // Convert to an object
949 $fa = array_change_key_case($fa, CASE_LOWER);
951 if ($fieldOffset != -1)
954 'name' => $fa['name'],
955 'max_length' => $fa['size'],
956 'column_source' => $fa['name'],
957 'type' => $_typeConversion[$fa['type']]
962 foreach ($fa as $key => $value)
965 'name' => $value['name'],
966 'max_length' => $value['size'],
967 'column_source' => $value['name'],
968 'type' => $_typeConversion[$value['type']]
978 * Fetchfield copies the oracle method, it loads the field information
979 * into the _fieldobjs array once, to save multiple calls to the
980 * sqlsrv_field_metadata function
985 function FetchField($fieldOffset = -1)
987 return $this->_fieldobjs[$fieldOffset];
992 return false;//There is no support for cursors in the driver at this time. All data is returned via forward-only streams.
998 //# KMN # if ($this->connection->debug) error_log("movenext()");
999 //# KMN # if ($this->connection->debug) error_log("eof (beginning): ".$this->EOF);
1000 if ($this->EOF) return false;
1002 $this->_currentRow++;
1003 // # KMN # if ($this->connection->debug) error_log("_currentRow: ".$this->_currentRow);
1005 if ($this->_fetch()) return true;
1007 //# KMN # if ($this->connection->debug) error_log("eof (end): ".$this->EOF);
1013 // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
1014 // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
1015 function _fetch($ignore_fields=false)
1017 # KMN # if ($this->connection->debug) error_log("_fetch()");
1018 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1019 if ($this->fetchMode & ADODB_FETCH_NUM) {
1020 //# KMN # if ($this->connection->debug) error_log("fetch mode: both");
1021 $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
1023 //# KMN # if ($this->connection->debug) error_log("fetch mode: assoc");
1024 $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
1027 if (is_array($this->fields)) {
1028 if (ADODB_ASSOC_CASE == 0) {
1029 foreach($this->fields as $k=>$v) {
1030 $this->fields[strtolower($k)] = $v;
1032 } else if (ADODB_ASSOC_CASE == 1) {
1033 foreach($this->fields as $k=>$v) {
1034 $this->fields[strtoupper($k)] = $v;
1039 //# KMN # if ($this->connection->debug) error_log("fetch mode: num");
1040 $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
1042 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
1043 $arrFixed = array();
1044 foreach($this->fields as $key=>$value) {
1045 if(is_numeric($key)) {
1046 $arrFixed[$key-1] = $value;
1048 $arrFixed[$key] = $value;
1051 //if($this->connection->debug) error_log("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
1052 $this->fields = $arrFixed;
1054 if(is_array($this->fields)) {
1055 foreach($this->fields as $key=>$value) {
1056 if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
1057 $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
1061 if($this->fields === null) $this->fields = false;
1062 # KMN # if ($this->connection->debug) error_log("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
1063 return $this->fields;
1066 /* close() only needs to be called if you are worried about using too much memory while your script
1067 is running. All associated result memory for the specified result identifier will automatically be freed. */
1070 $rez = sqlsrv_free_stmt($this->_queryID);
1071 $this->_queryID = false;
1075 // mssql uses a default date like Dec 30 2000 12:00AM
1076 static function UnixDate($v)
1078 return ADORecordSet_array_mssqlnative::UnixDate($v);
1081 static function UnixTimeStamp($v)
1083 return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
1088 class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
1089 function ADORecordSet_array_mssqlnative($id=-1,$mode=false)
1091 $this->ADORecordSet_array($id,$mode);
1094 // mssql uses a default date like Dec 30 2000 12:00AM
1095 static function UnixDate($v)
1098 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
1100 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1102 //Dec 30 2000 12:00AM
1103 if ($ADODB_mssql_date_order == 'dmy') {
1104 if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1105 return parent::UnixDate($v);
1107 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1110 $themth = substr(strtoupper($rr[2]),0,3);
1112 if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1113 return parent::UnixDate($v);
1115 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1118 $themth = substr(strtoupper($rr[1]),0,3);
1120 $themth = $ADODB_mssql_mths[$themth];
1121 if ($themth <= 0) return false;
1123 return adodb_mktime(0,0,0,$themth,$theday,$rr[3]);
1126 static function UnixTimeStamp($v)
1129 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
1131 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1133 //Dec 30 2000 12:00AM
1134 if ($ADODB_mssql_date_order == 'dmy') {
1135 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})|"
1136 ,$v, $rr)) return parent::UnixTimeStamp($v);
1137 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1140 $themth = substr(strtoupper($rr[2]),0,3);
1142 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})|"
1143 ,$v, $rr)) return parent::UnixTimeStamp($v);
1144 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1147 $themth = substr(strtoupper($rr[1]),0,3);
1150 $themth = $ADODB_mssql_mths[$themth];
1151 if ($themth <= 0) return false;
1153 switch (strtoupper($rr[6])) {
1155 if ($rr[4]<12) $rr[4] += 12;
1158 if ($rr[4]==12) $rr[4] = 0;
1164 return adodb_mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
1171 select object_name(constid) as constraint_name,
1172 object_name(fkeyid) as table_name,
1173 col_name(fkeyid, fkey) as column_name,
1174 object_name(rkeyid) as referenced_table_name,
1175 col_name(rkeyid, rkey) as referenced_column_name
1177 where object_name(fkeyid) = x
1178 order by constraint_name, table_name, referenced_table_name, keyno
1181 select constraint_name,
1184 from information_schema.key_column_usage
1185 where constraint_catalog = db_name()
1187 order by constraint_name, ordinal_position
1189 http://www.databasejournal.com/scripts/article.php/1440551