PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/msadouni/cakephp2x
PHP | 547 lines | 299 code | 56 blank | 192 comment | 80 complexity | 893b0f67eb91acf05200fe8795a7bbe5 MD5 | raw file
  1. <?php
  2. /**
  3. * AdoDB layer for DBO.
  4. *
  5. * Long description for file
  6. *
  7. * PHP Version 5.x
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package cake
  18. * @subpackage cake.cake.libs.model.datasources.dbo
  19. * @since CakePHP(tm) v 0.2.9
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. /**
  23. * Include AdoDB files.
  24. */
  25. App::import('Vendor', 'NewADOConnection', array('file' => 'adodb' . DS . 'adodb.inc.php'));
  26. /**
  27. * AdoDB DBO implementation.
  28. *
  29. * Database abstraction implementation for the AdoDB library.
  30. *
  31. * @package cake
  32. * @subpackage cake.cake.libs.model.datasources.dbo
  33. */
  34. class DboAdodb extends DboSource {
  35. /**
  36. * Enter description here...
  37. *
  38. * @var string
  39. */
  40. public $description = "ADOdb DBO Driver";
  41. /**
  42. * ADOConnection object with which we connect.
  43. *
  44. * @var ADOConnection The connection object.
  45. * @access private
  46. */
  47. protected $_adodb = null;
  48. /**
  49. * Array translating ADOdb column MetaTypes to cake-supported metatypes
  50. *
  51. * @var array
  52. * @access private
  53. */
  54. protected $_adodbColumnTypes = array(
  55. 'string' => 'C',
  56. 'text' => 'X',
  57. 'date' => 'D',
  58. 'timestamp' => 'T',
  59. 'time' => 'T',
  60. 'datetime' => 'T',
  61. 'boolean' => 'L',
  62. 'float' => 'N',
  63. 'integer' => 'I',
  64. 'binary' => 'R',
  65. );
  66. /**
  67. * ADOdb column definition
  68. *
  69. * @var array
  70. */
  71. public $columns = array(
  72. 'primary_key' => array('name' => 'R', 'limit' => 11),
  73. 'string' => array('name' => 'C', 'limit' => '255'),
  74. 'text' => array('name' => 'X'),
  75. 'integer' => array('name' => 'I', 'limit' => '11', 'formatter' => 'intval'),
  76. 'float' => array('name' => 'N', 'formatter' => 'floatval'),
  77. 'timestamp' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
  78. 'time' => array('name' => 'T', 'format' => 'H:i:s', 'formatter' => 'date'),
  79. 'datetime' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
  80. 'date' => array('name' => 'D', 'format' => 'Y-m-d', 'formatter' => 'date'),
  81. 'binary' => array('name' => 'B'),
  82. 'boolean' => array('name' => 'L', 'limit' => '1')
  83. );
  84. /**
  85. * Connects to the database using options in the given configuration array.
  86. *
  87. * @param array $config Configuration array for connecting
  88. */
  89. public function connect() {
  90. $config = $this->config;
  91. $persistent = strrpos($config['connect'], '|p');
  92. if ($persistent === false) {
  93. $adodb_driver = $config['connect'];
  94. $connect = 'Connect';
  95. } else {
  96. $adodb_driver = substr($config['connect'], 0, $persistent);
  97. $connect = 'PConnect';
  98. }
  99. if (!$this->enabled()) {
  100. return false;
  101. }
  102. $this->_adodb = NewADOConnection($adodb_driver);
  103. $this->_adodbDataDict = NewDataDictionary($this->_adodb, $adodb_driver);
  104. $this->startQuote = $this->_adodb->nameQuote;
  105. $this->endQuote = $this->_adodb->nameQuote;
  106. $this->connected = $this->_adodb->$connect($config['host'], $config['login'], $config['password'], $config['database']);
  107. $this->_adodbMetatyper = &$this->_adodb->execute('Select 1');
  108. return $this->connected;
  109. }
  110. /**
  111. * Check that AdoDB is available.
  112. *
  113. * @return boolean
  114. */
  115. function enabled() {
  116. return function_exists('NewADOConnection');
  117. }
  118. /**
  119. * Disconnects from database.
  120. *
  121. * @return boolean True if the database could be disconnected, else false
  122. */
  123. public function disconnect() {
  124. return $this->_adodb->Close();
  125. }
  126. /**
  127. * Executes given SQL statement.
  128. *
  129. * @param string $sql SQL statement
  130. * @return resource Result resource identifier
  131. */
  132. private function _execute($sql) {
  133. global $ADODB_FETCH_MODE;
  134. $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
  135. return $this->_adodb->execute($sql);
  136. }
  137. /**
  138. * Returns a row from current resultset as an array .
  139. *
  140. * @return array The fetched row as an array
  141. */
  142. public function fetchRow($sql = null) {
  143. if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
  144. if (!$this->execute($sql)) {
  145. return null;
  146. }
  147. }
  148. if (!$this->hasResult()) {
  149. return null;
  150. } else {
  151. $resultRow = $this->_result->FetchRow();
  152. $this->resultSet($resultRow);
  153. return $this->fetchResult();
  154. }
  155. }
  156. /**
  157. * Begin a transaction
  158. *
  159. * @param unknown_type $model
  160. * @return boolean True on success, false on fail
  161. * (i.e. if the database/model does not support transactions).
  162. */
  163. public function begin(&$model) {
  164. if (parent::begin($model)) {
  165. if ($this->_adodb->BeginTrans()) {
  166. $this->_transactionStarted = true;
  167. return true;
  168. }
  169. }
  170. return false;
  171. }
  172. /**
  173. * Commit a transaction
  174. *
  175. * @param unknown_type $model
  176. * @return boolean True on success, false on fail
  177. * (i.e. if the database/model does not support transactions,
  178. * or a transaction has not started).
  179. */
  180. public function commit(&$model) {
  181. if (parent::commit($model)) {
  182. $this->_transactionStarted = false;
  183. return $this->_adodb->CommitTrans();
  184. }
  185. return false;
  186. }
  187. /**
  188. * Rollback a transaction
  189. *
  190. * @param unknown_type $model
  191. * @return boolean True on success, false on fail
  192. * (i.e. if the database/model does not support transactions,
  193. * or a transaction has not started).
  194. */
  195. public function rollback(&$model) {
  196. if (parent::rollback($model)) {
  197. return $this->_adodb->RollbackTrans();
  198. }
  199. return false;
  200. }
  201. /**
  202. * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
  203. *
  204. * @return array Array of tablenames in the database
  205. */
  206. public function listSources() {
  207. $tables = $this->_adodb->MetaTables('TABLES');
  208. if (!count($tables) > 0) {
  209. trigger_error(ERROR_NO_TABLE_LIST, E_USER_NOTICE);
  210. exit;
  211. }
  212. return $tables;
  213. }
  214. /**
  215. * Returns an array of the fields in the table used by the given model.
  216. *
  217. * @param AppModel $model Model object
  218. * @return array Fields in table. Keys are name and type
  219. */
  220. public function describe(&$model) {
  221. $cache = parent::describe($model);
  222. if ($cache != null) {
  223. return $cache;
  224. }
  225. $fields = false;
  226. $cols = $this->_adodb->MetaColumns($this->fullTableName($model, false));
  227. foreach ($cols as $column) {
  228. $fields[$column->name] = array(
  229. 'type' => $this->column($column->type),
  230. 'null' => !$column->not_null,
  231. 'length' => $column->max_length,
  232. );
  233. if ($column->has_default) {
  234. $fields[$column->name]['default'] = $column->default_value;
  235. }
  236. if ($column->primary_key == 1) {
  237. $fields[$column->name]['key'] = 'primary';
  238. }
  239. }
  240. $this->__cacheDescription($this->fullTableName($model, false), $fields);
  241. return $fields;
  242. }
  243. /**
  244. * Returns a formatted error message from previous database operation.
  245. *
  246. * @return string Error message
  247. */
  248. public function lastError() {
  249. return $this->_adodb->ErrorMsg();
  250. }
  251. /**
  252. * Returns number of affected rows in previous database operation, or false if no previous operation exists.
  253. *
  254. * @return integer Number of affected rows
  255. */
  256. public function lastAffected() {
  257. return $this->_adodb->Affected_Rows();
  258. }
  259. /**
  260. * Returns number of rows in previous resultset, or false if no previous resultset exists.
  261. *
  262. * @return integer Number of rows in resultset
  263. */
  264. public function lastNumRows() {
  265. return $this->_result ? $this->_result->RecordCount() : false;
  266. }
  267. /**
  268. * Returns the ID generated from the previous INSERT operation.
  269. *
  270. * @return int
  271. *
  272. * @Returns the last autonumbering ID inserted. Returns false if function not supported.
  273. */
  274. public function lastInsertId() {
  275. return $this->_adodb->Insert_ID();
  276. }
  277. /**
  278. * Returns a LIMIT statement in the correct format for the particular database.
  279. *
  280. * @param integer $limit Limit of results returned
  281. * @param integer $offset Offset from which to start results
  282. * @return string SQL limit/offset statement
  283. * @todo Please change output string to whatever select your database accepts. adodb doesn't allow us to get the correct limit string out of it.
  284. */
  285. public function limit($limit, $offset = null) {
  286. if ($limit) {
  287. $rt = '';
  288. if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
  289. $rt = ' LIMIT';
  290. }
  291. if ($offset) {
  292. $rt .= ' ' . $offset . ',';
  293. }
  294. $rt .= ' ' . $limit;
  295. return $rt;
  296. }
  297. return null;
  298. // please change to whatever select your database accepts
  299. // adodb doesn't allow us to get the correct limit string out of it
  300. }
  301. /**
  302. * Converts database-layer column types to basic types
  303. *
  304. * @param string $real Real database-layer column type (i.e. "varchar(255)")
  305. * @return string Abstract column type (i.e. "string")
  306. */
  307. public function column($real) {
  308. $metaTypes = array_flip($this->_adodbColumnTypes);
  309. $interpreted_type = $this->_adodbMetatyper->MetaType($real);
  310. if (!isset($metaTypes[$interpreted_type])) {
  311. return 'text';
  312. }
  313. return $metaTypes[$interpreted_type];
  314. }
  315. /**
  316. * Returns a quoted and escaped string of $data for use in an SQL statement.
  317. *
  318. * @param string $data String to be prepared for use in an SQL statement
  319. * @param string $column_type The type of the column into which this data will be inserted
  320. * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
  321. * @return string Quoted and escaped data
  322. */
  323. public function value($data, $column = null, $safe = false) {
  324. $parent = parent::value($data, $column, $safe);
  325. if ($parent != null) {
  326. return $parent;
  327. }
  328. if ($data === null) {
  329. return 'NULL';
  330. }
  331. if ($data === '') {
  332. return "''";
  333. }
  334. return $this->_adodb->qstr($data);
  335. }
  336. /**
  337. * Generates the fields list of an SQL query.
  338. *
  339. * @param Model $model
  340. * @param string $alias Alias tablename
  341. * @param mixed $fields
  342. * @return array
  343. */
  344. public function fields(&$model, $alias = null, $fields = array(), $quote = true) {
  345. if (empty($alias)) {
  346. $alias = $model->alias;
  347. }
  348. $fields = parent::fields($model, $alias, $fields, false);
  349. if (!$quote) {
  350. return $fields;
  351. }
  352. $count = count($fields);
  353. if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
  354. for ($i = 0; $i < $count; $i++) {
  355. if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
  356. $prepend = '';
  357. if (strpos($fields[$i], 'DISTINCT') !== false) {
  358. $prepend = 'DISTINCT ';
  359. $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
  360. }
  361. if (strrpos($fields[$i], '.') === false) {
  362. $fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
  363. } else {
  364. $build = explode('.', $fields[$i]);
  365. $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
  366. }
  367. }
  368. }
  369. }
  370. return $fields;
  371. }
  372. /**
  373. * Build ResultSets and map data
  374. *
  375. * @param array $results
  376. */
  377. public function resultSet(&$results) {
  378. $num_fields = count($results);
  379. $fields = array_keys($results);
  380. $this->results =& $results;
  381. $this->map = array();
  382. $index = 0;
  383. $j = 0;
  384. while ($j < $num_fields) {
  385. $columnName = $fields[$j];
  386. if (strpos($columnName, '__')) {
  387. $parts = explode('__', $columnName);
  388. $this->map[$index++] = array($parts[0], $parts[1]);
  389. } else {
  390. $this->map[$index++] = array(0, $columnName);
  391. }
  392. $j++;
  393. }
  394. }
  395. /**
  396. * Fetches the next row from the current result set
  397. *
  398. * @return unknown
  399. */
  400. public function fetchResult() {
  401. if (!empty($this->results)) {
  402. $row = $this->results;
  403. $this->results = null;
  404. } else {
  405. $row = $this->_result->FetchRow();
  406. }
  407. if (empty($row)) {
  408. return false;
  409. }
  410. $resultRow = array();
  411. $fields = array_keys($row);
  412. $count = count($fields);
  413. $i = 0;
  414. for ($i = 0; $i < $count; $i++) { //$row as $index => $field) {
  415. list($table, $column) = $this->map[$i];
  416. $resultRow[$table][$column] = $row[$fields[$i]];
  417. }
  418. return $resultRow;
  419. }
  420. /**
  421. * Generate a database-native column schema string
  422. *
  423. * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
  424. * where options can be 'default', 'length', or 'key'.
  425. * @return string
  426. */
  427. public function buildColumn($column) {
  428. $name = $type = null;
  429. extract(array_merge(array('null' => true), $column));
  430. if (empty($name) || empty($type)) {
  431. trigger_error('Column name or type not defined in schema', E_USER_WARNING);
  432. return null;
  433. }
  434. //$metaTypes = array_flip($this->_adodbColumnTypes);
  435. if (!isset($this->_adodbColumnTypes[$type])) {
  436. trigger_error("Column type {$type} does not exist", E_USER_WARNING);
  437. return null;
  438. }
  439. $metaType = $this->_adodbColumnTypes[$type];
  440. $concreteType = $this->_adodbDataDict->ActualType($metaType);
  441. $real = $this->columns[$type];
  442. //UUIDs are broken so fix them.
  443. if ($type == 'string' && isset($real['length']) && $real['length'] == 36) {
  444. $concreteType = 'CHAR';
  445. }
  446. $out = $this->name($name) . ' ' . $concreteType;
  447. if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
  448. if (isset($column['length'])) {
  449. $length = $column['length'];
  450. } elseif (isset($column['limit'])) {
  451. $length = $column['limit'];
  452. } elseif (isset($real['length'])) {
  453. $length = $real['length'];
  454. } else {
  455. $length = $real['limit'];
  456. }
  457. $out .= '(' . $length . ')';
  458. }
  459. $_notNull = $_default = $_autoInc = $_constraint = $_unsigned = false;
  460. if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
  461. $_constraint = '';
  462. $_autoInc = true;
  463. } elseif (isset($column['key']) && $column['key'] == 'primary') {
  464. $_notNull = '';
  465. } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
  466. $_notNull = true;
  467. $_default = $column['default'];
  468. } elseif ( isset($column['null']) && $column['null'] == true) {
  469. $_notNull = false;
  470. $_default = 'NULL';
  471. }
  472. if (isset($column['default']) && $_default == false) {
  473. $_default = $this->value($column['default']);
  474. }
  475. if (isset($column['null']) && $column['null'] == false) {
  476. $_notNull = true;
  477. }
  478. //use concrete instance of DataDict to make the suffixes for us.
  479. $out .= $this->_adodbDataDict->_CreateSuffix($out, $metaType, $_notNull, $_default, $_autoInc, $_constraint, $_unsigned);
  480. return $out;
  481. }
  482. /**
  483. * Checks if the result is valid
  484. *
  485. * @return boolean True if the result is valid, else false
  486. */
  487. public function hasResult() {
  488. return is_object($this->_result) && !$this->_result->EOF;
  489. }
  490. }
  491. ?>