PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/standard/branches/release-1.0/library/Zend/Db/Adapter/Pdo/Mssql.php

https://github.com/bhaumik25/zend-framework
PHP | 326 lines | 164 code | 28 blank | 134 comment | 14 complexity | 6517cf451dc0bdea9cab3b44dbd21275 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Db
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_Db_Adapter_Pdo_Abstract
  24. */
  25. require_once 'Zend/Db/Adapter/Pdo/Abstract.php';
  26. /**
  27. * Class for connecting to Microsoft SQL Server databases and performing common operations.
  28. *
  29. * @category Zend
  30. * @package Zend_Db
  31. * @subpackage Adapter
  32. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Db_Adapter_Pdo_Mssql extends Zend_Db_Adapter_Pdo_Abstract
  36. {
  37. /**
  38. * PDO type.
  39. *
  40. * @var string
  41. */
  42. protected $_pdoType = 'mssql';
  43. /**
  44. * Keys are UPPERCASE SQL datatypes or the constants
  45. * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
  46. *
  47. * Values are:
  48. * 0 = 32-bit integer
  49. * 1 = 64-bit integer
  50. * 2 = float or decimal
  51. *
  52. * @var array Associative array of datatypes to values 0, 1, or 2.
  53. */
  54. protected $_numericDataTypes = array(
  55. Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
  56. Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
  57. Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
  58. 'INT' => Zend_Db::INT_TYPE,
  59. 'SMALLINT' => Zend_Db::INT_TYPE,
  60. 'TINYINT' => Zend_Db::INT_TYPE,
  61. 'BIGINT' => Zend_Db::BIGINT_TYPE,
  62. 'DECIMAL' => Zend_Db::FLOAT_TYPE,
  63. 'FLOAT' => Zend_Db::FLOAT_TYPE,
  64. 'MONEY' => Zend_Db::FLOAT_TYPE,
  65. 'NUMERIC' => Zend_Db::FLOAT_TYPE,
  66. 'REAL' => Zend_Db::FLOAT_TYPE,
  67. 'SMALLMONEY' => Zend_Db::FLOAT_TYPE
  68. );
  69. /**
  70. * Creates a PDO DSN for the adapter from $this->_config settings.
  71. *
  72. * @return string
  73. */
  74. protected function _dsn()
  75. {
  76. // baseline of DSN parts
  77. $dsn = $this->_config;
  78. // don't pass the username and password in the DSN
  79. unset($dsn['username']);
  80. unset($dsn['password']);
  81. unset($dsn['driver_options']);
  82. if (isset($dsn['port'])) {
  83. $seperator = ':';
  84. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
  85. $seperator = ',';
  86. }
  87. $dsn['host'] .= $seperator . $dsn['port'];
  88. unset($dsn['port']);
  89. }
  90. // this driver supports multiple DSN prefixes
  91. // @see http://www.php.net/manual/en/ref.pdo-dblib.connection.php
  92. if (isset($dsn['pdoType'])) {
  93. switch (strtolower($dsn['pdoType'])) {
  94. case 'freetds':
  95. case 'sybase':
  96. $this->_pdoType = 'sybase';
  97. break;
  98. case 'mssql':
  99. $this->_pdoType = 'mssql';
  100. break;
  101. case 'dblib':
  102. default:
  103. $this->_pdoType = 'dblib';
  104. break;
  105. }
  106. unset($dsn['pdoType']);
  107. }
  108. // use all remaining parts in the DSN
  109. foreach ($dsn as $key => $val) {
  110. $dsn[$key] = "$key=$val";
  111. }
  112. $dsn = $this->_pdoType . ':' . implode(';', $dsn);
  113. return $dsn;
  114. }
  115. /**
  116. * @return void
  117. */
  118. protected function _connect()
  119. {
  120. if ($this->_connection) {
  121. return;
  122. }
  123. parent::_connect();
  124. $this->_connection->exec('SET QUOTED_IDENTIFIER ON');
  125. }
  126. /**
  127. * Returns a list of the tables in the database.
  128. *
  129. * @return array
  130. */
  131. public function listTables()
  132. {
  133. $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
  134. return $this->fetchCol($sql);
  135. }
  136. /**
  137. * Returns the column descriptions for a table.
  138. *
  139. * The return value is an associative array keyed by the column name,
  140. * as returned by the RDBMS.
  141. *
  142. * The value of each array element is an associative array
  143. * with the following keys:
  144. *
  145. * SCHEMA_NAME => string; name of database or schema
  146. * TABLE_NAME => string;
  147. * COLUMN_NAME => string; column name
  148. * COLUMN_POSITION => number; ordinal position of column in table
  149. * DATA_TYPE => string; SQL datatype name of column
  150. * DEFAULT => string; default expression of column, null if none
  151. * NULLABLE => boolean; true if column can have nulls
  152. * LENGTH => number; length of CHAR/VARCHAR
  153. * SCALE => number; scale of NUMERIC/DECIMAL
  154. * PRECISION => number; precision of NUMERIC/DECIMAL
  155. * UNSIGNED => boolean; unsigned property of an integer type
  156. * PRIMARY => boolean; true if column is part of the primary key
  157. * PRIMARY_POSITION => integer; position of column in primary key
  158. * PRIMARY_AUTO => integer; position of auto-generated column in primary key
  159. *
  160. * @todo Discover column primary key position.
  161. * @todo Discover integer unsigned property.
  162. *
  163. * @param string $tableName
  164. * @param string $schemaName OPTIONAL
  165. * @return array
  166. */
  167. public function describeTable($tableName, $schemaName = null)
  168. {
  169. /**
  170. * Discover metadata information about this table.
  171. */
  172. $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true);
  173. $stmt = $this->query($sql);
  174. $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
  175. $table_name = 2;
  176. $column_name = 3;
  177. $type_name = 5;
  178. $precision = 6;
  179. $length = 7;
  180. $scale = 8;
  181. $nullable = 10;
  182. $column_def = 12;
  183. $column_position = 16;
  184. /**
  185. * Discover primary key column(s) for this table.
  186. */
  187. $sql = "exec sp_pkeys @table_name = " . $this->quoteIdentifier($tableName, true);
  188. $stmt = $this->query($sql);
  189. $primaryKeysResult = $stmt->fetchAll(Zend_Db::FETCH_NUM);
  190. $primaryKeyColumn = array();
  191. $pkey_column_name = 3;
  192. $pkey_key_seq = 4;
  193. foreach ($primaryKeysResult as $pkeysRow) {
  194. $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq];
  195. }
  196. $desc = array();
  197. $p = 1;
  198. foreach ($result as $key => $row) {
  199. $identity = false;
  200. $words = explode(' ', $row[$type_name], 2);
  201. if (isset($words[0])) {
  202. $type = $words[0];
  203. if (isset($words[1])) {
  204. $identity = (bool) preg_match('/identity/', $words[1]);
  205. }
  206. }
  207. $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn);
  208. if ($isPrimary) {
  209. $primaryPosition = $primaryKeyColumn[$row[$column_name]];
  210. } else {
  211. $primaryPosition = null;
  212. }
  213. $desc[$this->foldCase($row[$column_name])] = array(
  214. 'SCHEMA_NAME' => null, // @todo
  215. 'TABLE_NAME' => $this->foldCase($row[$table_name]),
  216. 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
  217. 'COLUMN_POSITION' => (int) $row[$column_position],
  218. 'DATA_TYPE' => $type,
  219. 'DEFAULT' => $row[$column_def],
  220. 'NULLABLE' => (bool) $row[$nullable],
  221. 'LENGTH' => $row[$length],
  222. 'SCALE' => $row[$scale],
  223. 'PRECISION' => $row[$precision],
  224. 'UNSIGNED' => null, // @todo
  225. 'PRIMARY' => $isPrimary,
  226. 'PRIMARY_POSITION' => $primaryPosition,
  227. 'IDENTITY' => $identity
  228. );
  229. }
  230. return $desc;
  231. }
  232. /**
  233. * Adds an adapter-specific LIMIT clause to the SELECT statement.
  234. *
  235. * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html
  236. *
  237. * @param string $sql
  238. * @param integer $count
  239. * @param integer $offset OPTIONAL
  240. * @throws Zend_Db_Adapter_Exception
  241. * @return string
  242. */
  243. public function limit($sql, $count, $offset = 0)
  244. {
  245. $count = intval($count);
  246. if ($count <= 0) {
  247. /** @see Zend_Db_Adapter_Exception */
  248. require_once 'Zend/Db/Adapter/Exception.php';
  249. throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
  250. }
  251. $offset = intval($offset);
  252. if ($offset < 0) {
  253. /** @see Zend_Db_Adapter_Exception */
  254. require_once 'Zend/Db/Adapter/Exception.php';
  255. throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
  256. }
  257. $orderby = stristr($sql, 'ORDER BY');
  258. if ($orderby !== false) {
  259. $sort = (stripos($orderby, ' desc') !== false) ? 'desc' : 'asc';
  260. $order = str_ireplace('ORDER BY', '', $orderby);
  261. $order = trim(preg_replace('/\bASC\b|\bDESC\b/i', '', $order));
  262. }
  263. $sql = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . ($count+$offset) . ' ', $sql);
  264. $sql = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $sql . ') AS inner_tbl';
  265. if ($orderby !== false) {
  266. $sql .= ' ORDER BY ' . $order . ' ';
  267. $sql .= (stripos($sort, 'asc') !== false) ? 'DESC' : 'ASC';
  268. }
  269. $sql .= ') AS outer_tbl';
  270. if ($orderby !== false) {
  271. $sql .= ' ORDER BY ' . $order . ' ' . $sort;
  272. }
  273. return $sql;
  274. }
  275. /**
  276. * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
  277. *
  278. * As a convention, on RDBMS brands that support sequences
  279. * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
  280. * from the arguments and returns the last id generated by that sequence.
  281. * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
  282. * returns the last value generated for such a column, and the table name
  283. * argument is disregarded.
  284. *
  285. * Microsoft SQL Server does not support sequences, so the arguments to
  286. * this method are ignored.
  287. *
  288. * @param string $tableName OPTIONAL Name of table.
  289. * @param string $primaryKey OPTIONAL Name of primary key column.
  290. * @return string
  291. * @throws Zend_Db_Adapter_Exception
  292. */
  293. public function lastInsertId($tableName = null, $primaryKey = null)
  294. {
  295. $sql = 'SELECT SCOPE_IDENTITY()';
  296. return (int)$this->fetchOne($sql);
  297. }
  298. }