Source for file adodb-mssql.inc.php
Documentation is available at adodb-mssql.inc.php
V4.97 22 Jan 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
the BSD license will take precedence.
Set tabs to 4 for best viewing.
Latest version is available at http://adodb.sourceforge.net
Native mssql driver. Requires mssql client. Works on Windows.
To configure for Unix, see
http://phpbuilder.com/columns/alberto20000919.php3
//----------------------------------------------------------------
// MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
// and this causes tons of problems because localized versions of
// MSSQL will return the dates in dmy or mdy order; and also the
// month strings depends on what language has been configured. The
// following two variables allow you to control the localization
// MORE LOCALIZATION INFO
// ----------------------
// To configure datetime, look for and modify sqlcommn.loc,
// typically found in c:\mssql\install
// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
// CONVERT(char(12),datecol,120)
// Also if your month is showing as month-1,
// e.g. Jan 13, 2002 is showing as 13/0/2002, then see
// http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
// it's a localisation problem.
//----------------------------------------------------------------
// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
// docs say 4.2.0, but testing shows only since 4.3.0 does it work!
ini_set('mssql.datetimeconvert',0);
global $ADODB_mssql_mths; // array, months must be upper-case
$ADODB_mssql_date_order = 'mdy';
$ADODB_mssql_mths = array(
'JAN'=> 1,'FEB'=> 2,'MAR'=> 3,'APR'=> 4,'MAY'=> 5,'JUN'=> 6,
'JUL'=> 7,'AUG'=> 8,'SEP'=> 9,'OCT'=> 10,'NOV'=> 11,'DEC'=> 12);
//---------------------------------------------------------------------------
// Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
// just after you connect to the database. Supports mdy and dmy only.
// Not required for PHP 4.2.0 and above.
global $ADODB_mssql_date_order;
$adate = $conn->GetOne('select getdate()');
//ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
$ADODB_mssql_date_order = 'dmy';
$ADODB_mssql_date_order = 'mdy';
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'))";
"select c.name,t.name,c.length,
(case when c.xusertype=61 then 0 else c.xprec end),
(case when c.xusertype=61 then 0 else c.xscale end)
from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
global $ADODB_FETCH_MODE;
$savem = $ADODB_FETCH_MODE;
$row = $this->GetRow("execute sp_server_info 2");
$ADODB_FETCH_MODE = $savem;
$arr['description'] = $row[2];
function IfNull( $field, $ifNull )
return " ISNULL($field, $ifNull) "; // if MS SQL Server
// Returns the last IDENTITY value inserted into an IDENTITY column in
// the same scope. A scope is a module -- a stored procedure, trigger,
// function, or batch. Thus, two statements are in the same scope if
// they are in the same stored procedure, function, or batch.
return $this->GetOne('select @@rowcount');
$this->Execute('BEGIN TRANSACTION adodbseq');
$this->Execute("create table $seq (id float(53))");
$ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
$this->Execute('ROLLBACK TRANSACTION adodbseq');
$this->Execute('COMMIT TRANSACTION adodbseq');
function GenID($seq= 'adodbseq',$start= 1)
$this->Execute('BEGIN TRANSACTION adodbseq');
$ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
$this->Execute("create table $seq (id float(53))");
$ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
$this->Execute('ROLLBACK TRANSACTION adodbseq');
$this->Execute('COMMIT TRANSACTION adodbseq');
$num = $this->GetOne("select id from $seq");
$this->Execute('COMMIT TRANSACTION adodbseq');
// in old implementation, pre 1.90, we returned GUID...
//return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'");
function &SelectLimit($sql,$nrows=- 1,$offset=- 1, $inputarr= false,$secs2cache= 0)
if ($nrows > 0 && $offset <= 0) {
'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '. $this->hasTop. " $nrows ",$sql);
$rs = & $this->Execute($sql,$inputarr);
// Format date column in sql string given an input format that understands Y M D
for ($i= 0; $i < $len; $i++ ) {
$s .= "datename(yyyy,$col)";
$s .= "convert(char(3),$col,0)";
$s .= "replace(str(month($col),2),' ','0')";
$s .= "datename(quarter,$col)";
$s .= "replace(str(day($col),2),' ','0')";
$s .= "substring(convert(char(14),$col,0),13,2)";
$s .= "replace(str(datepart(hh,$col),2),' ','0')";
$s .= "replace(str(datepart(mi,$col),2),' ','0')";
$s .= "replace(str(datepart(ss,$col),2),' ','0')";
$s .= "substring(convert(char(19),$col,0),18,2)";
if (empty($transaction_mode)) {
$this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '. $transaction_mode;
$this->Execute("SET TRANSACTION ". $transaction_mode);
$this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
# some operation on both tables table1 and table2
See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
function RowLock($tables,$where,$flds= 'top 1 null as ignore')
return $this->GetOne("select $flds from $tables with (ROWLOCK,HOLDLOCK) where $where");
$table = $this->qstr($table);
$sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
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,
CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
ORDER BY O.name, I.Name, K.keyno";
global $ADODB_FETCH_MODE;
$save = $ADODB_FETCH_MODE;
$ADODB_FETCH_MODE = $save;
while ($row = $rs->FetchRow()) {
if (!$primary && $row[5]) continue;
$indexes[$row[0]]['unique'] = $row[6];
$indexes[$row[0]]['columns'][] = $row[1];
global $ADODB_FETCH_MODE;
$save = $ADODB_FETCH_MODE;
"select object_name(constid) as constraint_name,
col_name(fkeyid, fkey) as column_name,
object_name(rkeyid) as referenced_table_name,
col_name(rkeyid, rkey) as referenced_column_name
where upper(object_name(fkeyid)) = $table
order by constraint_name, referenced_table_name, keyno";
$ADODB_FETCH_MODE = $save;
foreach($constraints as $constr) {
$arr[$constr[0]][$constr[2]][] = $constr[1]. '='. $constr[3];
foreach($arr as $k => $v) {
foreach($v as $a => $b) {
//From: Fernando Moreira <FMoreira@imediata.pt>
if(@mssql_select_db("master")) {
while($tmpAr= @mssql_fetch_row($rs))
// "Stein-Aksel Basma" <basma@accelero.no>
// tested with MSSQL 2000
global $ADODB_FETCH_MODE;
if (!$schema) $schema = $this->database;
if ($schema) $schema = "and k.table_catalog like '$schema%'";
$sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
information_schema.table_constraints tc
where tc.constraint_name = k.constraint_name and tc.constraint_type =
'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
$savem = $ADODB_FETCH_MODE;
$ADODB_FETCH_MODE = $savem;
if ($a && sizeof($a)> 0) return $a;
function &MetaTables($ttype= false,$showSchema= false,$mask= false)
$mask = $this->qstr(($mask));
$this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
return @mssql_select_db($dbName);
$arr = mssql_fetch_array($id);
function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
$this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword);
if ($argDatabasename) return $this->SelectDB($argDatabasename);
function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
$this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword);
// persistent connections can forget to rollback on crash, so we do it here.
$cnt = $this->GetOne('select @@TRANCOUNT');
while (-- $cnt >= 0) $this->Execute('ROLLBACK TRAN');
if ($argDatabasename) return $this->SelectDB($argDatabasename);
if (sizeof($sqlarr) <= 1) return $sql;
for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++ ) {
$sql2 .= '@P'. ($i- 1) . $sqlarr[$i];
return array($sql,$this->qstr($sql2),$max);
return array($sql,$stmt);
// returns concatenated string
// MSSQL requires integers to be cast as strings
// automatically cast every datatype to VARCHAR(255)
// @author David Rogers (introspectshun)
// Split single record on commas, if possible
if (sizeof($arr) > 0) return "$s";
$stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group
# note that the parameter does not have @ in front!
$db->Parameter($stmt,$id,'myid');
$db->Parameter($stmt,$group,'group',false,64);
@param $stmt Statement returned by Prepare() or PrepareSP().
@param $var PHP variable to bind to. Can set to null (for isNull support).
@param $name Name of stored procedure variable name to bind to.
@param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
@param [$maxLen] Holds an maximum length of the variable.
@param [$type] The data type of $var. Legal values depend on driver.
See mssql_bind documentation at php.net.
function Parameter(&$stmt, &$var, $name, $isOutput= false, $maxLen= 4000, $type= false)
$isNull = is_null($var); // php 4.0.4 and above...
case 'string': $type = SQLVARCHAR; break;
case 'double': $type = SQLFLT8; break;
case 'integer': $type = SQLINT4; break;
case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0
$prefix = ($isOutput) ? 'Out' : 'In';
$ztype = (empty($type)) ? 'false' : $type;
ADOConnection::outp( "{ $prefix}Parameter(\$stmt, \$php_var=' $var', \$name=' $name', \$maxLen= $maxLen, \$type= $ztype); ");
See http://phplens.com/lens/lensforum/msgs.php?id=7231
RETVAL is HARD CODED into php_mssql extension:
The return value (a long integer value) is treated like a special OUTPUT parameter,
called "RETVAL" (without the @). See the example at mssql_execute to
see how it works. - type: one of this new supported PHP constants.
SQLTEXT, SQLVARCHAR,SQLCHAR, SQLINT1,SQLINT2, SQLINT4, SQLBIT,SQLFLT8
if ($name !== 'RETVAL') $name = '@'. $name;
return mssql_bind($stmt[1], $name, $var, $type, $isOutput, $isNull, $maxLen);
Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
So all your blobs must be of type "image".
Remember to set in php.ini the following...
; Valid range 0 - 2147483647. Default = 4096.
mssql.textlimit = 0 ; zero to pass through
; Valid range 0 - 2147483647. Default = 4096.
mssql.textsize = 0 ; zero to pass through
function UpdateBlob($table,$column,$val,$where,$blobtype= 'BLOB')
$sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
$sql = "UPDATE $table SET $column=0x". bin2hex($val). " WHERE $where";
// returns query ID if successful, otherwise false
function _query($sql,$inputarr)
# bind input params with sp_executesql:
# see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm
# works only with sql server 7 and newer
foreach($inputarr as $v) {
// NVARCHAR is max 4000 chars. Let's use NTEXT
$decl .= "@P$i NVARCHAR($len)";
$params .= "@P$i=N". (strncmp($v,"'",1)== 0? $v : $this->qstr($v));
$decl .= "@P$i INT"; # Used INT just in case BIT in not supported on the user's MSSQL version. It will cast appropriately.
$params .= "@P$i=". (($v)? '1': '0'); # True == 1 in MSSQL BIT fields and acceptable for storing logical true in an int field
$decl .= "@P$i CHAR"; # Used char because a type is required even when the value is to be NULL.
$decl = $this->qstr($decl);
$rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params", $this->_connectionID);
$rez = mssql_execute($sql[1]);
// mssql uses a default date like Dec 30 2000 12:00AM
/*--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------*/
var $hasFetchAssoc; // see http://phplens.com/lens/lensforum/msgs.php?id=6083
// _mths works only in non-localised system
global $ADODB_FETCH_MODE;
$mode = $ADODB_FETCH_MODE;
//Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
// get next resultset - requires PHP 4.0.5 or later
if (!mssql_next_result($this->_queryID)) return false;
/* Use associative array to get fields array */
/* Returns: an object containing field information.
Get column information in the Recordset object. fetchField() can be used in order to obtain information about
fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
fetchField() is retrieved. */
if ($fieldOffset != - 1) {
$f = @mssql_fetch_field($this->_queryID, $fieldOffset);
else if ($fieldOffset == - 1) { /* The $fieldOffset argument is not provided thus its -1 */
$f = @mssql_fetch_field($this->_queryID);
if (empty($f)) return $false;
return @mssql_data_seek($this->_queryID, $row);
if ($this->EOF) return false;
$flds = @mssql_fetch_array($this->_queryID);
foreach($flds as $k => $v) {
if (ADODB_ASSOC_CASE == 0) {
foreach($this->fields as $k=> $v) {
} else if (ADODB_ASSOC_CASE == 1) {
foreach($this->fields as $k=> $v) {
if ($this->fields) return true;
// INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
// also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
function _fetch($ignore_fields= false)
foreach($ $this->fields as $k => $v) {
} else if (ADODB_ASSOC_CASE == 0) {
foreach($this->fields as $k=> $v) {
} else if (ADODB_ASSOC_CASE == 1) {
foreach($this->fields as $k=> $v) {
/* close() only needs to be called if you are worried about using too much memory while your script
is running. All associated result memory for the specified result identifier will automatically be freed. */
$rez = mssql_free_result($this->_queryID);
// mssql uses a default date like Dec 30 2000 12:00AM
// mssql uses a default date like Dec 30 2000 12:00AM
global $ADODB_mssql_mths,$ADODB_mssql_date_order;
if ($ADODB_mssql_date_order == 'dmy') {
if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
return parent::UnixDate($v);
if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
return parent::UnixDate($v);
$themth = $ADODB_mssql_mths[$themth];
if ($themth <= 0) return false;
return mktime(0,0,0,$themth,$theday,$rr[3]);
global $ADODB_mssql_mths,$ADODB_mssql_date_order;
if ($ADODB_mssql_date_order == 'dmy') {
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})|"
,$v, $rr)) return parent::UnixTimeStamp($v);
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})|"
,$v, $rr)) return parent::UnixTimeStamp($v);
$themth = $ADODB_mssql_mths[$themth];
if ($themth <= 0) return false;
if ($rr[4]< 12) $rr[4] += 12;
if ($rr[4]== 12) $rr[4] = 0;
return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
select object_name(constid) as constraint_name,
object_name(fkeyid) as table_name,
col_name(fkeyid, fkey) as column_name,
object_name(rkeyid) as referenced_table_name,
col_name(rkeyid, rkey) as referenced_column_name
where object_name(fkeyid) = x
order by constraint_name, table_name, referenced_table_name, keyno
from information_schema.key_column_usage
where constraint_catalog = db_name()
order by constraint_name, ordinal_position
http://www.databasejournal.com/scripts/article.php/1440551
|