3 V5.18 3 Sep 2012 (c) 2000-2012 John Lim (jlim#natsoft.com). All rights reserved.
4 Released under both BSD license and Lesser GPL library license.
5 Whenever there is any discrepancy between the two licenses,
6 the BSD license will take precedence.
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 To configure for Unix, see
13 http://phpbuilder.com/columns/alberto20000919.php3
18 // security - hide paths
19 if (!defined('ADODB_DIR')) die();
21 //----------------------------------------------------------------
22 // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
23 // and this causes tons of problems because localized versions of
24 // MSSQL will return the dates in dmy or mdy order; and also the
25 // month strings depends on what language has been configured. The
26 // following two variables allow you to control the localization
29 // MORE LOCALIZATION INFO
30 // ----------------------
31 // To configure datetime, look for and modify sqlcommn.loc,
32 // typically found in c:\mssql\install
34 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
36 // CONVERT(char(12),datecol,120)
37 //----------------------------------------------------------------
40 // has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
41 if (ADODB_PHPVER >= 0x4300) {
42 // docs say 4.2.0, but testing shows only since 4.3.0 does it work!
43 ini_set('mssql.datetimeconvert',0);
45 global $ADODB_mssql_mths; // array, months must be upper-case
48 $ADODB_mssql_date_order = 'mdy';
49 $ADODB_mssql_mths = array(
50 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
51 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
54 //---------------------------------------------------------------------------
55 // Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
56 // just after you connect to the database. Supports mdy and dmy only.
57 // Not required for PHP 4.2.0 and above.
58 function AutoDetect_MSSQL_Date_Order($conn)
60 global $ADODB_mssql_date_order;
61 $adate = $conn->GetOne('select getdate()');
66 //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
68 $ADODB_mssql_date_order = 'dmy';
70 $ADODB_mssql_date_order = 'mdy';
74 class ADODB_mssql extends ADOConnection {
75 var $databaseType = "mssql";
76 var $dataProvider = "mssql";
77 var $replaceQuote = "''"; // string to use to replace quotes
78 var $fmtDate = "'Y-m-d'";
79 var $fmtTimeStamp = "'Y-m-d H:i:s'";
80 var $hasInsertID = true;
81 var $substr = "substring";
83 var $hasAffectedRows = true;
84 var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'";
85 var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))";
86 var $metaColumnsSQL = # xtype==61 is datetime
87 "select c.name,t.name,c.length,c.isnullable, c.status,
88 (case when c.xusertype=61 then 0 else c.xprec end),
89 (case when c.xusertype=61 then 0 else c.xscale end)
90 from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
91 var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
93 var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
94 var $sysTimeStamp = 'GetDate()';
96 var $maxParameterLen = 4000;
97 var $arrayClass = 'ADORecordSet_array_mssql';
98 var $uniqueSort = true;
99 var $leftOuter = '*=';
100 var $rightOuter = '=*';
101 var $ansiOuter = true; // for mssql7 or later
102 var $poorAffectedRows = true;
103 var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
104 var $uniqueOrderBy = true;
105 var $_bindInputArray = true;
106 var $forceNewConnect = false;
108 function ADODB_mssql()
110 $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0);
113 function ServerInfo()
115 global $ADODB_FETCH_MODE;
118 if ($this->fetchMode === false) {
119 $savem = $ADODB_FETCH_MODE;
120 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
122 $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
125 $stmt = $this->PrepareSP('sp_server_info');
127 $this->Parameter($stmt,$val,'attribute_id');
128 $row = $this->GetRow($stmt);
131 $row = $this->GetRow("execute sp_server_info 2");
134 if ($this->fetchMode === false) {
135 $ADODB_FETCH_MODE = $savem;
137 $this->SetFetchMode($savem);
139 $arr['description'] = $row[2];
140 $arr['version'] = ADOConnection::_findvers($arr['description']);
144 function IfNull( $field, $ifNull )
146 return " ISNULL($field, $ifNull) "; // if MS SQL Server
152 // Returns the last IDENTITY value inserted into an IDENTITY column in
153 // the same scope. A scope is a module -- a stored procedure, trigger,
154 // function, or batch. Thus, two statements are in the same scope if
155 // they are in the same stored procedure, function, or batch.
156 if ($this->lastInsID !== false) {
157 return $this->lastInsID; // InsID from sp_executesql call
159 return $this->GetOne($this->identitySQL);
166 * Correctly quotes a string so that all strings are escaped. We prefix and append
167 * to the string single-quotes.
168 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
170 * @param s the string to quote
171 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
172 * This undoes the stupidity of magic quotes for GPC.
174 * @return quoted string to be sent back to database
176 function qstr($s,$magic_quotes=false)
178 if (!$magic_quotes) {
179 return "'".str_replace("'",$this->replaceQuote,$s)."'";
182 // undo magic quotes for " unless sybase is on
183 $sybase = ini_get('magic_quotes_sybase');
185 $s = str_replace('\\"','"',$s);
186 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
188 else {// change \' to '' for sybase/mssql
189 $s = str_replace('\\\\','\\',$s);
190 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
196 // moodle change end - see readme_moodle.txt
198 function _affectedrows()
200 return $this->GetOne('select @@rowcount');
203 var $_dropSeqSQL = "drop table %s";
205 function CreateSequence($seq='adodbseq',$start=1)
208 $this->Execute('BEGIN TRANSACTION adodbseq');
210 $this->Execute("create table $seq (id float(53))");
211 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
213 $this->Execute('ROLLBACK TRANSACTION adodbseq');
216 $this->Execute('COMMIT TRANSACTION adodbseq');
220 function GenID($seq='adodbseq',$start=1)
223 $this->Execute('BEGIN TRANSACTION adodbseq');
224 $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
226 $this->Execute("create table $seq (id float(53))");
227 $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
229 $this->Execute('ROLLBACK TRANSACTION adodbseq');
232 $this->Execute('COMMIT TRANSACTION adodbseq');
235 $num = $this->GetOne("select id from $seq");
236 $this->Execute('COMMIT TRANSACTION adodbseq');
239 // in old implementation, pre 1.90, we returned GUID...
240 //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'");
244 function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
246 if ($nrows > 0 && $offset <= 0) {
248 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql);
251 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
253 $rs = $this->Execute($sql,$inputarr);
255 $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
261 // Format date column in sql string given an input format that understands Y M D
262 function SQLDate($fmt, $col=false)
264 if (!$col) $col = $this->sysTimeStamp;
268 for ($i=0; $i < $len; $i++) {
274 $s .= "datename(yyyy,$col)";
277 $s .= "convert(char(3),$col,0)";
280 $s .= "replace(str(month($col),2),' ','0')";
284 $s .= "datename(quarter,$col)";
288 $s .= "replace(str(day($col),2),' ','0')";
291 $s .= "substring(convert(char(14),$col,0),13,2)";
295 $s .= "replace(str(datepart(hh,$col),2),' ','0')";
299 $s .= "replace(str(datepart(mi,$col),2),' ','0')";
302 $s .= "replace(str(datepart(ss,$col),2),' ','0')";
306 $s .= "substring(convert(char(19),$col,0),18,2)";
312 $ch = substr($fmt,$i,1);
314 $s .= $this->qstr($ch);
322 function BeginTrans()
324 if ($this->transOff) return true;
325 $this->transCnt += 1;
326 $ok = $this->Execute('BEGIN TRAN');
330 function CommitTrans($ok=true)
332 if ($this->transOff) return true;
333 if (!$ok) return $this->RollbackTrans();
334 if ($this->transCnt) $this->transCnt -= 1;
335 $ok = $this->Execute('COMMIT TRAN');
338 function RollbackTrans()
340 if ($this->transOff) return true;
341 if ($this->transCnt) $this->transCnt -= 1;
342 $ok = $this->Execute('ROLLBACK TRAN');
346 function SetTransactionMode( $transaction_mode )
348 $this->_transmode = $transaction_mode;
349 if (empty($transaction_mode)) {
350 $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
353 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
354 $this->Execute("SET TRANSACTION ".$transaction_mode);
361 $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
363 # some operation on both tables table1 and table2
365 $this->CommitTrans();
367 See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
369 function RowLock($tables,$where,$col='1 as adodbignore')
371 if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
372 if (!$this->transCnt) $this->BeginTrans();
373 return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
377 function MetaColumns($table, $normalize=true)
379 // $arr = ADOConnection::MetaColumns($table);
382 $this->_findschema($table,$schema);
384 $dbName = $this->database;
385 $this->SelectDB($schema);
387 global $ADODB_FETCH_MODE;
388 $save = $ADODB_FETCH_MODE;
389 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
391 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
392 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
395 $this->SelectDB($dbName);
398 if (isset($savem)) $this->SetFetchMode($savem);
399 $ADODB_FETCH_MODE = $save;
400 if (!is_object($rs)) {
407 $fld = new ADOFieldObject();
408 $fld->name = $rs->fields[0];
409 $fld->type = $rs->fields[1];
411 $fld->not_null = (!$rs->fields[3]);
412 $fld->auto_increment = ($rs->fields[4] == 128); // sys.syscolumns status field. 0x80 = 128 ref: http://msdn.microsoft.com/en-us/library/ms186816.aspx
414 if (isset($rs->fields[5]) && $rs->fields[5]) {
415 if ($rs->fields[5]>0) $fld->max_length = $rs->fields[5];
416 $fld->scale = $rs->fields[6];
417 if ($fld->scale>0) $fld->max_length += 1;
419 $fld->max_length = $rs->fields[2];
421 if ($save == ADODB_FETCH_NUM) {
424 $retarr[strtoupper($fld->name)] = $fld;
435 function MetaIndexes($table,$primary=false, $owner=false)
437 $table = $this->qstr($table);
439 $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
440 CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
441 CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
442 FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
443 INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
444 INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
445 WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
446 ORDER BY O.name, I.Name, K.keyno";
448 global $ADODB_FETCH_MODE;
449 $save = $ADODB_FETCH_MODE;
450 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
451 if ($this->fetchMode !== FALSE) {
452 $savem = $this->SetFetchMode(FALSE);
455 $rs = $this->Execute($sql);
457 $this->SetFetchMode($savem);
459 $ADODB_FETCH_MODE = $save;
461 if (!is_object($rs)) {
466 while ($row = $rs->FetchRow()) {
467 if ($primary && !$row[5]) continue;
469 $indexes[$row[0]]['unique'] = $row[6];
470 $indexes[$row[0]]['columns'][] = $row[1];
475 function MetaForeignKeys($table, $owner=false, $upper=false)
477 global $ADODB_FETCH_MODE;
479 $save = $ADODB_FETCH_MODE;
480 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
481 $table = $this->qstr(strtoupper($table));
484 "select object_name(constid) as constraint_name,
485 col_name(fkeyid, fkey) as column_name,
486 object_name(rkeyid) as referenced_table_name,
487 col_name(rkeyid, rkey) as referenced_column_name
489 where upper(object_name(fkeyid)) = $table
490 order by constraint_name, referenced_table_name, keyno";
492 $constraints = $this->GetArray($sql);
494 $ADODB_FETCH_MODE = $save;
497 foreach($constraints as $constr) {
499 $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
501 if (!$arr) return false;
505 foreach($arr as $k => $v) {
506 foreach($v as $a => $b) {
507 if ($upper) $a = strtoupper($a);
514 //From: Fernando Moreira <FMoreira@imediata.pt>
515 function MetaDatabases()
517 if(@mssql_select_db("master")) {
518 $qry=$this->metaDatabasesSQL;
519 if($rs=@mssql_query($qry,$this->_connectionID)){
521 while($tmpAr=@mssql_fetch_row($rs))
523 @mssql_select_db($this->database);
529 @mssql_select_db($this->database);
536 // "Stein-Aksel Basma" <basma@accelero.no>
537 // tested with MSSQL 2000
538 function MetaPrimaryKeys($table, $owner=false)
540 global $ADODB_FETCH_MODE;
543 $this->_findschema($table,$schema);
544 if (!$schema) $schema = $this->database;
545 if ($schema) $schema = "and k.table_catalog like '$schema%'";
547 $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
548 information_schema.table_constraints tc
549 where tc.constraint_name = k.constraint_name and tc.constraint_type =
550 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
552 $savem = $ADODB_FETCH_MODE;
553 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
554 $a = $this->GetCol($sql);
555 $ADODB_FETCH_MODE = $savem;
557 if ($a && sizeof($a)>0) return $a;
563 function MetaTables($ttype=false,$showSchema=false,$mask=false)
566 $save = $this->metaTablesSQL;
567 $mask = $this->qstr(($mask));
568 $this->metaTablesSQL .= " AND name like $mask";
570 $ret = ADOConnection::MetaTables($ttype,$showSchema);
573 $this->metaTablesSQL = $save;
578 function SelectDB($dbName)
580 $this->database = $dbName;
581 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
582 if ($this->_connectionID) {
583 return @mssql_select_db($dbName);
590 if (empty($this->_errorMsg)){
591 $this->_errorMsg = mssql_get_last_message();
593 return $this->_errorMsg;
598 if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
599 if (empty($this->_errorMsg)) {
600 $this->_errorMsg = mssql_get_last_message();
602 $id = @mssql_query("select @@ERROR",$this->_connectionID);
603 if (!$id) return false;
604 $arr = mssql_fetch_array($id);
605 @mssql_free_result($id);
606 if (is_array($arr)) return $arr[0];
610 // returns true or false, newconnect supported since php 5.1.0.
611 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false)
613 if (!function_exists('mssql_pconnect')) return null;
614 $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect);
615 if ($this->_connectionID === false) return false;
616 if ($argDatabasename) return $this->SelectDB($argDatabasename);
621 // returns true or false
622 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
624 if (!function_exists('mssql_pconnect')) return null;
625 $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword);
626 if ($this->_connectionID === false) return false;
628 // persistent connections can forget to rollback on crash, so we do it here.
629 if ($this->autoRollback) {
630 $cnt = $this->GetOne('select @@TRANCOUNT');
631 while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN');
633 if ($argDatabasename) return $this->SelectDB($argDatabasename);
637 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
639 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
642 function Prepare($sql)
644 $sqlarr = explode('?',$sql);
645 if (sizeof($sqlarr) <= 1) return $sql;
647 for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) {
648 $sql2 .= '@P'.($i-1) . $sqlarr[$i];
650 return array($sql,$this->qstr($sql2),$max,$sql2);
653 function PrepareSP($sql,$param=true)
655 if (!$this->_has_mssql_init) {
656 ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0");
659 $stmt = mssql_init($sql,$this->_connectionID);
660 if (!$stmt) return $sql;
661 return array($sql,$stmt);
664 // returns concatenated string
665 // MSSQL requires integers to be cast as strings
666 // automatically cast every datatype to VARCHAR(255)
667 // @author David Rogers (introspectshun)
671 $arr = func_get_args();
673 // Split single record on commas, if possible
674 if (sizeof($arr) == 1) {
675 foreach ($arr as $arg) {
676 $args = explode(',', $arg);
681 array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
682 $s = implode('+',$arr);
683 if (sizeof($arr) > 0) return "$s";
690 $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group
692 # note that the parameter does not have @ in front!
693 $db->Parameter($stmt,$id,'myid');
694 $db->Parameter($stmt,$group,'group',false,64);
697 @param $stmt Statement returned by Prepare() or PrepareSP().
698 @param $var PHP variable to bind to. Can set to null (for isNull support).
699 @param $name Name of stored procedure variable name to bind to.
700 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
701 @param [$maxLen] Holds an maximum length of the variable.
702 @param [$type] The data type of $var. Legal values depend on driver.
704 See mssql_bind documentation at php.net.
706 function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false)
708 if (!$this->_has_mssql_init) {
709 ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0");
713 $isNull = is_null($var); // php 4.0.4 and above...
716 switch(gettype($var)) {
718 case 'string': $type = SQLVARCHAR; break;
719 case 'double': $type = SQLFLT8; break;
720 case 'integer': $type = SQLINT4; break;
721 case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0
725 $prefix = ($isOutput) ? 'Out' : 'In';
726 $ztype = (empty($type)) ? 'false' : $type;
727 ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
730 See http://phplens.com/lens/lensforum/msgs.php?id=7231
732 RETVAL is HARD CODED into php_mssql extension:
733 The return value (a long integer value) is treated like a special OUTPUT parameter,
734 called "RETVAL" (without the @). See the example at mssql_execute to
735 see how it works. - type: one of this new supported PHP constants.
736 SQLTEXT, SQLVARCHAR,SQLCHAR, SQLINT1,SQLINT2, SQLINT4, SQLBIT,SQLFLT8
738 if ($name !== 'RETVAL') $name = '@'.$name;
739 return mssql_bind($stmt[1], $name, $var, $type, $isOutput, $isNull, $maxLen);
743 Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
744 So all your blobs must be of type "image".
746 Remember to set in php.ini the following...
748 ; Valid range 0 - 2147483647. Default = 4096.
749 mssql.textlimit = 0 ; zero to pass through
751 ; Valid range 0 - 2147483647. Default = 4096.
752 mssql.textsize = 0 ; zero to pass through
754 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
757 if (strtoupper($blobtype) == 'CLOB') {
758 $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
759 return $this->Execute($sql) != false;
761 $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
762 return $this->Execute($sql) != false;
765 // returns query ID if successful, otherwise false
766 function _query($sql,$inputarr=false)
768 $this->_errorMsg = false;
769 if (is_array($inputarr)) {
771 # bind input params with sp_executesql:
772 # see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm
773 # works only with sql server 7 and newer
774 $getIdentity = false;
775 if (!is_array($sql) && preg_match('/^\\s*insert/i', $sql)) {
777 $sql .= (preg_match('/;\\s*$/i', $sql) ? ' ' : '; ') . $this->identitySQL;
779 if (!is_array($sql)) $sql = $this->Prepare($sql);
783 foreach($inputarr as $v) {
790 if ($len == 0) $len = 1;
793 // NVARCHAR is max 4000 chars. Let's use NTEXT
794 $decl .= "@P$i NTEXT";
796 $decl .= "@P$i NVARCHAR($len)";
799 $params .= "@P$i=N". (strncmp($v,"'",1)==0? $v : $this->qstr($v));
800 } else if (is_integer($v)) {
802 $params .= "@P$i=".$v;
803 } else if (is_float($v)) {
804 $decl .= "@P$i FLOAT";
805 $params .= "@P$i=".$v;
806 } else if (is_bool($v)) {
807 $decl .= "@P$i INT"; # Used INT just in case BIT in not supported on the user's MSSQL version. It will cast appropriately.
808 $params .= "@P$i=".(($v)?'1':'0'); # True == 1 in MSSQL BIT fields and acceptable for storing logical true in an int field
810 $decl .= "@P$i CHAR"; # Used char because a type is required even when the value is to be NULL.
811 $params .= "@P$i=NULL";
815 $decl = $this->qstr($decl);
816 if ($this->debug) ADOConnection::outp("<font size=-1>sp_executesql N{$sql[1]},N$decl,$params</font>");
817 $rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params", $this->_connectionID);
819 $arr = @mssql_fetch_row($rez);
820 $this->lastInsID = isset($arr[0]) ? $arr[0] : false;
821 @mssql_data_seek($rez, 0);
824 } else if (is_array($sql)) {
826 $rez = mssql_execute($sql[1]);
827 $this->lastInsID = false;
830 $rez = mssql_query($sql,$this->_connectionID);
831 $this->lastInsID = false;
836 // returns true or false
839 if ($this->transCnt) $this->RollbackTrans();
840 $rez = @mssql_close($this->_connectionID);
841 $this->_connectionID = false;
845 // mssql uses a default date like Dec 30 2000 12:00AM
846 static function UnixDate($v)
848 return ADORecordSet_array_mssql::UnixDate($v);
851 static function UnixTimeStamp($v)
853 return ADORecordSet_array_mssql::UnixTimeStamp($v);
857 /*--------------------------------------------------------------------------------------
858 Class Name: Recordset
859 --------------------------------------------------------------------------------------*/
861 class ADORecordset_mssql extends ADORecordSet {
863 var $databaseType = "mssql";
865 var $hasFetchAssoc; // see http://phplens.com/lens/lensforum/msgs.php?id=6083
866 // _mths works only in non-localised system
868 function ADORecordset_mssql($id,$mode=false)
871 $this->hasFetchAssoc = function_exists('mssql_fetch_assoc');
873 if ($mode === false) {
874 global $ADODB_FETCH_MODE;
875 $mode = $ADODB_FETCH_MODE;
878 $this->fetchMode = $mode;
879 return $this->ADORecordSet($id,$mode);
885 GLOBAL $ADODB_COUNTRECS;
886 $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1;
887 $this->_numOfFields = @mssql_num_fields($this->_queryID);
891 //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
892 // get next resultset - requires PHP 4.0.5 or later
893 function NextRecordSet()
895 if (!mssql_next_result($this->_queryID)) return false;
896 $this->_inited = false;
898 $this->_currentRow = -1;
903 /* Use associative array to get fields array */
904 function Fields($colname)
906 if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
908 $this->bind = array();
909 for ($i=0; $i < $this->_numOfFields; $i++) {
910 $o = $this->FetchField($i);
911 $this->bind[strtoupper($o->name)] = $i;
915 return $this->fields[$this->bind[strtoupper($colname)]];
918 /* Returns: an object containing field information.
919 Get column information in the Recordset object. fetchField() can be used in order to obtain information about
920 fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
921 fetchField() is retrieved. */
923 function FetchField($fieldOffset = -1)
925 if ($fieldOffset != -1) {
926 $f = @mssql_fetch_field($this->_queryID, $fieldOffset);
928 else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */
929 $f = @mssql_fetch_field($this->_queryID);
932 if (empty($f)) return $false;
938 return @mssql_data_seek($this->_queryID, $row);
944 if ($this->EOF) return false;
946 $this->_currentRow++;
948 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
949 if ($this->fetchMode & ADODB_FETCH_NUM) {
950 //ADODB_FETCH_BOTH mode
951 $this->fields = @mssql_fetch_array($this->_queryID);
954 if ($this->hasFetchAssoc) {// only for PHP 4.2.0 or later
955 $this->fields = @mssql_fetch_assoc($this->_queryID);
957 $flds = @mssql_fetch_array($this->_queryID);
958 if (is_array($flds)) {
960 foreach($flds as $k => $v) {
961 if (is_numeric($k)) continue;
964 $this->fields = $fassoc;
966 $this->fields = false;
970 if (is_array($this->fields)) {
971 if (ADODB_ASSOC_CASE == 0) {
972 foreach($this->fields as $k=>$v) {
973 $kn = strtolower($k);
975 unset($this->fields[$k]);
976 $this->fields[$kn] = $v;
979 } else if (ADODB_ASSOC_CASE == 1) {
980 foreach($this->fields as $k=>$v) {
981 $kn = strtoupper($k);
983 unset($this->fields[$k]);
984 $this->fields[$kn] = $v;
990 $this->fields = @mssql_fetch_row($this->_queryID);
992 if ($this->fields) return true;
999 // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
1000 // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
1001 function _fetch($ignore_fields=false)
1003 if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1004 if ($this->fetchMode & ADODB_FETCH_NUM) {
1005 //ADODB_FETCH_BOTH mode
1006 $this->fields = @mssql_fetch_array($this->_queryID);
1008 if ($this->hasFetchAssoc) // only for PHP 4.2.0 or later
1009 $this->fields = @mssql_fetch_assoc($this->_queryID);
1011 $this->fields = @mssql_fetch_array($this->_queryID);
1012 if (@is_array($$this->fields)) {
1014 foreach($$this->fields as $k => $v) {
1015 if (is_integer($k)) continue;
1018 $this->fields = $fassoc;
1023 if (!$this->fields) {
1024 } else if (ADODB_ASSOC_CASE == 0) {
1025 foreach($this->fields as $k=>$v) {
1026 $kn = strtolower($k);
1028 unset($this->fields[$k]);
1029 $this->fields[$kn] = $v;
1032 } else if (ADODB_ASSOC_CASE == 1) {
1033 foreach($this->fields as $k=>$v) {
1034 $kn = strtoupper($k);
1036 unset($this->fields[$k]);
1037 $this->fields[$kn] = $v;
1042 $this->fields = @mssql_fetch_row($this->_queryID);
1044 return $this->fields;
1047 /* close() only needs to be called if you are worried about using too much memory while your script
1048 is running. All associated result memory for the specified result identifier will automatically be freed. */
1052 $rez = mssql_free_result($this->_queryID);
1053 $this->_queryID = false;
1056 // mssql uses a default date like Dec 30 2000 12:00AM
1057 static function UnixDate($v)
1059 return ADORecordSet_array_mssql::UnixDate($v);
1062 static function UnixTimeStamp($v)
1064 return ADORecordSet_array_mssql::UnixTimeStamp($v);
1070 class ADORecordSet_array_mssql extends ADORecordSet_array {
1071 function ADORecordSet_array_mssql($id=-1,$mode=false)
1073 $this->ADORecordSet_array($id,$mode);
1076 // mssql uses a default date like Dec 30 2000 12:00AM
1077 static function UnixDate($v)
1080 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
1082 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1084 //Dec 30 2000 12:00AM
1085 if ($ADODB_mssql_date_order == 'dmy') {
1086 if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1087 return parent::UnixDate($v);
1089 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1092 $themth = substr(strtoupper($rr[2]),0,3);
1094 if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1095 return parent::UnixDate($v);
1097 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1100 $themth = substr(strtoupper($rr[1]),0,3);
1102 $themth = $ADODB_mssql_mths[$themth];
1103 if ($themth <= 0) return false;
1105 return mktime(0,0,0,$themth,$theday,$rr[3]);
1108 static function UnixTimeStamp($v)
1111 if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
1113 global $ADODB_mssql_mths,$ADODB_mssql_date_order;
1115 //Dec 30 2000 12:00AM
1116 if ($ADODB_mssql_date_order == 'dmy') {
1117 if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
1118 ,$v, $rr)) return parent::UnixTimeStamp($v);
1119 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1122 $themth = substr(strtoupper($rr[2]),0,3);
1124 if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
1125 ,$v, $rr)) return parent::UnixTimeStamp($v);
1126 if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1129 $themth = substr(strtoupper($rr[1]),0,3);
1132 $themth = $ADODB_mssql_mths[$themth];
1133 if ($themth <= 0) return false;
1135 switch (strtoupper($rr[6])) {
1137 if ($rr[4]<12) $rr[4] += 12;
1140 if ($rr[4]==12) $rr[4] = 0;
1146 return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
1153 select object_name(constid) as constraint_name,
1154 object_name(fkeyid) as table_name,
1155 col_name(fkeyid, fkey) as column_name,
1156 object_name(rkeyid) as referenced_table_name,
1157 col_name(rkeyid, rkey) as referenced_column_name
1159 where object_name(fkeyid) = x
1160 order by constraint_name, table_name, referenced_table_name, keyno
1163 select constraint_name,
1166 from information_schema.key_column_usage
1167 where constraint_catalog = db_name()
1169 order by constraint_name, ordinal_position
1171 http://www.databasejournal.com/scripts/article.php/1440551