TYPO3 CMS  TYPO3_7-6
adodb-mssqlnative.inc.php
Go to the documentation of this file.
1 <?php
2 /*
3 @version v5.20.3 01-Jan-2016
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.
10 
11  Latest version is available at http://adodb.sourceforge.net
12 
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
17 
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
20 
21 */
22 
23 // security - hide paths
24 if (!defined('ADODB_DIR')) die();
25 
26 if (!function_exists('sqlsrv_configure')) {
27  die("mssqlnative extension not installed");
28 }
29 
30 if (!function_exists('sqlsrv_set_error_handling')) {
31  function sqlsrv_set_error_handling($constant) {
32  sqlsrv_configure("WarningsReturnAsErrors", $constant);
33  }
34 }
35 if (!function_exists('sqlsrv_log_set_severity')) {
36  function sqlsrv_log_set_severity($constant) {
37  sqlsrv_configure("LogSeverity", $constant);
38  }
39 }
40 if (!function_exists('sqlsrv_log_set_subsystems')) {
41  function sqlsrv_log_set_subsystems($constant) {
42  sqlsrv_configure("LogSubsystems", $constant);
43  }
44 }
45 
46 
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
53 // settings - Ugh.
54 //
55 // MORE LOCALIZATION INFO
56 // ----------------------
57 // To configure datetime, look for and modify sqlcommn.loc,
58 // typically found in c:\mssql\install
59 // Also read :
60 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
61 // Alternatively use:
62 // CONVERT(char(12),datecol,120)
63 //
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 //----------------------------------------------------------------
69 
70 
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);
75 } else {
76  global $ADODB_mssql_mths; // array, months must be upper-case
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);
81 }
82 
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";
91  var $length = 'len';
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'))";
97  "select c.name,
98  t.name as type,
99  c.length,
100  c.xprec as precision,
101  c.xscale as scale,
102  c.isnullable as nullable,
103  c.cdefault as default_value,
104  c.xtype,
105  t.length as type_length,
106  sc.is_identity
107  from syscolumns c
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
112  where o.name='%s'";
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 = '';
130 
131  function __construct()
132  {
133  if ($this->debug) {
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);
139  } else {
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);
144  }
145  }
146  function ServerVersion() {
147  $data = $this->ServerInfo();
148  if (preg_match('/^09/',$data['version'])){
149  /*
150  * SQL Server 2005
151  */
152  $this->mssql_version = 9;
153  } elseif (preg_match('/^10/',$data['version'])){
154  /*
155  * SQL Server 2008
156  */
157  $this->mssql_version = 10;
158  } elseif (preg_match('/^11/',$data['version'])){
159  /*
160  * SQL Server 2012
161  */
162  $this->mssql_version = 11;
163  } else
164  die("SQL SERVER VERSION {$data['version']} NOT SUPPORTED IN mssqlnative DRIVER");
165  }
166 
167  function ServerInfo() {
168  global $ADODB_FETCH_MODE;
169  static $arr = false;
170  if (is_array($arr))
171  return $arr;
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;
177  } else
178  $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
179 
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']);
184  return $arr;
185  }
186 
187  function IfNull( $field, $ifNull )
188  {
189  return " ISNULL($field, $ifNull) "; // if MS SQL Server
190  }
191 
192  function _insertid()
193  {
194  // SCOPE_IDENTITY()
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;
200  }
201 
202  function _affectedrows()
203  {
204  if ($this->_queryID)
205  return sqlsrv_rows_affected($this->_queryID);
206  }
207 
208  function GenID($seq='adodbseq',$start=1) {
209  if (!$this->mssql_version)
210  $this->ServerVersion();
211  switch($this->mssql_version){
212  case 9:
213  case 10:
214  return $this->GenID2008($seq, $start);
215  break;
216  case 11:
217  return $this->GenID2012($seq, $start);
218  break;
219  }
220  }
221 
222  function CreateSequence($seq='adodbseq',$start=1)
223  {
224  if (!$this->mssql_vesion)
225  $this->ServerVersion();
226 
227  switch($this->mssql_version){
228  case 9:
229  case 10:
230  return $this->CreateSequence2008($seq, $start);
231  break;
232  case 11:
233  return $this->CreateSequence2012($seq, $start);
234  break;
235  }
236 
237  }
238 
242  function CreateSequence2008($seq='adodbseq',$start=1)
243  {
244  if($this->debug) ADOConnection::outp("<hr>CreateSequence($seq,$start)");
245  sqlsrv_begin_transaction($this->_connectionID);
246  $start -= 1;
247  $this->Execute("create table $seq (id int)");//was float(53)
248  $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
249  if (!$ok) {
250  if($this->debug) ADOConnection::outp("<hr>Error: ROLLBACK");
251  sqlsrv_rollback($this->_connectionID);
252  return false;
253  }
254  sqlsrv_commit($this->_connectionID);
255  return true;
256  }
257 
261  function CreateSequence2012($seq='adodb',$start=1){
262  if (!$this->sequences){
263  $sql = "SELECT name FROM sys.sequences";
264  $this->sequences = $this->GetCol($sql);
265  }
266  $ok = $this->Execute("CREATE SEQUENCE $seq START WITH $start INCREMENT BY 1");
267  if (!$ok)
268  die("CANNOT CREATE SEQUENCE" . print_r(sqlsrv_errors(),true));
269  $this->sequences[] = $seq;
270  }
271 
275  function GenID2008($seq='adodbseq',$start=1)
276  {
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");
280  if (!$ok) {
281  $start -= 1;
282  $this->Execute("create table $seq (id int)");//was float(53)
283  $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
284  if (!$ok) {
285  if($this->debug) ADOConnection::outp("<hr>Error: ROLLBACK");
286  sqlsrv_rollback($this->_connectionID);
287  return false;
288  }
289  }
290  $num = $this->GetOne("select id from $seq");
291  sqlsrv_commit($this->_connectionID);
292  return $num;
293  }
300  function GenID2012($seq='adodbseq',$start=1)
301  {
302 
303  /*
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
309  */
310  if (!$this->sequences){
311  $sql = "SELECT name FROM sys.sequences";
312  $this->sequences = $this->GetCol($sql);
313  }
314  if (!is_array($this->sequences)
315  || is_array($this->sequences) && !in_array($seq,$this->sequences)){
316  $this->CreateSequence2012($seq, $start);
317 
318  }
319  $num = $this->GetOne("SELECT NEXT VALUE FOR $seq");
320  return $num;
321  }
322 
323  // Format date column in sql string given an input format that understands Y M D
324  function SQLDate($fmt, $col=false)
325  {
326  if (!$col) $col = $this->sysTimeStamp;
327  $s = '';
328 
329  $len = strlen($fmt);
330  for ($i=0; $i < $len; $i++) {
331  if ($s) $s .= '+';
332  $ch = $fmt[$i];
333  switch($ch) {
334  case 'Y':
335  case 'y':
336  $s .= "datename(yyyy,$col)";
337  break;
338  case 'M':
339  $s .= "convert(char(3),$col,0)";
340  break;
341  case 'm':
342  $s .= "replace(str(month($col),2),' ','0')";
343  break;
344  case 'Q':
345  case 'q':
346  $s .= "datename(quarter,$col)";
347  break;
348  case 'D':
349  case 'd':
350  $s .= "replace(str(day($col),2),' ','0')";
351  break;
352  case 'h':
353  $s .= "substring(convert(char(14),$col,0),13,2)";
354  break;
355 
356  case 'H':
357  $s .= "replace(str(datepart(hh,$col),2),' ','0')";
358  break;
359 
360  case 'i':
361  $s .= "replace(str(datepart(mi,$col),2),' ','0')";
362  break;
363  case 's':
364  $s .= "replace(str(datepart(ss,$col),2),' ','0')";
365  break;
366  case 'a':
367  case 'A':
368  $s .= "substring(convert(char(19),$col,0),18,2)";
369  break;
370 
371  default:
372  if ($ch == '\\') {
373  $i++;
374  $ch = substr($fmt,$i,1);
375  }
376  $s .= $this->qstr($ch);
377  break;
378  }
379  }
380  return $s;
381  }
382 
383 
384  function BeginTrans()
385  {
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);
390  return true;
391  }
392 
393  function CommitTrans($ok=true)
394  {
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);
400  return true;
401  }
402  function RollbackTrans()
403  {
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);
408  return true;
409  }
410 
411  function SetTransactionMode( $transaction_mode )
412  {
413  $this->_transmode = $transaction_mode;
414  if (empty($transaction_mode)) {
415  $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
416  return;
417  }
418  if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
419  $this->Execute("SET TRANSACTION ".$transaction_mode);
420  }
421 
422  /*
423  Usage:
424 
425  $this->BeginTrans();
426  $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
427 
428  # some operation on both tables table1 and table2
429 
430  $this->CommitTrans();
431 
432  See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
433  */
434  function RowLock($tables,$where,$col='1 as adodbignore')
435  {
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");
439  }
440 
441  function SelectDB($dbName)
442  {
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);
447  if($rs) {
448  return true;
449  } else return false;
450  }
451  else return false;
452  }
453 
454  function ErrorMsg()
455  {
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";
462  }
463  } else {
464  $this->_errorMsg = '';
465  }
466  return $this->_errorMsg;
467  }
468 
469  function ErrorNo()
470  {
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'];
474  else return -1;
475  }
476 
477  // returns true or false
478  function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
479  {
480  if (!function_exists('sqlsrv_connect')) return null;
481  // Port is always given as part of $argHostname but
482  // 1) should only be set if using an IP/hostname and not a named instance
483  // 2) must use a comma instead of a colon
484  list ($hostname, $port) = explode(':', $argHostname, 2);
485  if (strpos($hostname, '\\') === false) {
486  $argHostname = $hostname . ',' . $port;
487  } else {
488  $argHostname = $hostname;
489  }
490  $connectionInfo = $this->connectionInfo;
491  $connectionInfo['Database'] = $argDatabasename;
492  if (!empty($argUsername)) {
493  $connectionInfo['UID'] = $argUsername;
494  }
495  if (!empty($argPassword)) {
496  $connectionInfo['PWD'] = $argPassword;
497  }
498  if (!empty($this->charSet)) {
499  $connectionInfo['CharacterSet'] = $this->charSet;
500  }
501  foreach ($this->connectionParameters as $parameter=>$value)
502  $connectionInfo[$parameter] = $value;
503 
504  if ($this->debug) ADOConnection::outp("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
505  //if ($this->debug) ADOConnection::outp("<hr>_connectionID before: ".serialize($this->_connectionID));
506  if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) {
507  if ($this->debug) ADOConnection::outp( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
508  return false;
509  }
510  //if ($this->debug) ADOConnection::outp(" _connectionID after: ".serialize($this->_connectionID));
511  //if ($this->debug) ADOConnection::outp("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
512  return true;
513  }
514 
515  // returns true or false
516  function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
517  {
518  //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
519  return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
520  }
521 
522  function Prepare($sql)
523  {
524  return $sql; // prepare does not work properly with bind parameters as bind parameters are managed by sqlsrv_prepare!
525 
526  $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
527  if (!$stmt) return $sql;
528  return array($sql,$stmt);
529  }
530 
531  // returns concatenated string
532  // MSSQL requires integers to be cast as strings
533  // automatically cast every datatype to VARCHAR(255)
534  // @author David Rogers (introspectshun)
535  function Concat()
536  {
537  $s = "";
538  $arr = func_get_args();
539 
540  // Split single record on commas, if possible
541  if (sizeof($arr) == 1) {
542  foreach ($arr as $arg) {
543  $args = explode(',', $arg);
544  }
545  $arr = $args;
546  }
547 
548  array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
549  $s = implode('+',$arr);
550  if (sizeof($arr) > 0) return "$s";
551 
552  return '';
553  }
554 
555  /*
556  Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
557  So all your blobs must be of type "image".
558 
559  Remember to set in php.ini the following...
560 
561  ; Valid range 0 - 2147483647. Default = 4096.
562  mssql.textlimit = 0 ; zero to pass through
563 
564  ; Valid range 0 - 2147483647. Default = 4096.
565  mssql.textsize = 0 ; zero to pass through
566  */
567  function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
568  {
569 
570  if (strtoupper($blobtype) == 'CLOB') {
571  $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
572  return $this->Execute($sql) != false;
573  }
574  $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
575  return $this->Execute($sql) != false;
576  }
577 
578  // returns query ID if successful, otherwise false
579  function _query($sql,$inputarr=false)
580  {
581  $this->_errorMsg = false;
582 
583  if (is_array($sql)) $sql = $sql[1];
584 
585  $insert = false;
586  // handle native driver flaw for retrieving the last insert ID
587  if(preg_match('/^\W*insert\s(?:(?:(?:\'\')*\'[^\']+\'(?:\'\')*)|[^;\'])*;?$/i', $sql)) {
588  $insert = true;
589  $sql .= '; '.$this->identitySQL; // select scope_identity()
590  }
591  if($inputarr) {
592  $rez = sqlsrv_query($this->_connectionID, $sql, $inputarr);
593  } else {
594  $rez = sqlsrv_query($this->_connectionID,$sql);
595  }
596 
597  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));
598 
599  if(!$rez) {
600  $rez = false;
601  } else if ($insert) {
602  // retrieve the last insert ID (where applicable)
603  while ( sqlsrv_next_result($rez) ) {
604  sqlsrv_fetch($rez);
605  $this->lastInsertID = sqlsrv_get_field($rez, 0);
606  }
607  }
608  return $rez;
609  }
610 
611  // returns true or false
612  function _close()
613  {
614  if ($this->transCnt) $this->RollbackTrans();
615  $rez = @sqlsrv_close($this->_connectionID);
616  $this->_connectionID = false;
617  return $rez;
618  }
619 
620  // mssql uses a default date like Dec 30 2000 12:00AM
621  static function UnixDate($v)
622  {
624  }
625 
626  static function UnixTimeStamp($v)
627  {
629  }
630 
631  function MetaIndexes($table,$primary=false, $owner = false)
632  {
633  $table = $this->qstr($table);
634 
635  $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
636  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,
637  CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
638  FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
639  INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
640  INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
641  WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
642  ORDER BY O.name, I.Name, K.keyno";
643 
644  global $ADODB_FETCH_MODE;
645  $save = $ADODB_FETCH_MODE;
646  $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
647  if ($this->fetchMode !== FALSE) {
648  $savem = $this->SetFetchMode(FALSE);
649  }
650 
651  $rs = $this->Execute($sql);
652  if (isset($savem)) {
653  $this->SetFetchMode($savem);
654  }
655  $ADODB_FETCH_MODE = $save;
656 
657  if (!is_object($rs)) {
658  return FALSE;
659  }
660 
661  $indexes = array();
662  while ($row = $rs->FetchRow()) {
663  if (!$primary && $row[5]) continue;
664 
665  $indexes[$row[0]]['unique'] = $row[6];
666  $indexes[$row[0]]['columns'][] = $row[1];
667  }
668  return $indexes;
669  }
670 
671  function MetaForeignKeys($table, $owner=false, $upper=false)
672  {
673  global $ADODB_FETCH_MODE;
674 
675  $save = $ADODB_FETCH_MODE;
676  $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
677  $table = $this->qstr(strtoupper($table));
678 
679  $sql =
680  "select object_name(constid) as constraint_name,
681  col_name(fkeyid, fkey) as column_name,
682  object_name(rkeyid) as referenced_table_name,
683  col_name(rkeyid, rkey) as referenced_column_name
684  from sysforeignkeys
685  where upper(object_name(fkeyid)) = $table
686  order by constraint_name, referenced_table_name, keyno";
687 
688  $constraints =& $this->GetArray($sql);
689 
690  $ADODB_FETCH_MODE = $save;
691 
692  $arr = false;
693  foreach($constraints as $constr) {
694  //print_r($constr);
695  $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
696  }
697  if (!$arr) return false;
698 
699  $arr2 = false;
700 
701  foreach($arr as $k => $v) {
702  foreach($v as $a => $b) {
703  if ($upper) $a = strtoupper($a);
704  $arr2[$a] = $b;
705  }
706  }
707  return $arr2;
708  }
709 
710  //From: Fernando Moreira <FMoreira@imediata.pt>
711  function MetaDatabases()
712  {
713  $this->SelectDB("master");
714  $rs =& $this->Execute($this->metaDatabasesSQL);
715  $rows = $rs->GetRows();
716  $ret = array();
717  for($i=0;$i<count($rows);$i++) {
718  $ret[] = $rows[$i][0];
719  }
720  $this->SelectDB($this->database);
721  if($ret)
722  return $ret;
723  else
724  return false;
725  }
726 
727  // "Stein-Aksel Basma" <basma@accelero.no>
728  // tested with MSSQL 2000
729  function MetaPrimaryKeys($table, $owner=false)
730  {
731  global $ADODB_FETCH_MODE;
732 
733  $schema = '';
734  $this->_findschema($table,$schema);
735  if (!$schema) $schema = $this->database;
736  if ($schema) $schema = "and k.table_catalog like '$schema%'";
737 
738  $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
739  information_schema.table_constraints tc
740  where tc.constraint_name = k.constraint_name and tc.constraint_type =
741  'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
742 
743  $savem = $ADODB_FETCH_MODE;
744  $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
745  $a = $this->GetCol($sql);
746  $ADODB_FETCH_MODE = $savem;
747 
748  if ($a && sizeof($a)>0) return $a;
749  $false = false;
750  return $false;
751  }
752 
753 
754  function MetaTables($ttype=false,$showSchema=false,$mask=false)
755  {
756  if ($mask) {
757  $save = $this->metaTablesSQL;
758  $mask = $this->qstr(($mask));
759  $this->metaTablesSQL .= " AND name like $mask";
760  }
761  $ret = ADOConnection::MetaTables($ttype,$showSchema);
762 
763  if ($mask) {
764  $this->metaTablesSQL = $save;
765  }
766  return $ret;
767  }
768  function MetaColumns($table, $upper=true, $schema=false){
769 
770  # start adg
771  static $cached_columns = array();
772  if ($this->cachedSchemaFlush)
773  $cached_columns = array();
774 
775  if (array_key_exists($table,$cached_columns)){
776  return $cached_columns[$table];
777  }
778  # end adg
779 
780  if (!$this->mssql_version)
781  $this->ServerVersion();
782 
783  $this->_findschema($table,$schema);
784  if ($schema) {
785  $dbName = $this->database;
786  $this->SelectDB($schema);
787  }
788  global $ADODB_FETCH_MODE;
789  $save = $ADODB_FETCH_MODE;
790  $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
791 
792  if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
793  $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
794 
795  if ($schema) {
796  $this->SelectDB($dbName);
797  }
798 
799  if (isset($savem)) $this->SetFetchMode($savem);
800  $ADODB_FETCH_MODE = $save;
801  if (!is_object($rs)) {
802  $false = false;
803  return $false;
804  }
805 
806  $retarr = array();
807  while (!$rs->EOF){
808 
809  $fld = new ADOFieldObject();
810  if (array_key_exists(0,$rs->fields)) {
811  $fld->name = $rs->fields[0];
812  $fld->type = $rs->fields[1];
813  $fld->max_length = $rs->fields[2];
814  $fld->precision = $rs->fields[3];
815  $fld->scale = $rs->fields[4];
816  $fld->not_null =!$rs->fields[5];
817  $fld->has_default = $rs->fields[6];
818  $fld->xtype = $rs->fields[7];
819  $fld->type_length = $rs->fields[8];
820  $fld->auto_increment= $rs->fields[9];
821  } else {
822  $fld->name = $rs->fields['name'];
823  $fld->type = $rs->fields['type'];
824  $fld->max_length = $rs->fields['length'];
825  $fld->precision = $rs->fields['precision'];
826  $fld->scale = $rs->fields['scale'];
827  $fld->not_null =!$rs->fields['nullable'];
828  $fld->has_default = $rs->fields['default_value'];
829  $fld->xtype = $rs->fields['xtype'];
830  $fld->type_length = $rs->fields['type_length'];
831  $fld->auto_increment= $rs->fields['is_identity'];
832  }
833 
834  if ($save == ADODB_FETCH_NUM)
835  $retarr[] = $fld;
836  else
837  $retarr[strtoupper($fld->name)] = $fld;
838 
839  $rs->MoveNext();
840 
841  }
842  $rs->Close();
843  # start adg
844  $cached_columns[$table] = $retarr;
845  # end adg
846  return $retarr;
847  }
848 
849 }
850 
851 /*--------------------------------------------------------------------------------------
852  Class Name: Recordset
853 --------------------------------------------------------------------------------------*/
854 
855 class ADORecordset_mssqlnative extends ADORecordSet {
856 
857  var $databaseType = "mssqlnative";
858  var $canSeek = false;
859  var $fieldOffset = 0;
860  // _mths works only in non-localised system
861 
862  function __construct($id,$mode=false)
863  {
864  if ($mode === false) {
865  global $ADODB_FETCH_MODE;
866  $mode = $ADODB_FETCH_MODE;
867 
868  }
869  $this->fetchMode = $mode;
870  return parent::__construct($id,$mode);
871  }
872 
873 
874  function _initrs()
875  {
876  global $ADODB_COUNTRECS;
877  # KMN # if ($this->connection->debug) ADOConnection::outp("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
878  /*$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."
879  ADOConnection::outp("rowsaff: ".serialize($retRowsAff));
880  $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
881  $this->_numOfRows = -1;//not supported
882  $fieldmeta = sqlsrv_field_metadata($this->_queryID);
883  $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
884  # KMN # if ($this->connection->debug) ADOConnection::outp("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
885  /*
886  * Copy the oracle method and cache the metadata at init time
887  */
888  if ($this->_numOfFields>0) {
889  $this->_fieldobjs = array();
890  $max = $this->_numOfFields;
891  for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
892  }
893 
894  }
895 
896 
897  //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
898  // get next resultset - requires PHP 4.0.5 or later
899  function NextRecordSet()
900  {
901  if (!sqlsrv_next_result($this->_queryID)) return false;
902  $this->_inited = false;
903  $this->bind = false;
904  $this->_currentRow = -1;
905  $this->Init();
906  return true;
907  }
908 
909  /* Use associative array to get fields array */
910  function Fields($colname)
911  {
912  if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
913  if (!$this->bind) {
914  $this->bind = array();
915  for ($i=0; $i < $this->_numOfFields; $i++) {
916  $o = $this->FetchField($i);
917  $this->bind[strtoupper($o->name)] = $i;
918  }
919  }
920 
921  return $this->fields[$this->bind[strtoupper($colname)]];
922  }
923 
924  /* Returns: an object containing field information.
925  Get column information in the Recordset object. fetchField() can be used in order to obtain information about
926  fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
927  fetchField() is retrieved.
928  Designed By jcortinap#jc.com.mx
929  */
930  function _FetchField($fieldOffset = -1)
931  {
932  $_typeConversion = array(
933  -155 => 'datetimeoffset',
934  -154 => 'time',
935  -152 => 'xml',
936  -151 => 'udt',
937  -11 => 'uniqueidentifier',
938  -10 => 'ntext',
939  -9 => 'nvarchar',
940  -8 => 'nchar',
941  -7 => 'bit',
942  -6 => 'tinyint',
943  -5 => 'bigint',
944  -4 => 'image',
945  -3 => 'varbinary',
946  -2 => 'timestamp',
947  -1 => 'text',
948  1 => 'char',
949  2 => 'numeric',
950  3 => 'decimal',
951  4 => 'int',
952  5 => 'smallint',
953  6 => 'float',
954  7 => 'real',
955  12 => 'varchar',
956  91 => 'date',
957  93 => 'datetime'
958  );
959 
960  $fa = @sqlsrv_field_metadata($this->_queryID);
961  if ($fieldOffset != -1) {
962  $fa = $fa[$fieldOffset];
963  }
964  $false = false;
965  if (empty($fa)) {
966  $f = false;//PHP Notice: Only variable references should be returned by reference
967  }
968  else
969  {
970  // Convert to an object
971  $fa = array_change_key_case($fa, CASE_LOWER);
972  $fb = array();
973  if ($fieldOffset != -1)
974  {
975  $fb = array(
976  'name' => $fa['name'],
977  'max_length' => $fa['size'],
978  'column_source' => $fa['name'],
979  'type' => $_typeConversion[$fa['type']]
980  );
981  }
982  else
983  {
984  foreach ($fa as $key => $value)
985  {
986  $fb[] = array(
987  'name' => $value['name'],
988  'max_length' => $value['size'],
989  'column_source' => $value['name'],
990  'type' => $_typeConversion[$value['type']]
991  );
992  }
993  }
994  $f = (object) $fb;
995  }
996  return $f;
997  }
998 
999  /*
1000  * Fetchfield copies the oracle method, it loads the field information
1001  * into the _fieldobjs array once, to save multiple calls to the
1002  * sqlsrv_field_metadata function
1003  *
1004  * @author KM Newnham
1005  * @date 02/20/2013
1006  */
1007  function FetchField($fieldOffset = -1)
1008  {
1009  return $this->_fieldobjs[$fieldOffset];
1010  }
1011 
1012  function _seek($row)
1013  {
1014  return false;//There is no support for cursors in the driver at this time. All data is returned via forward-only streams.
1015  }
1016 
1017  // speedup
1018  function MoveNext()
1019  {
1020  //# KMN # if ($this->connection->debug) ADOConnection::outp("movenext()");
1021  //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (beginning): ".$this->EOF);
1022  if ($this->EOF) return false;
1023 
1024  $this->_currentRow++;
1025  // # KMN # if ($this->connection->debug) ADOConnection::outp("_currentRow: ".$this->_currentRow);
1026 
1027  if ($this->_fetch()) return true;
1028  $this->EOF = true;
1029  //# KMN # if ($this->connection->debug) ADOConnection::outp("eof (end): ".$this->EOF);
1030 
1031  return false;
1032  }
1033 
1034 
1035  // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
1036  // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
1037  function _fetch($ignore_fields=false)
1038  {
1039  # KMN # if ($this->connection->debug) ADOConnection::outp("_fetch()");
1040  if ($this->fetchMode & ADODB_FETCH_ASSOC) {
1041  if ($this->fetchMode & ADODB_FETCH_NUM) {
1042  //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: both");
1043  $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
1044  } else {
1045  //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: assoc");
1046  $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
1047  }
1048 
1049  if (is_array($this->fields)) {
1050  if (ADODB_ASSOC_CASE == 0) {
1051  foreach($this->fields as $k=>$v) {
1052  $this->fields[strtolower($k)] = $v;
1053  }
1054  } else if (ADODB_ASSOC_CASE == 1) {
1055  foreach($this->fields as $k=>$v) {
1056  $this->fields[strtoupper($k)] = $v;
1057  }
1058  }
1059  }
1060  } else {
1061  //# KMN # if ($this->connection->debug) ADOConnection::outp("fetch mode: num");
1062  $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
1063  }
1064  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
1065  $arrFixed = array();
1066  foreach($this->fields as $key=>$value) {
1067  if(is_numeric($key)) {
1068  $arrFixed[$key-1] = $value;
1069  } else {
1070  $arrFixed[$key] = $value;
1071  }
1072  }
1073  //if($this->connection->debug) ADOConnection::outp("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
1074  $this->fields = $arrFixed;
1075  }
1076  if(is_array($this->fields)) {
1077  foreach($this->fields as $key=>$value) {
1078  if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
1079  $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
1080  }
1081  }
1082  }
1083  if($this->fields === null) $this->fields = false;
1084  # KMN # if ($this->connection->debug) ADOConnection::outp("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
1085  return $this->fields;
1086  }
1087 
1088  /* close() only needs to be called if you are worried about using too much memory while your script
1089  is running. All associated result memory for the specified result identifier will automatically be freed. */
1090  function _close()
1091  {
1092  if($this->_queryID) {
1093  $rez = sqlsrv_free_stmt($this->_queryID);
1094  $this->_queryID = false;
1095  return $rez;
1096  }
1097  return true;
1098  }
1099 
1100  // mssql uses a default date like Dec 30 2000 12:00AM
1101  static function UnixDate($v)
1102  {
1104  }
1105 
1106  static function UnixTimeStamp($v)
1107  {
1109  }
1110 }
1111 
1112 
1113 class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
1114  function __construct($id=-1,$mode=false)
1115  {
1116  parent::__construct($id,$mode);
1117  }
1118 
1119  // mssql uses a default date like Dec 30 2000 12:00AM
1120  static function UnixDate($v)
1121  {
1122 
1123  if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
1124 
1126 
1127  //Dec 30 2000 12:00AM
1128  if ($ADODB_mssql_date_order == 'dmy') {
1129  if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1130  return parent::UnixDate($v);
1131  }
1132  if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1133 
1134  $theday = $rr[1];
1135  $themth = substr(strtoupper($rr[2]),0,3);
1136  } else {
1137  if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
1138  return parent::UnixDate($v);
1139  }
1140  if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1141 
1142  $theday = $rr[2];
1143  $themth = substr(strtoupper($rr[1]),0,3);
1144  }
1145  $themth = $ADODB_mssql_mths[$themth];
1146  if ($themth <= 0) return false;
1147  // h-m-s-MM-DD-YY
1148  return adodb_mktime(0,0,0,$themth,$theday,$rr[3]);
1149  }
1150 
1151  static function UnixTimeStamp($v)
1152  {
1153 
1154  if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
1155 
1157 
1158  //Dec 30 2000 12:00AM
1159  if ($ADODB_mssql_date_order == 'dmy') {
1160  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})|"
1161  ,$v, $rr)) return parent::UnixTimeStamp($v);
1162  if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1163 
1164  $theday = $rr[1];
1165  $themth = substr(strtoupper($rr[2]),0,3);
1166  } else {
1167  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})|"
1168  ,$v, $rr)) return parent::UnixTimeStamp($v);
1169  if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
1170 
1171  $theday = $rr[2];
1172  $themth = substr(strtoupper($rr[1]),0,3);
1173  }
1174 
1175  $themth = $ADODB_mssql_mths[$themth];
1176  if ($themth <= 0) return false;
1177 
1178  switch (strtoupper($rr[6])) {
1179  case 'P':
1180  if ($rr[4]<12) $rr[4] += 12;
1181  break;
1182  case 'A':
1183  if ($rr[4]==12) $rr[4] = 0;
1184  break;
1185  default:
1186  break;
1187  }
1188  // h-m-s-MM-DD-YY
1189  return adodb_mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
1190  }
1191 }
1192 
1193 /*
1194 Code Example 1:
1195 
1196 select object_name(constid) as constraint_name,
1197  object_name(fkeyid) as table_name,
1198  col_name(fkeyid, fkey) as column_name,
1199  object_name(rkeyid) as referenced_table_name,
1200  col_name(rkeyid, rkey) as referenced_column_name
1201 from sysforeignkeys
1202 where object_name(fkeyid) = x
1203 order by constraint_name, table_name, referenced_table_name, keyno
1204 
1205 Code Example 2:
1206 select constraint_name,
1207  column_name,
1208  ordinal_position
1209 from information_schema.key_column_usage
1210 where constraint_catalog = db_name()
1211 and table_name = x
1212 order by constraint_name, ordinal_position
1213 
1214 http://www.databasejournal.com/scripts/article.php/1440551
1215 */
MetaTables($ttype=false, $showSchema=false, $mask=false)
GenID2012($seq='adodbseq', $start=1)
$database
Definition: server.php:40
if(isset($_REQUEST['nrows'])) else $rs
Definition: server.php:94
MetaForeignKeys($table, $owner=false, $upper=false)
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
SQLDate($fmt, $col=false)
IfNull( $field, $ifNull)
SetTransactionMode( $transaction_mode)
UpdateBlob($table, $column, $val, $where, $blobtype='BLOB')
$ADODB_mssql_date_order
GenID($seq='adodbseq', $start=1)
GenID2008($seq='adodbseq', $start=1)
MetaColumns($table, $upper=true, $schema=false)
MetaPrimaryKeys($table, $owner=false)
CreateSequence($seq='adodbseq', $start=1)
_query($sql, $inputarr=false)
adodb_mktime($hr, $min, $sec, $mon=false, $day=false, $year=false, $is_dst=false, $is_gmt=false)
CreateSequence2012($seq='adodb', $start=1)
_connect($argHostname, $argUsername, $argPassword, $argDatabasename)
RowLock($tables, $where, $col='1 as adodbignore')
$sql
Definition: server.php:84
MetaIndexes($table, $primary=false, $owner=false)
_pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
CreateSequence2008($seq='adodbseq', $start=1)