PageRenderTime 32ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/libs/model/datasources/dbo/dbo_mssql.php

http://github.com/Datawalke/Coordino
PHP | 792 lines | 516 code | 55 blank | 221 comment | 112 complexity | 2079519cd1456fe6d1c02d69d3c9080b MD5 | raw file
  1. <?php
  2. /**
  3. * MS SQL layer for DBO
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package cake
  16. * @subpackage cake.cake.libs.model.datasources.dbo
  17. * @since CakePHP(tm) v 0.10.5.1790
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. /**
  21. * MS SQL layer for DBO
  22. *
  23. * Long description for class
  24. *
  25. * @package cake
  26. * @subpackage cake.cake.libs.model.datasources.dbo
  27. */
  28. class DboMssql extends DboSource {
  29. /**
  30. * Driver description
  31. *
  32. * @var string
  33. */
  34. var $description = "MS SQL DBO Driver";
  35. /**
  36. * Starting quote character for quoted identifiers
  37. *
  38. * @var string
  39. */
  40. var $startQuote = "[";
  41. /**
  42. * Ending quote character for quoted identifiers
  43. *
  44. * @var string
  45. */
  46. var $endQuote = "]";
  47. /**
  48. * Creates a map between field aliases and numeric indexes. Workaround for the
  49. * SQL Server driver's 30-character column name limitation.
  50. *
  51. * @var array
  52. */
  53. var $__fieldMappings = array();
  54. /**
  55. * Base configuration settings for MS SQL driver
  56. *
  57. * @var array
  58. */
  59. var $_baseConfig = array(
  60. 'persistent' => true,
  61. 'host' => 'localhost',
  62. 'login' => 'root',
  63. 'password' => '',
  64. 'database' => 'cake',
  65. 'port' => '1433',
  66. );
  67. /**
  68. * MS SQL column definition
  69. *
  70. * @var array
  71. */
  72. var $columns = array(
  73. 'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
  74. 'string' => array('name' => 'varchar', 'limit' => '255'),
  75. 'text' => array('name' => 'text'),
  76. 'integer' => array('name' => 'int', 'formatter' => 'intval'),
  77. 'float' => array('name' => 'numeric', 'formatter' => 'floatval'),
  78. 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
  79. 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
  80. 'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
  81. 'date' => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
  82. 'binary' => array('name' => 'image'),
  83. 'boolean' => array('name' => 'bit')
  84. );
  85. /**
  86. * Index of basic SQL commands
  87. *
  88. * @var array
  89. * @access protected
  90. */
  91. var $_commands = array(
  92. 'begin' => 'BEGIN TRANSACTION',
  93. 'commit' => 'COMMIT',
  94. 'rollback' => 'ROLLBACK'
  95. );
  96. /**
  97. * Define if the last query had error
  98. *
  99. * @var string
  100. * @access private
  101. */
  102. var $__lastQueryHadError = false;
  103. /**
  104. * MS SQL DBO driver constructor; sets SQL Server error reporting defaults
  105. *
  106. * @param array $config Configuration data from app/config/databases.php
  107. * @return boolean True if connected successfully, false on error
  108. */
  109. function __construct($config, $autoConnect = true) {
  110. if ($autoConnect) {
  111. if (!function_exists('mssql_min_message_severity')) {
  112. trigger_error(__("PHP SQL Server interface is not installed, cannot continue. For troubleshooting information, see http://php.net/mssql/", true), E_USER_WARNING);
  113. }
  114. mssql_min_message_severity(15);
  115. mssql_min_error_severity(2);
  116. }
  117. return parent::__construct($config, $autoConnect);
  118. }
  119. /**
  120. * Connects to the database using options in the given configuration array.
  121. *
  122. * @return boolean True if the database could be connected, else false
  123. */
  124. function connect() {
  125. $config = $this->config;
  126. $os = env('OS');
  127. if (!empty($os) && strpos($os, 'Windows') !== false) {
  128. $sep = ',';
  129. } else {
  130. $sep = ':';
  131. }
  132. $this->connected = false;
  133. if (is_numeric($config['port'])) {
  134. $port = $sep . $config['port']; // Port number
  135. } elseif ($config['port'] === null) {
  136. $port = ''; // No port - SQL Server 2005
  137. } else {
  138. $port = '\\' . $config['port']; // Named pipe
  139. }
  140. if (!$config['persistent']) {
  141. $this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true);
  142. } else {
  143. $this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']);
  144. }
  145. if (mssql_select_db($config['database'], $this->connection)) {
  146. $this->_execute("SET DATEFORMAT ymd");
  147. $this->connected = true;
  148. }
  149. return $this->connected;
  150. }
  151. /**
  152. * Check that MsSQL is installed/loaded
  153. *
  154. * @return boolean
  155. */
  156. function enabled() {
  157. return extension_loaded('mssql');
  158. }
  159. /**
  160. * Disconnects from database.
  161. *
  162. * @return boolean True if the database could be disconnected, else false
  163. */
  164. function disconnect() {
  165. @mssql_free_result($this->results);
  166. $this->connected = !@mssql_close($this->connection);
  167. return !$this->connected;
  168. }
  169. /**
  170. * Executes given SQL statement.
  171. *
  172. * @param string $sql SQL statement
  173. * @return resource Result resource identifier
  174. * @access protected
  175. */
  176. function _execute($sql) {
  177. $result = @mssql_query($sql, $this->connection);
  178. $this->__lastQueryHadError = ($result === false);
  179. return $result;
  180. }
  181. /**
  182. * Returns an array of sources (tables) in the database.
  183. *
  184. * @return array Array of tablenames in the database
  185. */
  186. function listSources() {
  187. $cache = parent::listSources();
  188. if ($cache != null) {
  189. return $cache;
  190. }
  191. $result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES', false);
  192. if (!$result || empty($result)) {
  193. return array();
  194. } else {
  195. $tables = array();
  196. foreach ($result as $table) {
  197. $tables[] = $table[0]['TABLE_NAME'];
  198. }
  199. parent::listSources($tables);
  200. return $tables;
  201. }
  202. }
  203. /**
  204. * Returns an array of the fields in given table name.
  205. *
  206. * @param Model $model Model object to describe
  207. * @return array Fields in table. Keys are name and type
  208. */
  209. function describe(&$model) {
  210. $cache = parent::describe($model);
  211. if ($cache != null) {
  212. return $cache;
  213. }
  214. $table = $this->fullTableName($model, false);
  215. $cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'", false);
  216. $fields = false;
  217. foreach ($cols as $column) {
  218. $field = $column[0]['Field'];
  219. $fields[$field] = array(
  220. 'type' => $this->column($column[0]['Type']),
  221. 'null' => (strtoupper($column[0]['Null']) == 'YES'),
  222. 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column[0]['Default']),
  223. 'length' => intval($column[0]['Length']),
  224. 'key' => ($column[0]['Key'] == '1') ? 'primary' : false
  225. );
  226. if ($fields[$field]['default'] === 'null') {
  227. $fields[$field]['default'] = null;
  228. } else {
  229. $this->value($fields[$field]['default'], $fields[$field]['type']);
  230. }
  231. if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') {
  232. $fields[$field]['length'] = 11;
  233. } elseif (!$fields[$field]['key']) {
  234. unset($fields[$field]['key']);
  235. }
  236. if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) {
  237. $fields[$field]['length'] = null;
  238. }
  239. }
  240. $this->__cacheDescription($this->fullTableName($model, false), $fields);
  241. return $fields;
  242. }
  243. /**
  244. * Returns a quoted and escaped string of $data for use in an SQL statement.
  245. *
  246. * @param string $data String to be prepared for use in an SQL statement
  247. * @param string $column The column into which this data will be inserted
  248. * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
  249. * @return string Quoted and escaped data
  250. */
  251. function value($data, $column = null, $safe = false) {
  252. $parent = parent::value($data, $column, $safe);
  253. if ($parent != null) {
  254. return $parent;
  255. }
  256. if ($data === null) {
  257. return 'NULL';
  258. }
  259. if (in_array($column, array('integer', 'float', 'binary')) && $data === '') {
  260. return 'NULL';
  261. }
  262. if ($data === '') {
  263. return "''";
  264. }
  265. switch ($column) {
  266. case 'boolean':
  267. $data = $this->boolean((bool)$data);
  268. break;
  269. default:
  270. if (get_magic_quotes_gpc()) {
  271. $data = stripslashes(str_replace("'", "''", $data));
  272. } else {
  273. $data = str_replace("'", "''", $data);
  274. }
  275. break;
  276. }
  277. if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) {
  278. return $data;
  279. }
  280. return "'" . $data . "'";
  281. }
  282. /**
  283. * Generates the fields list of an SQL query.
  284. *
  285. * @param Model $model
  286. * @param string $alias Alias tablename
  287. * @param mixed $fields
  288. * @return array
  289. */
  290. function fields(&$model, $alias = null, $fields = array(), $quote = true) {
  291. if (empty($alias)) {
  292. $alias = $model->alias;
  293. }
  294. $fields = parent::fields($model, $alias, $fields, false);
  295. $count = count($fields);
  296. if (
  297. $count >= 1 &&
  298. strpos($fields[0], 'COUNT(*)') === false &&
  299. strpos($fields[0], 'COUNT(DISTINCT') === false
  300. ) {
  301. $result = array();
  302. for ($i = 0; $i < $count; $i++) {
  303. $prepend = '';
  304. if (strpos($fields[$i], 'DISTINCT') !== false) {
  305. $prepend = 'DISTINCT ';
  306. $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
  307. }
  308. $fieldAlias = count($this->__fieldMappings);
  309. if (!preg_match('/\s+AS\s+/i', $fields[$i])) {
  310. if (substr($fields[$i], -1) == '*') {
  311. if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
  312. $build = explode('.', $fields[$i]);
  313. $AssociatedModel = $model->{$build[0]};
  314. } else {
  315. $AssociatedModel = $model;
  316. }
  317. $_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
  318. $result = array_merge($result, $_fields);
  319. continue;
  320. }
  321. if (strpos($fields[$i], '.') === false) {
  322. $this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i];
  323. $fieldName = $this->name($alias . '.' . $fields[$i]);
  324. $fieldAlias = $this->name($alias . '__' . $fieldAlias);
  325. } else {
  326. $build = explode('.', $fields[$i]);
  327. $this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i];
  328. $fieldName = $this->name($build[0] . '.' . $build[1]);
  329. $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias);
  330. }
  331. if ($model->getColumnType($fields[$i]) == 'datetime') {
  332. $fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)";
  333. }
  334. $fields[$i] = "{$fieldName} AS {$fieldAlias}";
  335. }
  336. $result[] = $prepend . $fields[$i];
  337. }
  338. return $result;
  339. } else {
  340. return $fields;
  341. }
  342. }
  343. /**
  344. * Generates and executes an SQL INSERT statement for given model, fields, and values.
  345. * Removes Identity (primary key) column from update data before returning to parent, if
  346. * value is empty.
  347. *
  348. * @param Model $model
  349. * @param array $fields
  350. * @param array $values
  351. * @param mixed $conditions
  352. * @return array
  353. */
  354. function create(&$model, $fields = null, $values = null) {
  355. if (!empty($values)) {
  356. $fields = array_combine($fields, $values);
  357. }
  358. $primaryKey = $this->_getPrimaryKey($model);
  359. if (array_key_exists($primaryKey, $fields)) {
  360. if (empty($fields[$primaryKey])) {
  361. unset($fields[$primaryKey]);
  362. } else {
  363. $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' ON');
  364. }
  365. }
  366. $result = parent::create($model, array_keys($fields), array_values($fields));
  367. if (array_key_exists($primaryKey, $fields) && !empty($fields[$primaryKey])) {
  368. $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' OFF');
  369. }
  370. return $result;
  371. }
  372. /**
  373. * Generates and executes an SQL UPDATE statement for given model, fields, and values.
  374. * Removes Identity (primary key) column from update data before returning to parent.
  375. *
  376. * @param Model $model
  377. * @param array $fields
  378. * @param array $values
  379. * @param mixed $conditions
  380. * @return array
  381. */
  382. function update(&$model, $fields = array(), $values = null, $conditions = null) {
  383. if (!empty($values)) {
  384. $fields = array_combine($fields, $values);
  385. }
  386. if (isset($fields[$model->primaryKey])) {
  387. unset($fields[$model->primaryKey]);
  388. }
  389. if (empty($fields)) {
  390. return true;
  391. }
  392. return parent::update($model, array_keys($fields), array_values($fields), $conditions);
  393. }
  394. /**
  395. * Returns a formatted error message from previous database operation.
  396. *
  397. * @return string Error message with error number
  398. */
  399. function lastError() {
  400. if ($this->__lastQueryHadError) {
  401. $error = mssql_get_last_message();
  402. if ($error && !preg_match('/contexto de la base de datos a|contesto di database|changed database|contexte de la base de don|datenbankkontext/i', $error)) {
  403. return $error;
  404. }
  405. }
  406. return null;
  407. }
  408. /**
  409. * Returns number of affected rows in previous database operation. If no previous operation exists,
  410. * this returns false.
  411. *
  412. * @return integer Number of affected rows
  413. */
  414. function lastAffected() {
  415. if ($this->_result) {
  416. return mssql_rows_affected($this->connection);
  417. }
  418. return null;
  419. }
  420. /**
  421. * Returns number of rows in previous resultset. If no previous resultset exists,
  422. * this returns false.
  423. *
  424. * @return integer Number of rows in resultset
  425. */
  426. function lastNumRows() {
  427. if ($this->_result) {
  428. return @mssql_num_rows($this->_result);
  429. }
  430. return null;
  431. }
  432. /**
  433. * Returns the ID generated from the previous INSERT operation.
  434. *
  435. * @param unknown_type $source
  436. * @return in
  437. */
  438. function lastInsertId($source = null) {
  439. $id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false);
  440. return $id[0]['insertID'];
  441. }
  442. /**
  443. * Returns a limit statement in the correct format for the particular database.
  444. *
  445. * @param integer $limit Limit of results returned
  446. * @param integer $offset Offset from which to start results
  447. * @return string SQL limit/offset statement
  448. */
  449. function limit($limit, $offset = null) {
  450. if ($limit) {
  451. $rt = '';
  452. if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
  453. $rt = ' TOP';
  454. }
  455. $rt .= ' ' . $limit;
  456. if (is_int($offset) && $offset > 0) {
  457. $rt .= ' OFFSET ' . $offset;
  458. }
  459. return $rt;
  460. }
  461. return null;
  462. }
  463. /**
  464. * Converts database-layer column types to basic types
  465. *
  466. * @param string $real Real database-layer column type (i.e. "varchar(255)")
  467. * @return string Abstract column type (i.e. "string")
  468. */
  469. function column($real) {
  470. if (is_array($real)) {
  471. $col = $real['name'];
  472. if (isset($real['limit'])) {
  473. $col .= '(' . $real['limit'] . ')';
  474. }
  475. return $col;
  476. }
  477. $col = str_replace(')', '', $real);
  478. $limit = null;
  479. if (strpos($col, '(') !== false) {
  480. list($col, $limit) = explode('(', $col);
  481. }
  482. if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
  483. return $col;
  484. }
  485. if ($col == 'bit') {
  486. return 'boolean';
  487. }
  488. if (strpos($col, 'int') !== false) {
  489. return 'integer';
  490. }
  491. if (strpos($col, 'char') !== false) {
  492. return 'string';
  493. }
  494. if (strpos($col, 'text') !== false) {
  495. return 'text';
  496. }
  497. if (strpos($col, 'binary') !== false || $col == 'image') {
  498. return 'binary';
  499. }
  500. if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) {
  501. return 'float';
  502. }
  503. return 'text';
  504. }
  505. /**
  506. * Enter description here...
  507. *
  508. * @param unknown_type $results
  509. */
  510. function resultSet(&$results) {
  511. $this->results =& $results;
  512. $this->map = array();
  513. $numFields = mssql_num_fields($results);
  514. $index = 0;
  515. $j = 0;
  516. while ($j < $numFields) {
  517. $column = mssql_field_name($results, $j);
  518. if (strpos($column, '__')) {
  519. if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) {
  520. $map = explode('.', $this->__fieldMappings[$column]);
  521. } elseif (isset($this->__fieldMappings[$column])) {
  522. $map = array(0, $this->__fieldMappings[$column]);
  523. } else {
  524. $map = array(0, $column);
  525. }
  526. $this->map[$index++] = $map;
  527. } else {
  528. $this->map[$index++] = array(0, $column);
  529. }
  530. $j++;
  531. }
  532. }
  533. /**
  534. * Builds final SQL statement
  535. *
  536. * @param string $type Query type
  537. * @param array $data Query data
  538. * @return string
  539. */
  540. function renderStatement($type, $data) {
  541. switch (strtolower($type)) {
  542. case 'select':
  543. extract($data);
  544. $fields = trim($fields);
  545. if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) {
  546. $limit = 'DISTINCT ' . trim($limit);
  547. $fields = substr($fields, 9);
  548. }
  549. if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
  550. $limit = preg_replace('/\s*offset.*$/i', '', $limit);
  551. preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
  552. $offset = intval($offset[1]) + intval($limitVal[1]);
  553. $rOrder = $this->__switchSort($order);
  554. list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
  555. return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
  556. } else {
  557. return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}";
  558. }
  559. break;
  560. case "schema":
  561. extract($data);
  562. foreach ($indexes as $i => $index) {
  563. if (preg_match('/PRIMARY KEY/', $index)) {
  564. unset($indexes[$i]);
  565. break;
  566. }
  567. }
  568. foreach (array('columns', 'indexes') as $var) {
  569. if (is_array(${$var})) {
  570. ${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
  571. }
  572. }
  573. return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
  574. break;
  575. default:
  576. return parent::renderStatement($type, $data);
  577. break;
  578. }
  579. }
  580. /**
  581. * Reverses the sort direction of ORDER statements to get paging offsets to work correctly
  582. *
  583. * @param string $order
  584. * @return string
  585. * @access private
  586. */
  587. function __switchSort($order) {
  588. $order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order);
  589. $order = preg_replace('/\s+DESC/i', ' ASC', $order);
  590. return preg_replace('/__tmp_asc__/', ' DESC', $order);
  591. }
  592. /**
  593. * Translates field names used for filtering and sorting to shortened names using the field map
  594. *
  595. * @param string $sql A snippet of SQL representing an ORDER or WHERE statement
  596. * @return string The value of $sql with field names replaced
  597. * @access private
  598. */
  599. function __mapFields($sql) {
  600. if (empty($sql) || empty($this->__fieldMappings)) {
  601. return $sql;
  602. }
  603. foreach ($this->__fieldMappings as $key => $val) {
  604. $sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql);
  605. $sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql);
  606. }
  607. return $sql;
  608. }
  609. /**
  610. * Returns an array of all result rows for a given SQL query.
  611. * Returns false if no rows matched.
  612. *
  613. * @param string $sql SQL statement
  614. * @param boolean $cache Enables returning/storing cached query results
  615. * @return array Array of resultset rows, or false if no rows matched
  616. */
  617. function read(&$model, $queryData = array(), $recursive = null) {
  618. $results = parent::read($model, $queryData, $recursive);
  619. $this->__fieldMappings = array();
  620. return $results;
  621. }
  622. /**
  623. * Fetches the next row from the current result set
  624. *
  625. * @return unknown
  626. */
  627. function fetchResult() {
  628. if ($row = mssql_fetch_row($this->results)) {
  629. $resultRow = array();
  630. $i = 0;
  631. foreach ($row as $index => $field) {
  632. list($table, $column) = $this->map[$index];
  633. $resultRow[$table][$column] = $row[$index];
  634. $i++;
  635. }
  636. return $resultRow;
  637. } else {
  638. return false;
  639. }
  640. }
  641. /**
  642. * Inserts multiple values into a table
  643. *
  644. * @param string $table
  645. * @param string $fields
  646. * @param array $values
  647. * @access protected
  648. */
  649. function insertMulti($table, $fields, $values) {
  650. $primaryKey = $this->_getPrimaryKey($table);
  651. $hasPrimaryKey = $primaryKey != null && (
  652. (is_array($fields) && in_array($primaryKey, $fields)
  653. || (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false))
  654. );
  655. if ($hasPrimaryKey) {
  656. $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
  657. }
  658. parent::insertMulti($table, $fields, $values);
  659. if ($hasPrimaryKey) {
  660. $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF');
  661. }
  662. }
  663. /**
  664. * Generate a database-native column schema string
  665. *
  666. * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
  667. * where options can be 'default', 'length', or 'key'.
  668. * @return string
  669. */
  670. function buildColumn($column) {
  671. $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column));
  672. if (strpos($result, 'DEFAULT NULL') !== false) {
  673. $result = str_replace('DEFAULT NULL', 'NULL', $result);
  674. } else if (array_keys($column) == array('type', 'name')) {
  675. $result .= ' NULL';
  676. }
  677. return $result;
  678. }
  679. /**
  680. * Format indexes for create table
  681. *
  682. * @param array $indexes
  683. * @param string $table
  684. * @return string
  685. */
  686. function buildIndex($indexes, $table = null) {
  687. $join = array();
  688. foreach ($indexes as $name => $value) {
  689. if ($name == 'PRIMARY') {
  690. $join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
  691. } else if (isset($value['unique']) && $value['unique']) {
  692. $out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
  693. if (is_array($value['column'])) {
  694. $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
  695. } else {
  696. $value['column'] = $this->name($value['column']);
  697. }
  698. $out .= "({$value['column']});";
  699. $join[] = $out;
  700. }
  701. }
  702. return $join;
  703. }
  704. /**
  705. * Makes sure it will return the primary key
  706. *
  707. * @param mixed $model
  708. * @access protected
  709. * @return string
  710. */
  711. function _getPrimaryKey($model) {
  712. if (is_object($model)) {
  713. $schema = $model->schema();
  714. } else {
  715. $schema = $this->describe($model);
  716. }
  717. foreach ($schema as $field => $props) {
  718. if (isset($props['key']) && $props['key'] == 'primary') {
  719. return $field;
  720. }
  721. }
  722. return null;
  723. }
  724. }