mssql.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Sterling Hughes <sterling@php.net> |
  17. // | Maintainer: Daniel Convissor <danielc@php.net> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: mssql.php,v 1.51 2004/02/18 23:07:10 danielc Exp $
  21. require_once 'DB/common.php';
  22. /**
  23. * Database independent query interface definition for PHP's Microsoft SQL Server
  24. * extension.
  25. *
  26. * @package DB
  27. * @version $Id: mssql.php,v 1.51 2004/02/18 23:07:10 danielc Exp $
  28. * @category Database
  29. * @author Sterling Hughes <sterling@php.net>
  30. */
  31. class DB_mssql extends DB_common
  32. {
  33. // {{{ properties
  34. var $connection;
  35. var $phptype, $dbsyntax;
  36. var $prepare_tokens = array();
  37. var $prepare_types = array();
  38. var $transaction_opcount = 0;
  39. var $autocommit = true;
  40. var $_db = null;
  41. // }}}
  42. // {{{ constructor
  43. function DB_mssql()
  44. {
  45. $this->DB_common();
  46. $this->phptype = 'mssql';
  47. $this->dbsyntax = 'mssql';
  48. $this->features = array(
  49. 'prepare' => false,
  50. 'pconnect' => true,
  51. 'transactions' => true,
  52. 'limit' => 'emulate'
  53. );
  54. // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
  55. $this->errorcode_map = array(
  56. 170 => DB_ERROR_SYNTAX,
  57. 207 => DB_ERROR_NOSUCHFIELD,
  58. 208 => DB_ERROR_NOSUCHTABLE,
  59. 245 => DB_ERROR_INVALID_NUMBER,
  60. 515 => DB_ERROR_CONSTRAINT_NOT_NULL,
  61. 547 => DB_ERROR_CONSTRAINT,
  62. 2627 => DB_ERROR_CONSTRAINT,
  63. 2714 => DB_ERROR_ALREADY_EXISTS,
  64. 3701 => DB_ERROR_NOSUCHTABLE,
  65. 8134 => DB_ERROR_DIVZERO,
  66. );
  67. }
  68. // }}}
  69. // {{{ connect()
  70. function connect($dsninfo, $persistent = false)
  71. {
  72. if (!DB::assertExtension('mssql') && !DB::assertExtension('sybase')
  73. && !DB::assertExtension('sybase_ct'))
  74. {
  75. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  76. }
  77. $this->dsn = $dsninfo;
  78. $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
  79. $dbhost .= $dsninfo['port'] ? ':' . $dsninfo['port'] : '';
  80. $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
  81. if ($dbhost && $dsninfo['username'] && $dsninfo['password']) {
  82. $conn = @$connect_function($dbhost, $dsninfo['username'],
  83. $dsninfo['password']);
  84. } elseif ($dbhost && $dsninfo['username']) {
  85. $conn = @$connect_function($dbhost, $dsninfo['username']);
  86. } else {
  87. $conn = @$connect_function($dbhost);
  88. }
  89. if (!$conn) {
  90. return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
  91. null, mssql_get_last_message());
  92. }
  93. if ($dsninfo['database']) {
  94. if (!@mssql_select_db($dsninfo['database'], $conn)) {
  95. return $this->raiseError(DB_ERROR_NODBSELECTED, null, null,
  96. null, mssql_get_last_message());
  97. }
  98. $this->_db = $dsninfo['database'];
  99. }
  100. $this->connection = $conn;
  101. return DB_OK;
  102. }
  103. // }}}
  104. // {{{ disconnect()
  105. function disconnect()
  106. {
  107. $ret = @mssql_close($this->connection);
  108. $this->connection = null;
  109. return $ret;
  110. }
  111. // }}}
  112. // {{{ simpleQuery()
  113. function simpleQuery($query)
  114. {
  115. $ismanip = DB::isManip($query);
  116. $this->last_query = $query;
  117. if (!@mssql_select_db($this->_db, $this->connection)) {
  118. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  119. }
  120. $query = $this->modifyQuery($query);
  121. if (!$this->autocommit && $ismanip) {
  122. if ($this->transaction_opcount == 0) {
  123. $result = @mssql_query('BEGIN TRAN', $this->connection);
  124. if (!$result) {
  125. return $this->mssqlRaiseError();
  126. }
  127. }
  128. $this->transaction_opcount++;
  129. }
  130. $result = @mssql_query($query, $this->connection);
  131. if (!$result) {
  132. return $this->mssqlRaiseError();
  133. }
  134. // Determine which queries that should return data, and which
  135. // should return an error code only.
  136. return $ismanip ? DB_OK : $result;
  137. }
  138. // }}}
  139. // {{{ nextResult()
  140. /**
  141. * Move the internal mssql result pointer to the next available result
  142. *
  143. * @param a valid fbsql result resource
  144. *
  145. * @access public
  146. *
  147. * @return true if a result is available otherwise return false
  148. */
  149. function nextResult($result)
  150. {
  151. return mssql_next_result($result);
  152. }
  153. // }}}
  154. // {{{ fetchInto()
  155. /**
  156. * Fetch a row and insert the data into an existing array.
  157. *
  158. * Formating of the array and the data therein are configurable.
  159. * See DB_result::fetchInto() for more information.
  160. *
  161. * @param resource $result query result identifier
  162. * @param array $arr (reference) array where data from the row
  163. * should be placed
  164. * @param int $fetchmode how the resulting array should be indexed
  165. * @param int $rownum the row number to fetch
  166. *
  167. * @return mixed DB_OK on success, NULL when end of result set is
  168. * reached or on failure
  169. *
  170. * @see DB_result::fetchInto()
  171. * @access private
  172. */
  173. function fetchInto($result, &$arr, $fetchmode, $rownum=null)
  174. {
  175. if ($rownum !== null) {
  176. if (!@mssql_data_seek($result, $rownum)) {
  177. return null;
  178. }
  179. }
  180. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  181. $arr = @mssql_fetch_array($result, MSSQL_ASSOC);
  182. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  183. $arr = array_change_key_case($arr, CASE_LOWER);
  184. }
  185. } else {
  186. $arr = @mssql_fetch_row($result);
  187. }
  188. if (!$arr) {
  189. /* This throws informative error messages,
  190. don't use it for now
  191. if ($msg = mssql_get_last_message()) {
  192. return $this->raiseError($msg);
  193. }
  194. */
  195. return null;
  196. }
  197. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  198. $this->_rtrimArrayValues($arr);
  199. }
  200. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  201. $this->_convertNullArrayValuesToEmpty($arr);
  202. }
  203. return DB_OK;
  204. }
  205. // }}}
  206. // {{{ freeResult()
  207. function freeResult($result)
  208. {
  209. return @mssql_free_result($result);
  210. }
  211. // }}}
  212. // {{{ numCols()
  213. function numCols($result)
  214. {
  215. $cols = @mssql_num_fields($result);
  216. if (!$cols) {
  217. return $this->mssqlRaiseError();
  218. }
  219. return $cols;
  220. }
  221. // }}}
  222. // {{{ numRows()
  223. function numRows($result)
  224. {
  225. $rows = @mssql_num_rows($result);
  226. if ($rows === false) {
  227. return $this->mssqlRaiseError();
  228. }
  229. return $rows;
  230. }
  231. // }}}
  232. // {{{ autoCommit()
  233. /**
  234. * Enable/disable automatic commits
  235. */
  236. function autoCommit($onoff = false)
  237. {
  238. // XXX if $this->transaction_opcount > 0, we should probably
  239. // issue a warning here.
  240. $this->autocommit = $onoff ? true : false;
  241. return DB_OK;
  242. }
  243. // }}}
  244. // {{{ commit()
  245. /**
  246. * Commit the current transaction.
  247. */
  248. function commit()
  249. {
  250. if ($this->transaction_opcount > 0) {
  251. if (!@mssql_select_db($this->_db, $this->connection)) {
  252. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  253. }
  254. $result = @mssql_query('COMMIT TRAN', $this->connection);
  255. $this->transaction_opcount = 0;
  256. if (!$result) {
  257. return $this->mssqlRaiseError();
  258. }
  259. }
  260. return DB_OK;
  261. }
  262. // }}}
  263. // {{{ rollback()
  264. /**
  265. * Roll back (undo) the current transaction.
  266. */
  267. function rollback()
  268. {
  269. if ($this->transaction_opcount > 0) {
  270. if (!@mssql_select_db($this->_db, $this->connection)) {
  271. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  272. }
  273. $result = @mssql_query('ROLLBACK TRAN', $this->connection);
  274. $this->transaction_opcount = 0;
  275. if (!$result) {
  276. return $this->mssqlRaiseError();
  277. }
  278. }
  279. return DB_OK;
  280. }
  281. // }}}
  282. // {{{ affectedRows()
  283. /**
  284. * Gets the number of rows affected by the last query.
  285. * if the last query was a select, returns 0.
  286. *
  287. * @return number of rows affected by the last query or DB_ERROR
  288. */
  289. function affectedRows()
  290. {
  291. if (DB::isManip($this->last_query)) {
  292. $res = @mssql_query('select @@rowcount', $this->connection);
  293. if (!$res) {
  294. return $this->mssqlRaiseError();
  295. }
  296. $ar = @mssql_fetch_row($res);
  297. if (!$ar) {
  298. $result = 0;
  299. } else {
  300. @mssql_free_result($res);
  301. $result = $ar[0];
  302. }
  303. } else {
  304. $result = 0;
  305. }
  306. return $result;
  307. }
  308. // }}}
  309. // {{{ nextId()
  310. /**
  311. * Returns the next free id in a sequence
  312. *
  313. * @param string $seq_name name of the sequence
  314. * @param boolean $ondemand when true, the seqence is automatically
  315. * created if it does not exist
  316. *
  317. * @return int the next id number in the sequence. DB_Error if problem.
  318. *
  319. * @internal
  320. * @see DB_common::nextID()
  321. * @access public
  322. */
  323. function nextId($seq_name, $ondemand = true)
  324. {
  325. $seqname = $this->getSequenceName($seq_name);
  326. if (!@mssql_select_db($this->_db, $this->connection)) {
  327. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  328. }
  329. $repeat = 0;
  330. do {
  331. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  332. $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
  333. $this->popErrorHandling();
  334. if ($ondemand && DB::isError($result) &&
  335. ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
  336. {
  337. $repeat = 1;
  338. $result = $this->createSequence($seq_name);
  339. if (DB::isError($result)) {
  340. return $this->raiseError($result);
  341. }
  342. } elseif (!DB::isError($result)) {
  343. $result =& $this->query("SELECT @@IDENTITY FROM $seqname");
  344. $repeat = 0;
  345. } else {
  346. $repeat = false;
  347. }
  348. } while ($repeat);
  349. if (DB::isError($result)) {
  350. return $this->raiseError($result);
  351. }
  352. $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
  353. return $result[0];
  354. }
  355. /**
  356. * Creates a new sequence
  357. *
  358. * @param string $seq_name name of the new sequence
  359. *
  360. * @return int DB_OK on success. A DB_Error object is returned if
  361. * problems arise.
  362. *
  363. * @internal
  364. * @see DB_common::createSequence()
  365. * @access public
  366. */
  367. function createSequence($seq_name)
  368. {
  369. $seqname = $this->getSequenceName($seq_name);
  370. return $this->query("CREATE TABLE $seqname ".
  371. '([id] [int] IDENTITY (1, 1) NOT NULL ,' .
  372. '[vapor] [int] NULL)');
  373. }
  374. // }}}
  375. // {{{ dropSequence()
  376. /**
  377. * Deletes a sequence
  378. *
  379. * @param string $seq_name name of the sequence to be deleted
  380. *
  381. * @return int DB_OK on success. DB_Error if problems.
  382. *
  383. * @internal
  384. * @see DB_common::dropSequence()
  385. * @access public
  386. */
  387. function dropSequence($seq_name)
  388. {
  389. $seqname = $this->getSequenceName($seq_name);
  390. return $this->query("DROP TABLE $seqname");
  391. }
  392. // }}}
  393. // {{{ errorNative()
  394. /**
  395. * Determine MS SQL Server error code by querying @@ERROR.
  396. *
  397. * @return mixed mssql's native error code or DB_ERROR if unknown.
  398. */
  399. function errorNative()
  400. {
  401. $res = mssql_query('select @@ERROR as ErrorCode', $this->connection);
  402. if (!$res) {
  403. return DB_ERROR;
  404. }
  405. $row = mssql_fetch_row($res);
  406. return $row[0];
  407. }
  408. // }}}
  409. // {{{ errorCode()
  410. /**
  411. * Determine PEAR::DB error code from mssql's native codes.
  412. *
  413. * If <var>$nativecode</var> isn't known yet, it will be looked up.
  414. *
  415. * @param mixed $nativecode mssql error code, if known
  416. * @return integer an error number from a DB error constant
  417. * @see errorNative()
  418. */
  419. function errorCode($nativecode = null)
  420. {
  421. if (!$nativecode) {
  422. $nativecode = $this->errorNative();
  423. }
  424. if (isset($this->errorcode_map[$nativecode])) {
  425. return $this->errorcode_map[$nativecode];
  426. } else {
  427. return DB_ERROR;
  428. }
  429. }
  430. // }}}
  431. // {{{ mssqlRaiseError()
  432. /**
  433. * Gather information about an error, then use that info to create a
  434. * DB error object and finally return that object.
  435. *
  436. * @param integer $code PEAR error number (usually a DB constant) if
  437. * manually raising an error
  438. * @return object DB error object
  439. * @see errorCode()
  440. * @see errorNative()
  441. * @see DB_common::raiseError()
  442. */
  443. function mssqlRaiseError($code = null)
  444. {
  445. $message = mssql_get_last_message();
  446. if (!$code) {
  447. $code = $this->errorNative();
  448. }
  449. return $this->raiseError($this->errorCode($code), null, null, null,
  450. "$code - $message");
  451. }
  452. // }}}
  453. // {{{ tableInfo()
  454. /**
  455. * Returns information about a table or a result set.
  456. *
  457. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  458. * is a table name.
  459. *
  460. * @param object|string $result DB_result object from a query or a
  461. * string containing the name of a table
  462. * @param int $mode a valid tableInfo mode
  463. * @return array an associative array with the information requested
  464. * or an error object if something is wrong
  465. * @access public
  466. * @internal
  467. * @see DB_common::tableInfo()
  468. */
  469. function tableInfo($result, $mode = null)
  470. {
  471. if (isset($result->result)) {
  472. /*
  473. * Probably received a result object.
  474. * Extract the result resource identifier.
  475. */
  476. $id = $result->result;
  477. $got_string = false;
  478. } elseif (is_string($result)) {
  479. /*
  480. * Probably received a table name.
  481. * Create a result resource identifier.
  482. */
  483. if (!@mssql_select_db($this->_db, $this->connection)) {
  484. return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
  485. }
  486. $id = @mssql_query("SELECT * FROM $result WHERE 1=0",
  487. $this->connection);
  488. $got_string = true;
  489. } else {
  490. /*
  491. * Probably received a result resource identifier.
  492. * Copy it.
  493. * Depricated. Here for compatibility only.
  494. */
  495. $id = $result;
  496. $got_string = false;
  497. }
  498. if (!is_resource($id)) {
  499. return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  500. }
  501. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  502. $case_func = 'strtolower';
  503. } else {
  504. $case_func = 'strval';
  505. }
  506. $count = @mssql_num_fields($id);
  507. // made this IF due to performance (one if is faster than $count if's)
  508. if (!$mode) {
  509. for ($i=0; $i<$count; $i++) {
  510. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  511. $res[$i]['name'] = $case_func(@mssql_field_name($id, $i));
  512. $res[$i]['type'] = @mssql_field_type($id, $i);
  513. $res[$i]['len'] = @mssql_field_length($id, $i);
  514. // We only support flags for tables
  515. $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : '';
  516. }
  517. } else { // full
  518. $res['num_fields']= $count;
  519. for ($i=0; $i<$count; $i++) {
  520. $res[$i]['table'] = $got_string ? $case_func($result) : '';
  521. $res[$i]['name'] = $case_func(@mssql_field_name($id, $i));
  522. $res[$i]['type'] = @mssql_field_type($id, $i);
  523. $res[$i]['len'] = @mssql_field_length($id, $i);
  524. // We only support flags for tables
  525. $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : '';
  526. if ($mode & DB_TABLEINFO_ORDER) {
  527. $res['order'][$res[$i]['name']] = $i;
  528. }
  529. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  530. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  531. }
  532. }
  533. }
  534. // free the result only if we were called on a table
  535. if ($got_string) {
  536. @mssql_free_result($id);
  537. }
  538. return $res;
  539. }
  540. // }}}
  541. // {{{ getSpecialQuery()
  542. /**
  543. * Returns the query needed to get some backend info
  544. * @param string $type What kind of info you want to retrieve
  545. * @return string The SQL query string
  546. */
  547. function getSpecialQuery($type)
  548. {
  549. switch ($type) {
  550. case 'tables':
  551. return "select name from sysobjects where type = 'U' order by name";
  552. case 'views':
  553. return "select name from sysobjects where type = 'V'";
  554. default:
  555. return null;
  556. }
  557. }
  558. // }}}
  559. // {{{ _mssql_field_flags()
  560. /**
  561. * Get the flags for a field, currently supports "not_null", "primary_key",
  562. * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
  563. * "unique_key" (mssql unique index, unique check or primary_key) and
  564. * "multiple_key" (multikey index)
  565. *
  566. * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
  567. * not useful at all - is the behaviour of mysql_field_flags that primary
  568. * keys are alway unique? is the interpretation of multiple_key correct?
  569. *
  570. * @param string The table name
  571. * @param string The field
  572. * @author Joern Barthel <j_barthel@web.de>
  573. * @access private
  574. */
  575. function _mssql_field_flags($table, $column)
  576. {
  577. static $tableName = null;
  578. static $flags = array();
  579. if ($table != $tableName) {
  580. $flags = array();
  581. $tableName = $table;
  582. // get unique and primary keys
  583. $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC);
  584. foreach ($res as $val) {
  585. $keys = explode(', ', $val['index_keys']);
  586. if (sizeof($keys) > 1) {
  587. foreach ($keys as $key) {
  588. $this->_add_flag($flags[$key], 'multiple_key');
  589. }
  590. }
  591. if (strpos($val['index_description'], 'primary key')) {
  592. foreach ($keys as $key) {
  593. $this->_add_flag($flags[$key], 'primary_key');
  594. }
  595. } elseif (strpos($val['index_description'], 'unique')) {
  596. foreach ($keys as $key) {
  597. $this->_add_flag($flags[$key], 'unique_key');
  598. }
  599. }
  600. }
  601. // get auto_increment, not_null and timestamp
  602. $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC);
  603. foreach ($res as $val) {
  604. $val = array_change_key_case($val, CASE_LOWER);
  605. if ($val['nullable'] == '0') {
  606. $this->_add_flag($flags[$val['column_name']], 'not_null');
  607. }
  608. if (strpos($val['type_name'], 'identity')) {
  609. $this->_add_flag($flags[$val['column_name']], 'auto_increment');
  610. }
  611. if (strpos($val['type_name'], 'timestamp')) {
  612. $this->_add_flag($flags[$val['column_name']], 'timestamp');
  613. }
  614. }
  615. }
  616. if (array_key_exists($column, $flags)) {
  617. return(implode(' ', $flags[$column]));
  618. }
  619. return '';
  620. }
  621. // }}}
  622. // {{{ _add_flag()
  623. /**
  624. * Adds a string to the flags array if the flag is not yet in there
  625. * - if there is no flag present the array is created.
  626. *
  627. * @param reference Reference to the flag-array
  628. * @param value The flag value
  629. * @access private
  630. * @author Joern Barthel <j_barthel@web.de>
  631. */
  632. function _add_flag (&$array, $value)
  633. {
  634. if (!is_array($array)) {
  635. $array = array($value);
  636. } elseif (!in_array($value, $array)) {
  637. array_push($array, $value);
  638. }
  639. }
  640. // }}}
  641. // {{{ quoteIdentifier()
  642. /**
  643. * Quote a string so it can be safely used as a table / column name
  644. *
  645. * Quoting style depends on which database driver is being used.
  646. *
  647. * @param string $str identifier name to be quoted
  648. *
  649. * @return string quoted identifier string
  650. *
  651. * @since 1.6.0
  652. * @access public
  653. */
  654. function quoteIdentifier($str)
  655. {
  656. return '[' . str_replace(']', ']]', $str) . ']';
  657. }
  658. // }}}
  659. }
  660. /*
  661. * Local variables:
  662. * tab-width: 4
  663. * c-basic-offset: 4
  664. * End:
  665. */
  666. ?>