mssql.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * The PEAR DB driver for PHP's mssql extension
  5. * for interacting with Microsoft SQL Server databases
  6. *
  7. * PHP version 5
  8. *
  9. * LICENSE: This source file is subject to version 3.0 of the PHP license
  10. * that is available through the world-wide-web at the following URI:
  11. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  12. * the PHP License and are unable to obtain it through the web, please
  13. * send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category Database
  16. * @package DB
  17. * @author Sterling Hughes <sterling@php.net>
  18. * @author Daniel Convissor <danielc@php.net>
  19. * @copyright 1997-2007 The PHP Group
  20. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  21. * @version CVS: $Id$
  22. * @link http://pear.php.net/package/DB
  23. */
  24. /**
  25. * Obtain the DB_common class so it can be extended from
  26. */
  27. //require_once 'DB/common.php';
  28. require_once 'common.php';
  29. /**
  30. * The methods PEAR DB uses to interact with PHP's mssql extension
  31. * for interacting with Microsoft SQL Server databases
  32. *
  33. * These methods overload the ones declared in DB_common.
  34. *
  35. * DB's mssql driver is only for Microsfoft SQL Server databases.
  36. *
  37. * If you're connecting to a Sybase database, you MUST specify "sybase"
  38. * as the "phptype" in the DSN.
  39. *
  40. * This class only works correctly if you have compiled PHP using
  41. * --with-mssql=[dir_to_FreeTDS].
  42. *
  43. * @category Database
  44. * @package DB
  45. * @author Sterling Hughes <sterling@php.net>
  46. * @author Daniel Convissor <danielc@php.net>
  47. * @copyright 1997-2007 The PHP Group
  48. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  49. * @version Release: 1.9.2
  50. * @link http://pear.php.net/package/DB
  51. */
  52. class DB_mssql extends DB_common
  53. {
  54. // {{{ properties
  55. /**
  56. * The DB driver type (mysql, oci8, odbc, etc.)
  57. * @var string
  58. */
  59. public $phptype = 'mssql';
  60. /**
  61. * The database syntax variant to be used (db2, access, etc.), if any
  62. * @var string
  63. */
  64. public $dbsyntax = 'mssql';
  65. /**
  66. * The capabilities of this DB implementation
  67. *
  68. * The 'new_link' element contains the PHP version that first provided
  69. * new_link support for this DBMS. Contains false if it's unsupported.
  70. *
  71. * Meaning of the 'limit' element:
  72. * + 'emulate' = emulate with fetch row by number
  73. * + 'alter' = alter the query
  74. * + false = skip rows
  75. *
  76. * @var array
  77. */
  78. public $features = array(
  79. 'limit' => 'emulate',
  80. 'new_link' => false,
  81. 'numrows' => true,
  82. 'pconnect' => true,
  83. 'prepare' => false,
  84. 'ssl' => false,
  85. 'transactions' => true,
  86. );
  87. /**
  88. * A mapping of native error codes to DB error codes
  89. * @var array
  90. */
  91. // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
  92. public $errorcode_map = array(
  93. 102 => DB_ERROR_SYNTAX,
  94. 110 => DB_ERROR_VALUE_COUNT_ON_ROW,
  95. 155 => DB_ERROR_NOSUCHFIELD,
  96. 156 => DB_ERROR_SYNTAX,
  97. 170 => DB_ERROR_SYNTAX,
  98. 207 => DB_ERROR_NOSUCHFIELD,
  99. 208 => DB_ERROR_NOSUCHTABLE,
  100. 245 => DB_ERROR_INVALID_NUMBER,
  101. 319 => DB_ERROR_SYNTAX,
  102. 321 => DB_ERROR_NOSUCHFIELD,
  103. 325 => DB_ERROR_SYNTAX,
  104. 336 => DB_ERROR_SYNTAX,
  105. 515 => DB_ERROR_CONSTRAINT_NOT_NULL,
  106. 547 => DB_ERROR_CONSTRAINT,
  107. 1018 => DB_ERROR_SYNTAX,
  108. 1035 => DB_ERROR_SYNTAX,
  109. 1913 => DB_ERROR_ALREADY_EXISTS,
  110. 2209 => DB_ERROR_SYNTAX,
  111. 2223 => DB_ERROR_SYNTAX,
  112. 2248 => DB_ERROR_SYNTAX,
  113. 2256 => DB_ERROR_SYNTAX,
  114. 2257 => DB_ERROR_SYNTAX,
  115. 2627 => DB_ERROR_CONSTRAINT,
  116. 2714 => DB_ERROR_ALREADY_EXISTS,
  117. 3607 => DB_ERROR_DIVZERO,
  118. 3701 => DB_ERROR_NOSUCHTABLE,
  119. 7630 => DB_ERROR_SYNTAX,
  120. 8134 => DB_ERROR_DIVZERO,
  121. 9303 => DB_ERROR_SYNTAX,
  122. 9317 => DB_ERROR_SYNTAX,
  123. 9318 => DB_ERROR_SYNTAX,
  124. 9331 => DB_ERROR_SYNTAX,
  125. 9332 => DB_ERROR_SYNTAX,
  126. 15253 => DB_ERROR_SYNTAX,
  127. );
  128. /**
  129. * The raw database connection created by PHP
  130. * @var resource
  131. */
  132. public $connection;
  133. /**
  134. * The DSN information for connecting to a database
  135. * @var array
  136. */
  137. public $dsn = array();
  138. /**
  139. * Should data manipulation queries be committed automatically?
  140. * @var bool
  141. * @access private
  142. */
  143. public $autocommit = true;
  144. /**
  145. * The quantity of transactions begun
  146. *
  147. * {@internal While this is private, it can't actually be designated
  148. * private in PHP 5 because it is directly accessed in the test suite.}}
  149. *
  150. * @var integer
  151. * @access private
  152. */
  153. public $transaction_opcount = 0;
  154. /**
  155. * The database specified in the DSN
  156. *
  157. * It's a fix to allow calls to different databases in the same script.
  158. *
  159. * @var string
  160. * @access private
  161. */
  162. public $_db = null;
  163. // }}}
  164. // {{{ constructor
  165. /**
  166. * This constructor calls <kbd>parent::__construct()</kbd>
  167. *
  168. * @return void
  169. */
  170. public function __construct()
  171. {
  172. parent::__construct();
  173. }
  174. // }}}
  175. // {{{ connect()
  176. /**
  177. * Connect to the database server, log in and open the database
  178. *
  179. * Don't call this method directly. Use DB::connect() instead.
  180. *
  181. * @param array $dsn the data source name
  182. * @param bool $persistent should the connection be persistent?
  183. *
  184. * @return int|object
  185. */
  186. public function connect($dsn, $persistent = false)
  187. {
  188. if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
  189. && !PEAR::loadExtension('sybase_ct')) {
  190. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  191. }
  192. $this->dsn = $dsn;
  193. if ($dsn['dbsyntax']) {
  194. $this->dbsyntax = $dsn['dbsyntax'];
  195. }
  196. $params = array(
  197. $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
  198. $dsn['username'] ? $dsn['username'] : null,
  199. $dsn['password'] ? $dsn['password'] : null,
  200. );
  201. if ($dsn['port']) {
  202. $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
  203. . $dsn['port'];
  204. }
  205. $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
  206. $this->connection = @call_user_func_array($connect_function, $params);
  207. if (!$this->connection) {
  208. return $this->raiseError(
  209. DB_ERROR_CONNECT_FAILED,
  210. null,
  211. null,
  212. null,
  213. @mssql_get_last_message()
  214. );
  215. }
  216. if ($dsn['database']) {
  217. if (!@mssql_select_db($dsn['database'], $this->connection)) {
  218. return $this->raiseError(
  219. DB_ERROR_NODBSELECTED,
  220. null,
  221. null,
  222. null,
  223. @mssql_get_last_message()
  224. );
  225. }
  226. $this->_db = $dsn['database'];
  227. }
  228. return DB_OK;
  229. }
  230. // }}}
  231. // {{{ disconnect()
  232. /**
  233. * Disconnects from the database server
  234. *
  235. * @return bool TRUE on success, FALSE on failure
  236. */
  237. public function disconnect()
  238. {
  239. $ret = @mssql_close($this->connection);
  240. $this->connection = null;
  241. return $ret;
  242. }
  243. // }}}
  244. // {{{ simpleQuery()
  245. /**
  246. * Sends a query to the database server
  247. *
  248. * @param string the SQL query string
  249. *
  250. * @return mixed + a PHP result resrouce for successful SELECT queries
  251. * + the DB_OK constant for other successful queries
  252. * + a DB_Error object on failure
  253. */
  254. public function simpleQuery($query)
  255. {
  256. $ismanip = $this->_checkManip($query);
  257. $this->last_query = $query;
  258. if (!@mssql_select_db($this->_db, $this->connection)) {
  259. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  260. }
  261. $query = $this->modifyQuery($query);
  262. if (!$this->autocommit && $ismanip) {
  263. if ($this->transaction_opcount == 0) {
  264. $result = @mssql_query('BEGIN TRAN', $this->connection);
  265. if (!$result) {
  266. return $this->mssqlRaiseError();
  267. }
  268. }
  269. $this->transaction_opcount++;
  270. }
  271. $result = @mssql_query($query, $this->connection);
  272. if (!$result) {
  273. return $this->mssqlRaiseError();
  274. }
  275. // Determine which queries that should return data, and which
  276. // should return an error code only.
  277. return $ismanip ? DB_OK : $result;
  278. }
  279. // }}}
  280. // {{{ nextResult()
  281. /**
  282. * Produces a DB_Error object regarding the current problem
  283. *
  284. * @param null $code
  285. * @return object the DB_Error object
  286. *
  287. * @see DB_common::raiseError(),
  288. * DB_mssql::errorNative(), DB_mssql::errorCode()
  289. */
  290. public function mssqlRaiseError($code = null)
  291. {
  292. $message = @mssql_get_last_message();
  293. if (!$code) {
  294. $code = $this->errorNative();
  295. }
  296. return $this->raiseError(
  297. $this->errorCode($code, $message),
  298. null,
  299. null,
  300. null,
  301. "$code - $message"
  302. );
  303. }
  304. // }}}
  305. // {{{ fetchInto()
  306. /**
  307. * Gets the DBMS' native error code produced by the last query
  308. *
  309. * @return int the DBMS' error code
  310. */
  311. public function errorNative()
  312. {
  313. $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
  314. if (!$res) {
  315. return DB_ERROR;
  316. }
  317. $row = @mssql_fetch_row($res);
  318. return $row[0];
  319. }
  320. // }}}
  321. // {{{ freeResult()
  322. /**
  323. * Determines PEAR::DB error code from mssql's native codes.
  324. *
  325. * If <var>$nativecode</var> isn't known yet, it will be looked up.
  326. *
  327. * @param mixed $nativecode mssql error code, if known
  328. * @param string $msg
  329. * @return integer an error number from a DB error constant
  330. * @see errorNative()
  331. */
  332. public function errorCode($nativecode = null, $msg = '')
  333. {
  334. if (!$nativecode) {
  335. $nativecode = $this->errorNative();
  336. }
  337. if (isset($this->errorcode_map[$nativecode])) {
  338. if ($nativecode == 3701
  339. && preg_match('/Cannot drop the index/i', $msg)) {
  340. return DB_ERROR_NOT_FOUND;
  341. }
  342. return $this->errorcode_map[$nativecode];
  343. } else {
  344. return DB_ERROR;
  345. }
  346. }
  347. // }}}
  348. // {{{ numCols()
  349. /**
  350. * Move the internal mssql result pointer to the next available result
  351. *
  352. * @param a valid fbsql result resource
  353. *
  354. * @access public
  355. *
  356. * @return true if a result is available otherwise return false
  357. */
  358. public function nextResult($result)
  359. {
  360. return @mssql_next_result($result);
  361. }
  362. // }}}
  363. // {{{ numRows()
  364. /**
  365. * Places a row from the result set into the given array
  366. *
  367. * Formating of the array and the data therein are configurable.
  368. * See DB_result::fetchInto() for more information.
  369. *
  370. * This method is not meant to be called directly. Use
  371. * DB_result::fetchInto() instead. It can't be declared "protected"
  372. * because DB_result is a separate object.
  373. *
  374. * @param resource $result the query result resource
  375. * @param array $arr the referenced array to put the data in
  376. * @param int $fetchmode how the resulting array should be indexed
  377. * @param int $rownum the row number to fetch (0 = first row)
  378. *
  379. * @return mixed DB_OK on success, NULL when the end of a result set is
  380. * reached or on failure
  381. *
  382. * @see DB_result::fetchInto()
  383. */
  384. public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  385. {
  386. if ($rownum !== null) {
  387. if (!@mssql_data_seek($result, $rownum)) {
  388. return null;
  389. }
  390. }
  391. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  392. $arr = @mssql_fetch_assoc($result);
  393. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  394. $arr = array_change_key_case($arr, CASE_LOWER);
  395. }
  396. } else {
  397. $arr = @mssql_fetch_row($result);
  398. }
  399. if (!$arr) {
  400. return null;
  401. }
  402. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  403. $this->_rtrimArrayValues($arr);
  404. }
  405. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  406. $this->_convertNullArrayValuesToEmpty($arr);
  407. }
  408. return DB_OK;
  409. }
  410. // }}}
  411. // {{{ autoCommit()
  412. /**
  413. * Deletes the result set and frees the memory occupied by the result set
  414. *
  415. * This method is not meant to be called directly. Use
  416. * DB_result::free() instead. It can't be declared "protected"
  417. * because DB_result is a separate object.
  418. *
  419. * @param resource $result PHP's query result resource
  420. *
  421. * @return bool TRUE on success, FALSE if $result is invalid
  422. *
  423. * @see DB_result::free()
  424. */
  425. public function freeResult($result)
  426. {
  427. return is_resource($result) ? mssql_free_result($result) : false;
  428. }
  429. // }}}
  430. // {{{ commit()
  431. /**
  432. * Gets the number of columns in a result set
  433. *
  434. * This method is not meant to be called directly. Use
  435. * DB_result::numCols() instead. It can't be declared "protected"
  436. * because DB_result is a separate object.
  437. *
  438. * @param resource $result PHP's query result resource
  439. *
  440. * @return int|object
  441. *
  442. * @see DB_result::numCols()
  443. */
  444. public function numCols($result)
  445. {
  446. $cols = @mssql_num_fields($result);
  447. if (!$cols) {
  448. return $this->mssqlRaiseError();
  449. }
  450. return $cols;
  451. }
  452. // }}}
  453. // {{{ rollback()
  454. /**
  455. * Gets the number of rows in a result set
  456. *
  457. * This method is not meant to be called directly. Use
  458. * DB_result::numRows() instead. It can't be declared "protected"
  459. * because DB_result is a separate object.
  460. *
  461. * @param resource $result PHP's query result resource
  462. *
  463. * @return int|object
  464. *
  465. * @see DB_result::numRows()
  466. */
  467. public function numRows($result)
  468. {
  469. $rows = @mssql_num_rows($result);
  470. if ($rows === false) {
  471. return $this->mssqlRaiseError();
  472. }
  473. return $rows;
  474. }
  475. // }}}
  476. // {{{ affectedRows()
  477. /**
  478. * Enables or disables automatic commits
  479. *
  480. * @param bool $onoff true turns it on, false turns it off
  481. *
  482. * @return int DB_OK on success. A DB_Error object if the driver
  483. * doesn't support auto-committing transactions.
  484. */
  485. public function autoCommit($onoff = false)
  486. {
  487. // XXX if $this->transaction_opcount > 0, we should probably
  488. // issue a warning here.
  489. $this->autocommit = $onoff ? true : false;
  490. return DB_OK;
  491. }
  492. // }}}
  493. // {{{ nextId()
  494. /**
  495. * Commits the current transaction
  496. *
  497. * @return int|object
  498. */
  499. public function commit()
  500. {
  501. if ($this->transaction_opcount > 0) {
  502. if (!@mssql_select_db($this->_db, $this->connection)) {
  503. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  504. }
  505. $result = @mssql_query('COMMIT TRAN', $this->connection);
  506. $this->transaction_opcount = 0;
  507. if (!$result) {
  508. return $this->mssqlRaiseError();
  509. }
  510. }
  511. return DB_OK;
  512. }
  513. /**
  514. * Reverts the current transaction
  515. *
  516. * @return int|object
  517. */
  518. public function rollback()
  519. {
  520. if ($this->transaction_opcount > 0) {
  521. if (!@mssql_select_db($this->_db, $this->connection)) {
  522. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  523. }
  524. $result = @mssql_query('ROLLBACK TRAN', $this->connection);
  525. $this->transaction_opcount = 0;
  526. if (!$result) {
  527. return $this->mssqlRaiseError();
  528. }
  529. }
  530. return DB_OK;
  531. }
  532. // }}}
  533. // {{{ dropSequence()
  534. /**
  535. * Determines the number of rows affected by a data maniuplation query
  536. *
  537. * 0 is returned for queries that don't manipulate data.
  538. *
  539. * @return int|object
  540. */
  541. public function affectedRows()
  542. {
  543. if ($this->_last_query_manip) {
  544. $res = @mssql_query('select @@rowcount', $this->connection);
  545. if (!$res) {
  546. return $this->mssqlRaiseError();
  547. }
  548. $ar = @mssql_fetch_row($res);
  549. if (!$ar) {
  550. $result = 0;
  551. } else {
  552. @mssql_free_result($res);
  553. $result = $ar[0];
  554. }
  555. } else {
  556. $result = 0;
  557. }
  558. return $result;
  559. }
  560. // }}}
  561. // {{{ escapeSimple()
  562. /**
  563. * Returns the next free id in a sequence
  564. *
  565. * @param string $seq_name name of the sequence
  566. * @param boolean $ondemand when true, the seqence is automatically
  567. * created if it does not exist
  568. *
  569. * @return int|object
  570. * A DB_Error object on failure.
  571. *
  572. * @see DB_common::nextID(), DB_common::getSequenceName(),
  573. * DB_mssql::createSequence(), DB_mssql::dropSequence()
  574. */
  575. public function nextId($seq_name, $ondemand = true)
  576. {
  577. $seqname = $this->getSequenceName($seq_name);
  578. if (!@mssql_select_db($this->_db, $this->connection)) {
  579. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  580. }
  581. $repeat = 0;
  582. do {
  583. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  584. $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
  585. $this->popErrorHandling();
  586. if ($ondemand && DB::isError($result) &&
  587. ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) {
  588. $repeat = 1;
  589. $result = $this->createSequence($seq_name);
  590. if (DB::isError($result)) {
  591. return $this->raiseError($result);
  592. }
  593. } elseif (!DB::isError($result)) {
  594. $result = $this->query("SELECT IDENT_CURRENT('$seqname')");
  595. if (DB::isError($result)) {
  596. /* Fallback code for MS SQL Server 7.0, which doesn't have
  597. * IDENT_CURRENT. This is *not* safe for concurrent
  598. * requests, and really, if you're using it, you're in a
  599. * world of hurt. Nevertheless, it's here to ensure BC. See
  600. * bug #181 for the gory details.*/
  601. $result = $this->query("SELECT @@IDENTITY FROM $seqname");
  602. }
  603. $repeat = 0;
  604. } else {
  605. $repeat = false;
  606. }
  607. } while ($repeat);
  608. if (DB::isError($result)) {
  609. return $this->raiseError($result);
  610. }
  611. $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
  612. return $result[0];
  613. }
  614. // }}}
  615. // {{{ quoteIdentifier()
  616. /**
  617. * Creates a new sequence
  618. *
  619. * @param string $seq_name name of the new sequence
  620. *
  621. * @return int DB_OK on success. A DB_Error object on failure.
  622. *
  623. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  624. * DB_mssql::nextID(), DB_mssql::dropSequence()
  625. */
  626. public function createSequence($seq_name)
  627. {
  628. return $this->query('CREATE TABLE '
  629. . $this->getSequenceName($seq_name)
  630. . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
  631. . ' [vapor] [int] NULL)');
  632. }
  633. // }}}
  634. // {{{ mssqlRaiseError()
  635. /**
  636. * Deletes a sequence
  637. *
  638. * @param string $seq_name name of the sequence to be deleted
  639. *
  640. * @return int DB_OK on success. A DB_Error object on failure.
  641. *
  642. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  643. * DB_mssql::nextID(), DB_mssql::createSequence()
  644. */
  645. public function dropSequence($seq_name)
  646. {
  647. return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
  648. }
  649. // }}}
  650. // {{{ errorNative()
  651. /**
  652. * Escapes a string in a manner suitable for SQL Server.
  653. *
  654. * @param string $str the string to be escaped
  655. * @return string the escaped string
  656. *
  657. * @see DB_common::quoteSmart()
  658. * @since Method available since Release 1.6.0
  659. */
  660. public function escapeSimple($str)
  661. {
  662. return str_replace(
  663. array("'", "\\\r\n", "\\\n"),
  664. array("''", "\\\\\r\n\r\n", "\\\\\n\n"),
  665. $str
  666. );
  667. }
  668. // }}}
  669. // {{{ errorCode()
  670. /**
  671. * Quotes a string so it can be safely used as a table or column name
  672. *
  673. * @param string $str identifier name to be quoted
  674. *
  675. * @return string quoted identifier string
  676. *
  677. * @see DB_common::quoteIdentifier()
  678. * @since Method available since Release 1.6.0
  679. */
  680. public function quoteIdentifier($str)
  681. {
  682. return '[' . str_replace(']', ']]', $str) . ']';
  683. }
  684. // }}}
  685. // {{{ tableInfo()
  686. /**
  687. * Returns information about a table or a result set
  688. *
  689. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  690. * is a table name.
  691. *
  692. * @param object|string $result DB_result object from a query or a
  693. * string containing the name of a table.
  694. * While this also accepts a query result
  695. * resource identifier, this behavior is
  696. * deprecated.
  697. * @param int $mode a valid tableInfo mode
  698. *
  699. * @return array|object
  700. * A DB_Error object on failure.
  701. *
  702. * @see DB_common::tableInfo()
  703. */
  704. public function tableInfo($result, $mode = null)
  705. {
  706. if (is_string($result)) {
  707. /*
  708. * Probably received a table name.
  709. * Create a result resource identifier.
  710. */
  711. if (!@mssql_select_db($this->_db, $this->connection)) {
  712. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  713. }
  714. $id = @mssql_query(
  715. "SELECT * FROM $result WHERE 1=0",
  716. $this->connection
  717. );
  718. $got_string = true;
  719. } elseif (isset($result->result)) {
  720. /*
  721. * Probably received a result object.
  722. * Extract the result resource identifier.
  723. */
  724. $id = $result->result;
  725. $got_string = false;
  726. } else {
  727. /*
  728. * Probably received a result resource identifier.
  729. * Copy it.
  730. * Deprecated. Here for compatibility only.
  731. */
  732. $id = $result;
  733. $got_string = false;
  734. }
  735. if (!is_resource($id)) {
  736. return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  737. }
  738. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  739. $case_func = 'strtolower';
  740. } else {
  741. $case_func = 'strval';
  742. }
  743. $count = @mssql_num_fields($id);
  744. $res = array();
  745. if ($mode) {
  746. $res['num_fields'] = $count;
  747. }
  748. for ($i = 0; $i < $count; $i++) {
  749. if ($got_string) {
  750. $flags = $this->_mssql_field_flags(
  751. $result,
  752. @mssql_field_name($id, $i)
  753. );
  754. if (DB::isError($flags)) {
  755. return $flags;
  756. }
  757. } else {
  758. $flags = '';
  759. }
  760. $res[$i] = array(
  761. 'table' => $got_string ? $case_func($result) : '',
  762. 'name' => $case_func(@mssql_field_name($id, $i)),
  763. 'type' => @mssql_field_type($id, $i),
  764. 'len' => @mssql_field_length($id, $i),
  765. 'flags' => $flags,
  766. );
  767. if ($mode & DB_TABLEINFO_ORDER) {
  768. $res['order'][$res[$i]['name']] = $i;
  769. }
  770. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  771. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  772. }
  773. }
  774. // free the result only if we were called on a table
  775. if ($got_string) {
  776. @mssql_free_result($id);
  777. }
  778. return $res;
  779. }
  780. // }}}
  781. // {{{ _mssql_field_flags()
  782. /**
  783. * Get a column's flags
  784. *
  785. * Supports "not_null", "primary_key",
  786. * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
  787. * "unique_key" (mssql unique index, unique check or primary_key) and
  788. * "multiple_key" (multikey index)
  789. *
  790. * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
  791. * not useful at all - is the behaviour of mysql_field_flags that primary
  792. * keys are alway unique? is the interpretation of multiple_key correct?
  793. *
  794. * @param string $table the table name
  795. * @param string $column the field name
  796. *
  797. * @return array|string
  798. *
  799. * @access private
  800. * @author Joern Barthel <j_barthel@web.de>
  801. */
  802. public function _mssql_field_flags($table, $column)
  803. {
  804. static $tableName = null;
  805. static $flags = array();
  806. if ($table != $tableName) {
  807. $flags = array();
  808. $tableName = $table;
  809. // get unique and primary keys
  810. $res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC);
  811. if (DB::isError($res)) {
  812. return $res;
  813. }
  814. foreach ($res as $val) {
  815. $keys = explode(', ', $val['index_keys']);
  816. if (sizeof($keys) > 1) {
  817. foreach ($keys as $key) {
  818. $this->_add_flag($flags[$key], 'multiple_key');
  819. }
  820. }
  821. if (strpos($val['index_description'], 'primary key')) {
  822. foreach ($keys as $key) {
  823. $this->_add_flag($flags[$key], 'primary_key');
  824. }
  825. } elseif (strpos($val['index_description'], 'unique')) {
  826. foreach ($keys as $key) {
  827. $this->_add_flag($flags[$key], 'unique_key');
  828. }
  829. }
  830. }
  831. // get auto_increment, not_null and timestamp
  832. $res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC);
  833. if (DB::isError($res)) {
  834. return $res;
  835. }
  836. foreach ($res as $val) {
  837. $val = array_change_key_case($val, CASE_LOWER);
  838. if ($val['nullable'] == '0') {
  839. $this->_add_flag($flags[$val['column_name']], 'not_null');
  840. }
  841. if (strpos($val['type_name'], 'identity')) {
  842. $this->_add_flag($flags[$val['column_name']], 'auto_increment');
  843. }
  844. if (strpos($val['type_name'], 'timestamp')) {
  845. $this->_add_flag($flags[$val['column_name']], 'timestamp');
  846. }
  847. }
  848. }
  849. if (array_key_exists($column, $flags)) {
  850. return (implode(' ', $flags[$column]));
  851. }
  852. return '';
  853. }
  854. // }}}
  855. // {{{ _add_flag()
  856. /**
  857. * Adds a string to the flags array if the flag is not yet in there
  858. * - if there is no flag present the array is created
  859. *
  860. * @param array &$array the reference to the flag-array
  861. * @param string $value the flag value
  862. *
  863. * @return void
  864. *
  865. * @access private
  866. * @author Joern Barthel <j_barthel@web.de>
  867. */
  868. public function _add_flag(&$array, $value)
  869. {
  870. if (!is_array($array)) {
  871. $array = array($value);
  872. } elseif (!in_array($value, $array)) {
  873. array_push($array, $value);
  874. }
  875. }
  876. // }}}
  877. // {{{ getSpecialQuery()
  878. /**
  879. * Obtains the query string needed for listing a given type of objects
  880. *
  881. * @param string $type the kind of objects you want to retrieve
  882. *
  883. * @return string the SQL query string or null if the driver doesn't
  884. * support the object type requested
  885. *
  886. * @access protected
  887. * @see DB_common::getListOf()
  888. */
  889. public function getSpecialQuery($type)
  890. {
  891. switch ($type) {
  892. case 'tables':
  893. return "SELECT name FROM sysobjects WHERE type = 'U'"
  894. . ' ORDER BY name';
  895. case 'views':
  896. return "SELECT name FROM sysobjects WHERE type = 'V'";
  897. default:
  898. return null;
  899. }
  900. }
  901. // }}}
  902. }
  903. /*
  904. * Local variables:
  905. * tab-width: 4
  906. * c-basic-offset: 4
  907. * End:
  908. */