PageRenderTime 52ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/adodb/drivers/adodb-mssqlnative.inc.php

https://github.com/pennedav/moodle
PHP | 932 lines | 804 code | 42 blank | 86 comment | 60 complexity | 4a6fc2ba0714942205df43e1a850e17d MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MIT, Apache-2.0, BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /*
  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.
  8. Latest version is available at http://adodb.sourceforge.net
  9. Native mssql driver. Requires mssql client. Works on Windows.
  10. http://www.microsoft.com/sql/technologies/php/default.mspx
  11. To configure for Unix, see
  12. http://phpbuilder.com/columns/alberto20000919.php3
  13. $stream = sqlsrv_get_field($stmt, $index, SQLSRV_SQLTYPE_STREAM(SQLSRV_ENC_BINARY));
  14. stream_filter_append($stream, "convert.iconv.ucs-2/utf-8"); // Voila, UTF-8 can be read directly from $stream
  15. */
  16. // security - hide paths
  17. if (!defined('ADODB_DIR')) die();
  18. if (!function_exists('sqlsrv_configure')) {
  19. die("mssqlnative extension not installed");
  20. }
  21. if (!function_exists('sqlsrv_set_error_handling')) {
  22. function sqlsrv_set_error_handling($constant) {
  23. sqlsrv_configure("WarningsReturnAsErrors", $constant);
  24. }
  25. }
  26. if (!function_exists('sqlsrv_log_set_severity')) {
  27. function sqlsrv_log_set_severity($constant) {
  28. sqlsrv_configure("LogSeverity", $constant);
  29. }
  30. }
  31. if (!function_exists('sqlsrv_log_set_subsystems')) {
  32. function sqlsrv_log_set_subsystems($constant) {
  33. sqlsrv_configure("LogSubsystems", $constant);
  34. }
  35. }
  36. //----------------------------------------------------------------
  37. // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
  38. // and this causes tons of problems because localized versions of
  39. // MSSQL will return the dates in dmy or mdy order; and also the
  40. // month strings depends on what language has been configured. The
  41. // following two variables allow you to control the localization
  42. // settings - Ugh.
  43. //
  44. // MORE LOCALIZATION INFO
  45. // ----------------------
  46. // To configure datetime, look for and modify sqlcommn.loc,
  47. // typically found in c:\mssql\install
  48. // Also read :
  49. // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
  50. // Alternatively use:
  51. // CONVERT(char(12),datecol,120)
  52. //
  53. // Also if your month is showing as month-1,
  54. // e.g. Jan 13, 2002 is showing as 13/0/2002, then see
  55. // http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
  56. // it's a localisation problem.
  57. //----------------------------------------------------------------
  58. // has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
  59. if (ADODB_PHPVER >= 0x4300) {
  60. // docs say 4.2.0, but testing shows only since 4.3.0 does it work!
  61. ini_set('mssql.datetimeconvert',0);
  62. } else {
  63. global $ADODB_mssql_mths; // array, months must be upper-case
  64. $ADODB_mssql_date_order = 'mdy';
  65. $ADODB_mssql_mths = array(
  66. 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
  67. 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
  68. }
  69. //---------------------------------------------------------------------------
  70. // Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
  71. // just after you connect to the database. Supports mdy and dmy only.
  72. // Not required for PHP 4.2.0 and above.
  73. function AutoDetect_MSSQL_Date_Order($conn)
  74. {
  75. global $ADODB_mssql_date_order;
  76. $adate = $conn->GetOne('select getdate()');
  77. if ($adate) {
  78. $anum = (int) $adate;
  79. if ($anum > 0) {
  80. if ($anum > 31) {
  81. //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
  82. } else
  83. $ADODB_mssql_date_order = 'dmy';
  84. } else
  85. $ADODB_mssql_date_order = 'mdy';
  86. }
  87. }
  88. class ADODB_mssqlnative extends ADOConnection {
  89. var $databaseType = "mssqlnative";
  90. var $dataProvider = "mssqlnative";
  91. var $replaceQuote = "''"; // string to use to replace quotes
  92. var $fmtDate = "'Y-m-d'";
  93. var $fmtTimeStamp = "'Y-m-d H:i:s'";
  94. var $hasInsertID = true;
  95. var $substr = "substring";
  96. var $length = 'len';
  97. var $hasAffectedRows = true;
  98. var $poorAffectedRows = false;
  99. var $metaDatabasesSQL = "select name from sys.sysdatabases where name <> 'master'";
  100. 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'))";
  101. var $metaColumnsSQL = # xtype==61 is datetime
  102. "select c.name,t.name,c.length,
  103. (case when c.xusertype=61 then 0 else c.xprec end),
  104. (case when c.xusertype=61 then 0 else c.xscale end)
  105. from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
  106. var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
  107. var $hasGenID = true;
  108. var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
  109. var $sysTimeStamp = 'GetDate()';
  110. var $maxParameterLen = 4000;
  111. var $arrayClass = 'ADORecordSet_array_mssqlnative';
  112. var $uniqueSort = true;
  113. var $leftOuter = '*=';
  114. var $rightOuter = '=*';
  115. var $ansiOuter = true; // for mssql7 or later
  116. var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
  117. var $uniqueOrderBy = true;
  118. var $_bindInputArray = true;
  119. var $_dropSeqSQL = "drop table %s";
  120. var $connectionInfo = array();
  121. function ADODB_mssqlnative()
  122. {
  123. if ($this->debug) {
  124. error_log("<pre>");
  125. sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL );
  126. sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL );
  127. sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
  128. sqlsrv_configure('warnings_return_as_errors', 0);
  129. } else {
  130. sqlsrv_set_error_handling(0);
  131. sqlsrv_log_set_severity(0);
  132. sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
  133. sqlsrv_configure('warnings_return_as_errors', 0);
  134. }
  135. }
  136. function ServerInfo()
  137. {
  138. global $ADODB_FETCH_MODE;
  139. if ($this->fetchMode === false) {
  140. $savem = $ADODB_FETCH_MODE;
  141. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  142. } else
  143. $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
  144. $arrServerInfo = sqlsrv_server_info($this->_connectionID);
  145. $ADODB_FETCH_MODE = $savem;
  146. $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
  147. $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
  148. return $arr;
  149. }
  150. function IfNull( $field, $ifNull )
  151. {
  152. return " ISNULL($field, $ifNull) "; // if MS SQL Server
  153. }
  154. function _insertid()
  155. {
  156. // SCOPE_IDENTITY()
  157. // Returns the last IDENTITY value inserted into an IDENTITY column in
  158. // the same scope. A scope is a module -- a stored procedure, trigger,
  159. // function, or batch. Thus, two statements are in the same scope if
  160. // they are in the same stored procedure, function, or batch.
  161. return $this->GetOne($this->identitySQL);
  162. }
  163. function _affectedrows()
  164. {
  165. return sqlsrv_rows_affected($this->_queryID);
  166. }
  167. function CreateSequence($seq='adodbseq',$start=1)
  168. {
  169. if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
  170. sqlsrv_begin_transaction($this->_connectionID);
  171. $start -= 1;
  172. $this->Execute("create table $seq (id int)");//was float(53)
  173. $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
  174. if (!$ok) {
  175. if($this->debug) error_log("<hr>Error: ROLLBACK");
  176. sqlsrv_rollback($this->_connectionID);
  177. return false;
  178. }
  179. sqlsrv_commit($this->_connectionID);
  180. return true;
  181. }
  182. function GenID($seq='adodbseq',$start=1)
  183. {
  184. if($this->debug) error_log("<hr>GenID($seq,$start)");
  185. sqlsrv_begin_transaction($this->_connectionID);
  186. $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
  187. if (!$ok) {
  188. $this->Execute("create table $seq (id int)");
  189. $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
  190. if (!$ok) {
  191. if($this->debug) error_log("<hr>Error: ROLLBACK");
  192. sqlsrv_rollback($this->_connectionID);
  193. return false;
  194. }
  195. sqlsrv_commit($this->_connectionID);
  196. return $start;
  197. }
  198. $num = $this->GetOne("select id from $seq");
  199. sqlsrv_commit($this->_connectionID);
  200. if($this->debug) error_log(" Returning: $num");
  201. return $num;
  202. }
  203. // Format date column in sql string given an input format that understands Y M D
  204. function SQLDate($fmt, $col=false)
  205. {
  206. if (!$col) $col = $this->sysTimeStamp;
  207. $s = '';
  208. $len = strlen($fmt);
  209. for ($i=0; $i < $len; $i++) {
  210. if ($s) $s .= '+';
  211. $ch = $fmt[$i];
  212. switch($ch) {
  213. case 'Y':
  214. case 'y':
  215. $s .= "datename(yyyy,$col)";
  216. break;
  217. case 'M':
  218. $s .= "convert(char(3),$col,0)";
  219. break;
  220. case 'm':
  221. $s .= "replace(str(month($col),2),' ','0')";
  222. break;
  223. case 'Q':
  224. case 'q':
  225. $s .= "datename(quarter,$col)";
  226. break;
  227. case 'D':
  228. case 'd':
  229. $s .= "replace(str(day($col),2),' ','0')";
  230. break;
  231. case 'h':
  232. $s .= "substring(convert(char(14),$col,0),13,2)";
  233. break;
  234. case 'H':
  235. $s .= "replace(str(datepart(hh,$col),2),' ','0')";
  236. break;
  237. case 'i':
  238. $s .= "replace(str(datepart(mi,$col),2),' ','0')";
  239. break;
  240. case 's':
  241. $s .= "replace(str(datepart(ss,$col),2),' ','0')";
  242. break;
  243. case 'a':
  244. case 'A':
  245. $s .= "substring(convert(char(19),$col,0),18,2)";
  246. break;
  247. default:
  248. if ($ch == '\\') {
  249. $i++;
  250. $ch = substr($fmt,$i,1);
  251. }
  252. $s .= $this->qstr($ch);
  253. break;
  254. }
  255. }
  256. return $s;
  257. }
  258. function BeginTrans()
  259. {
  260. if ($this->transOff) return true;
  261. $this->transCnt += 1;
  262. if ($this->debug) error_log('<hr>begin transaction');
  263. sqlsrv_begin_transaction($this->_connectionID);
  264. return true;
  265. }
  266. function CommitTrans($ok=true)
  267. {
  268. if ($this->transOff) return true;
  269. if ($this->debug) error_log('<hr>commit transaction');
  270. if (!$ok) return $this->RollbackTrans();
  271. if ($this->transCnt) $this->transCnt -= 1;
  272. sqlsrv_commit($this->_connectionID);
  273. return true;
  274. }
  275. function RollbackTrans()
  276. {
  277. if ($this->transOff) return true;
  278. if ($this->debug) error_log('<hr>rollback transaction');
  279. if ($this->transCnt) $this->transCnt -= 1;
  280. sqlsrv_rollback($this->_connectionID);
  281. return true;
  282. }
  283. function SetTransactionMode( $transaction_mode )
  284. {
  285. $this->_transmode = $transaction_mode;
  286. if (empty($transaction_mode)) {
  287. $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
  288. return;
  289. }
  290. if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
  291. $this->Execute("SET TRANSACTION ".$transaction_mode);
  292. }
  293. /*
  294. Usage:
  295. $this->BeginTrans();
  296. $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
  297. # some operation on both tables table1 and table2
  298. $this->CommitTrans();
  299. See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
  300. */
  301. function RowLock($tables,$where,$col='1 as adodbignore')
  302. {
  303. if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
  304. if (!$this->transCnt) $this->BeginTrans();
  305. return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
  306. }
  307. function SelectDB($dbName)
  308. {
  309. $this->database = $dbName;
  310. $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
  311. if ($this->_connectionID) {
  312. $rs = $this->Execute('USE '.$dbName);
  313. if($rs) {
  314. return true;
  315. } else return false;
  316. }
  317. else return false;
  318. }
  319. function ErrorMsg()
  320. {
  321. $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
  322. if($retErrors != null) {
  323. foreach($retErrors as $arrError) {
  324. $this->_errorMsg .= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
  325. $this->_errorMsg .= "Error Code: ".$arrError[ 'code']."\n";
  326. $this->_errorMsg .= "Message: ".$arrError[ 'message']."\n";
  327. }
  328. } else {
  329. $this->_errorMsg = "No errors found";
  330. }
  331. return $this->_errorMsg;
  332. }
  333. function ErrorNo()
  334. {
  335. if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
  336. $err = sqlsrv_errors(SQLSRV_ERR_ALL);
  337. if($err[0]) return $err[0]['code'];
  338. else return -1;
  339. }
  340. // returns true or false
  341. function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
  342. {
  343. if (!function_exists('sqlsrv_connect')) return null;
  344. $connectionInfo = $this->connectionInfo;
  345. $connectionInfo["Database"]=$argDatabasename;
  346. $connectionInfo["UID"]=$argUsername;
  347. $connectionInfo["PWD"]=$argPassword;
  348. if ($this->debug) error_log("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
  349. //if ($this->debug) error_log("<hr>_connectionID before: ".serialize($this->_connectionID));
  350. if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) {
  351. if ($this->debug) error_log( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
  352. return false;
  353. }
  354. //if ($this->debug) error_log(" _connectionID after: ".serialize($this->_connectionID));
  355. //if ($this->debug) error_log("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
  356. return true;
  357. }
  358. // returns true or false
  359. function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
  360. {
  361. //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
  362. return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
  363. }
  364. function Prepare($sql)
  365. {
  366. return $sql; // prepare does not work properly with bind parameters as bind parameters are managed by sqlsrv_prepare!
  367. $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
  368. if (!$stmt) return $sql;
  369. return array($sql,$stmt);
  370. }
  371. // returns concatenated string
  372. // MSSQL requires integers to be cast as strings
  373. // automatically cast every datatype to VARCHAR(255)
  374. // @author David Rogers (introspectshun)
  375. function Concat()
  376. {
  377. $s = "";
  378. $arr = func_get_args();
  379. // Split single record on commas, if possible
  380. if (sizeof($arr) == 1) {
  381. foreach ($arr as $arg) {
  382. $args = explode(',', $arg);
  383. }
  384. $arr = $args;
  385. }
  386. array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
  387. $s = implode('+',$arr);
  388. if (sizeof($arr) > 0) return "$s";
  389. return '';
  390. }
  391. /*
  392. Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
  393. So all your blobs must be of type "image".
  394. Remember to set in php.ini the following...
  395. ; Valid range 0 - 2147483647. Default = 4096.
  396. mssql.textlimit = 0 ; zero to pass through
  397. ; Valid range 0 - 2147483647. Default = 4096.
  398. mssql.textsize = 0 ; zero to pass through
  399. */
  400. function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
  401. {
  402. if (strtoupper($blobtype) == 'CLOB') {
  403. $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
  404. return $this->Execute($sql) != false;
  405. }
  406. $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
  407. return $this->Execute($sql) != false;
  408. }
  409. // returns query ID if successful, otherwise false
  410. function _query($sql,$inputarr=false)
  411. {
  412. $this->_errorMsg = false;
  413. if (is_array($inputarr)) {
  414. $rez = sqlsrv_query($this->_connectionID,$sql,$inputarr);
  415. } else if (is_array($sql)) {
  416. // $inputarr is prepared in sqlsrv_prepare();
  417. $rez = sqlsrv_execute($this->_connectionID,$sql[1]);
  418. } else {
  419. $rez = sqlsrv_query($this->_connectionID,$sql);
  420. }
  421. 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));
  422. if(!$rez) $rez = false;
  423. return $rez;
  424. }
  425. // returns true or false
  426. function _close()
  427. {
  428. if ($this->transCnt) $this->RollbackTrans();
  429. $rez = @sqlsrv_close($this->_connectionID);
  430. $this->_connectionID = false;
  431. return $rez;
  432. }
  433. // mssql uses a default date like Dec 30 2000 12:00AM
  434. static function UnixDate($v)
  435. {
  436. return ADORecordSet_array_mssqlnative::UnixDate($v);
  437. }
  438. static function UnixTimeStamp($v)
  439. {
  440. return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
  441. }
  442. function &MetaIndexes($table,$primary=false, $owner = false)
  443. {
  444. $table = $this->qstr($table);
  445. $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
  446. 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,
  447. CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
  448. FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
  449. INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
  450. INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
  451. WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
  452. ORDER BY O.name, I.Name, K.keyno";
  453. global $ADODB_FETCH_MODE;
  454. $save = $ADODB_FETCH_MODE;
  455. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  456. if ($this->fetchMode !== FALSE) {
  457. $savem = $this->SetFetchMode(FALSE);
  458. }
  459. $rs = $this->Execute($sql);
  460. if (isset($savem)) {
  461. $this->SetFetchMode($savem);
  462. }
  463. $ADODB_FETCH_MODE = $save;
  464. if (!is_object($rs)) {
  465. return FALSE;
  466. }
  467. $indexes = array();
  468. while ($row = $rs->FetchRow()) {
  469. if (!$primary && $row[5]) continue;
  470. $indexes[$row[0]]['unique'] = $row[6];
  471. $indexes[$row[0]]['columns'][] = $row[1];
  472. }
  473. return $indexes;
  474. }
  475. function MetaForeignKeys($table, $owner=false, $upper=false)
  476. {
  477. global $ADODB_FETCH_MODE;
  478. $save = $ADODB_FETCH_MODE;
  479. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  480. $table = $this->qstr(strtoupper($table));
  481. $sql =
  482. "select object_name(constid) as constraint_name,
  483. col_name(fkeyid, fkey) as column_name,
  484. object_name(rkeyid) as referenced_table_name,
  485. col_name(rkeyid, rkey) as referenced_column_name
  486. from sysforeignkeys
  487. where upper(object_name(fkeyid)) = $table
  488. order by constraint_name, referenced_table_name, keyno";
  489. $constraints =& $this->GetArray($sql);
  490. $ADODB_FETCH_MODE = $save;
  491. $arr = false;
  492. foreach($constraints as $constr) {
  493. //print_r($constr);
  494. $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
  495. }
  496. if (!$arr) return false;
  497. $arr2 = false;
  498. foreach($arr as $k => $v) {
  499. foreach($v as $a => $b) {
  500. if ($upper) $a = strtoupper($a);
  501. $arr2[$a] = $b;
  502. }
  503. }
  504. return $arr2;
  505. }
  506. //From: Fernando Moreira <FMoreira@imediata.pt>
  507. function MetaDatabases()
  508. {
  509. $this->SelectDB("master");
  510. $rs =& $this->Execute($this->metaDatabasesSQL);
  511. $rows = $rs->GetRows();
  512. $ret = array();
  513. for($i=0;$i<count($rows);$i++) {
  514. $ret[] = $rows[$i][0];
  515. }
  516. $this->SelectDB($this->database);
  517. if($ret)
  518. return $ret;
  519. else
  520. return false;
  521. }
  522. // "Stein-Aksel Basma" <basma@accelero.no>
  523. // tested with MSSQL 2000
  524. function MetaPrimaryKeys($table, $owner=false)
  525. {
  526. global $ADODB_FETCH_MODE;
  527. $schema = '';
  528. $this->_findschema($table,$schema);
  529. if (!$schema) $schema = $this->database;
  530. if ($schema) $schema = "and k.table_catalog like '$schema%'";
  531. $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
  532. information_schema.table_constraints tc
  533. where tc.constraint_name = k.constraint_name and tc.constraint_type =
  534. 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
  535. $savem = $ADODB_FETCH_MODE;
  536. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  537. $a = $this->GetCol($sql);
  538. $ADODB_FETCH_MODE = $savem;
  539. if ($a && sizeof($a)>0) return $a;
  540. $false = false;
  541. return $false;
  542. }
  543. function &MetaTables($ttype=false,$showSchema=false,$mask=false)
  544. {
  545. if ($mask) {
  546. $save = $this->metaTablesSQL;
  547. $mask = $this->qstr(($mask));
  548. $this->metaTablesSQL .= " AND name like $mask";
  549. }
  550. $ret =& ADOConnection::MetaTables($ttype,$showSchema);
  551. if ($mask) {
  552. $this->metaTablesSQL = $save;
  553. }
  554. return $ret;
  555. }
  556. }
  557. /*--------------------------------------------------------------------------------------
  558. Class Name: Recordset
  559. --------------------------------------------------------------------------------------*/
  560. class ADORecordset_mssqlnative extends ADORecordSet {
  561. var $databaseType = "mssqlnative";
  562. var $canSeek = false;
  563. var $fieldOffset = 0;
  564. // _mths works only in non-localised system
  565. function ADORecordset_mssqlnative($id,$mode=false)
  566. {
  567. if ($mode === false) {
  568. global $ADODB_FETCH_MODE;
  569. $mode = $ADODB_FETCH_MODE;
  570. }
  571. $this->fetchMode = $mode;
  572. return $this->ADORecordSet($id,$mode);
  573. }
  574. function _initrs()
  575. {
  576. global $ADODB_COUNTRECS;
  577. if ($this->connection->debug) error_log("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
  578. /*$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."
  579. error_log("rowsaff: ".serialize($retRowsAff));
  580. $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
  581. $this->_numOfRows = -1;//not supported
  582. $fieldmeta = sqlsrv_field_metadata($this->_queryID);
  583. $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
  584. if ($this->connection->debug) error_log("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
  585. }
  586. //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
  587. // get next resultset - requires PHP 4.0.5 or later
  588. function NextRecordSet()
  589. {
  590. if (!sqlsrv_next_result($this->_queryID)) return false;
  591. $this->_inited = false;
  592. $this->bind = false;
  593. $this->_currentRow = -1;
  594. $this->Init();
  595. return true;
  596. }
  597. /* Use associative array to get fields array */
  598. function Fields($colname)
  599. {
  600. if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
  601. if (!$this->bind) {
  602. $this->bind = array();
  603. for ($i=0; $i < $this->_numOfFields; $i++) {
  604. $o = $this->FetchField($i);
  605. $this->bind[strtoupper($o->name)] = $i;
  606. }
  607. }
  608. return $this->fields[$this->bind[strtoupper($colname)]];
  609. }
  610. /* Returns: an object containing field information.
  611. Get column information in the Recordset object. fetchField() can be used in order to obtain information about
  612. fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
  613. fetchField() is retrieved. */
  614. function FetchField($fieldOffset = -1)
  615. {
  616. if ($this->connection->debug) error_log("<hr>fetchfield: $fieldOffset, fetch array: <pre>".print_r($this->fields,true)."</pre> backtrace: ".adodb_backtrace(false));
  617. if ($fieldOffset != -1) $this->fieldOffset = $fieldOffset;
  618. /*$arrKeys = array_keys($this->fields);
  619. if(array_key_exists($this->fieldOffset,$arrKeys) && !array_key_exists($arrKeys[$this->fieldOffset],$this->fields)) {
  620. $f = false;
  621. } else {
  622. $f = new ADOFetchObj();
  623. $f->name = $arrKeys[$this->fieldOffset];
  624. if($fieldOffset == -1) $this->fieldOffset++;
  625. }
  626. if (empty($f)) {
  627. $f = false;//PHP Notice: Only variable references should be returned by reference
  628. }*/
  629. $fieldMeta = @sqlsrv_field_metadata($this->_queryID);
  630. $f = new ADOFieldObject();
  631. $f->name = $fieldMeta[$this->fieldOffset]['Name'];
  632. $f->type = $fieldMeta[$this->fieldOffset]['Type'];
  633. $f->max_length = $fieldMeta[$this->fieldOffset]['Size'];
  634. return $f;
  635. }
  636. function _seek($row)
  637. {
  638. return false;//There is no support for cursors in the driver at this time. All data is returned via forward-only streams.
  639. }
  640. // speedup
  641. function MoveNext()
  642. {
  643. if ($this->connection->debug) error_log("movenext()");
  644. //if ($this->connection->debug) error_log("eof (beginning): ".$this->EOF);
  645. if ($this->EOF) return false;
  646. $this->_currentRow++;
  647. if ($this->connection->debug) error_log("_currentRow: ".$this->_currentRow);
  648. if ($this->_fetch()) return true;
  649. $this->EOF = true;
  650. //if ($this->connection->debug) error_log("eof (end): ".$this->EOF);
  651. return false;
  652. }
  653. // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
  654. // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
  655. function _fetch($ignore_fields=false)
  656. {
  657. if ($this->connection->debug) error_log("_fetch()");
  658. if ($this->fetchMode & ADODB_FETCH_BOTH) {
  659. if ($this->fetchMode & ADODB_FETCH_NUM) {
  660. if ($this->connection->debug) error_log("fetch mode: both");
  661. $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
  662. } else {
  663. if ($this->connection->debug) error_log("fetch mode: assoc");
  664. $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
  665. }
  666. if (is_array($this->fields)) {
  667. if (ADODB_ASSOC_CASE == 0) {
  668. foreach($this->fields as $k=>$v) {
  669. $this->fields[strtolower($k)] = $v;
  670. }
  671. } else if (ADODB_ASSOC_CASE == 1) {
  672. foreach($this->fields as $k=>$v) {
  673. $this->fields[strtoupper($k)] = $v;
  674. }
  675. }
  676. }
  677. } else {
  678. if ($this->connection->debug) error_log("fetch mode: num");
  679. $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
  680. }
  681. 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
  682. $arrFixed = array();
  683. foreach($this->fields as $key=>$value) {
  684. if(is_numeric($key)) {
  685. $arrFixed[$key-1] = $value;
  686. } else {
  687. $arrFixed[$key] = $value;
  688. }
  689. }
  690. //if($this->connection->debug) error_log("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
  691. $this->fields = $arrFixed;
  692. }
  693. if(is_array($this->fields)) {
  694. foreach($this->fields as $key=>$value) {
  695. if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
  696. $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
  697. }
  698. }
  699. }
  700. if($this->fields === null) $this->fields = false;
  701. if ($this->connection->debug) error_log("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
  702. return $this->fields;
  703. }
  704. /* close() only needs to be called if you are worried about using too much memory while your script
  705. is running. All associated result memory for the specified result identifier will automatically be freed. */
  706. function _close()
  707. {
  708. $rez = sqlsrv_free_stmt($this->_queryID);
  709. $this->_queryID = false;
  710. return $rez;
  711. }
  712. // mssql uses a default date like Dec 30 2000 12:00AM
  713. static function UnixDate($v)
  714. {
  715. return ADORecordSet_array_mssqlnative::UnixDate($v);
  716. }
  717. static function UnixTimeStamp($v)
  718. {
  719. return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
  720. }
  721. }
  722. class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
  723. function ADORecordSet_array_mssqlnative($id=-1,$mode=false)
  724. {
  725. $this->ADORecordSet_array($id,$mode);
  726. }
  727. // mssql uses a default date like Dec 30 2000 12:00AM
  728. static function UnixDate($v)
  729. {
  730. if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
  731. global $ADODB_mssql_mths,$ADODB_mssql_date_order;
  732. //Dec 30 2000 12:00AM
  733. if ($ADODB_mssql_date_order == 'dmy') {
  734. if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
  735. return parent::UnixDate($v);
  736. }
  737. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  738. $theday = $rr[1];
  739. $themth = substr(strtoupper($rr[2]),0,3);
  740. } else {
  741. if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
  742. return parent::UnixDate($v);
  743. }
  744. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  745. $theday = $rr[2];
  746. $themth = substr(strtoupper($rr[1]),0,3);
  747. }
  748. $themth = $ADODB_mssql_mths[$themth];
  749. if ($themth <= 0) return false;
  750. // h-m-s-MM-DD-YY
  751. return adodb_mktime(0,0,0,$themth,$theday,$rr[3]);
  752. }
  753. static function UnixTimeStamp($v)
  754. {
  755. if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
  756. global $ADODB_mssql_mths,$ADODB_mssql_date_order;
  757. //Dec 30 2000 12:00AM
  758. if ($ADODB_mssql_date_order == 'dmy') {
  759. 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})|"
  760. ,$v, $rr)) return parent::UnixTimeStamp($v);
  761. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  762. $theday = $rr[1];
  763. $themth = substr(strtoupper($rr[2]),0,3);
  764. } else {
  765. 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})|"
  766. ,$v, $rr)) return parent::UnixTimeStamp($v);
  767. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  768. $theday = $rr[2];
  769. $themth = substr(strtoupper($rr[1]),0,3);
  770. }
  771. $themth = $ADODB_mssql_mths[$themth];
  772. if ($themth <= 0) return false;
  773. switch (strtoupper($rr[6])) {
  774. case 'P':
  775. if ($rr[4]<12) $rr[4] += 12;
  776. break;
  777. case 'A':
  778. if ($rr[4]==12) $rr[4] = 0;
  779. break;
  780. default:
  781. break;
  782. }
  783. // h-m-s-MM-DD-YY
  784. return adodb_mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
  785. }
  786. }
  787. /*
  788. Code Example 1:
  789. select object_name(constid) as constraint_name,
  790. object_name(fkeyid) as table_name,
  791. col_name(fkeyid, fkey) as column_name,
  792. object_name(rkeyid) as referenced_table_name,
  793. col_name(rkeyid, rkey) as referenced_column_name
  794. from sysforeignkeys
  795. where object_name(fkeyid) = x
  796. order by constraint_name, table_name, referenced_table_name, keyno
  797. Code Example 2:
  798. select constraint_name,
  799. column_name,
  800. ordinal_position
  801. from information_schema.key_column_usage
  802. where constraint_catalog = db_name()
  803. and table_name = x
  804. order by constraint_name, ordinal_position
  805. http://www.databasejournal.com/scripts/article.php/1440551
  806. */
  807. ?>