PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/runtime/lib/adapter/DBAdapter.php

https://github.com/1989gaurav/Propel
PHP | 576 lines | 236 code | 46 blank | 294 comment | 25 complexity | 3094b6b744bdfd516fcf92fb070c5753 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. /**
  10. * DBAdapter</code> defines the interface for a Propel database adapter.
  11. *
  12. * <p>Support for new databases is added by subclassing
  13. * <code>DBAdapter</code> and implementing its abstract interface, and by
  14. * registering the new database adapter and corresponding Propel
  15. * driver in the private adapters map (array) in this class.</p>
  16. *
  17. * <p>The Propel database adapters exist to present a uniform
  18. * interface to database access across all available databases. Once
  19. * the necessary adapters have been written and configured,
  20. * transparent swapping of databases is theoretically supported with
  21. * <i>zero code change</i> and minimal configuration file
  22. * modifications.</p>
  23. *
  24. * @author Hans Lellelid <hans@xmpl.org> (Propel)
  25. * @author Jon S. Stevens <jon@latchkey.com> (Torque)
  26. * @author Brett McLaughlin <bmclaugh@algx.net> (Torque)
  27. * @author Daniel Rall <dlr@finemaltcoding.com> (Torque)
  28. * @version $Revision$
  29. * @package propel.runtime.adapter
  30. */
  31. abstract class DBAdapter
  32. {
  33. const ID_METHOD_NONE = 0;
  34. const ID_METHOD_AUTOINCREMENT = 1;
  35. const ID_METHOD_SEQUENCE = 2;
  36. /**
  37. * Propel driver to Propel adapter map.
  38. * @var array
  39. */
  40. private static $adapters = array(
  41. 'mysql' => 'DBMySQL',
  42. 'mysqli' => 'DBMySQLi',
  43. 'mssql' => 'DBMSSQL',
  44. 'sqlsrv' => 'DBSQLSRV',
  45. 'oracle' => 'DBOracle',
  46. 'oci' => 'DBOracle',
  47. 'pgsql' => 'DBPostgres',
  48. 'sqlite' => 'DBSQLite',
  49. '' => 'DBNone',
  50. );
  51. /**
  52. * Creates a new instance of the database adapter associated
  53. * with the specified Propel driver.
  54. *
  55. * @param string $driver The name of the Propel driver to create a new adapter instance
  56. * for or a shorter form adapter key.
  57. *
  58. * @throws PropelException If the adapter could not be instantiated.
  59. * @return DBAdapter An instance of a Propel database adapter.
  60. */
  61. public static function factory($driver) {
  62. $adapterClass = isset(self::$adapters[$driver]) ? self::$adapters[$driver] : null;
  63. if ($adapterClass !== null) {
  64. $a = new $adapterClass();
  65. return $a;
  66. } else {
  67. throw new PropelException("Unsupported Propel driver: " . $driver . ": Check your configuration file");
  68. }
  69. }
  70. /**
  71. * Prepare connection parameters.
  72. *
  73. * @param array $params
  74. * @return array
  75. */
  76. public function prepareParams($settings)
  77. {
  78. return $settings;
  79. }
  80. /**
  81. * This method is called after a connection was created to run necessary
  82. * post-initialization queries or code.
  83. *
  84. * If a charset was specified, this will be set before any other queries
  85. * are executed.
  86. *
  87. * This base method runs queries specified using the "query" setting.
  88. *
  89. * @see setCharset()
  90. *
  91. * @param PDO $con A PDO connection instance.
  92. * @param array $settings An array of settings.
  93. */
  94. public function initConnection(PDO $con, array $settings)
  95. {
  96. if (isset($settings['charset']['value'])) {
  97. $this->setCharset($con, $settings['charset']['value']);
  98. }
  99. if (isset($settings['queries']) && is_array($settings['queries'])) {
  100. foreach ($settings['queries'] as $queries) {
  101. foreach ((array)$queries as $query) {
  102. $con->exec($query);
  103. }
  104. }
  105. }
  106. }
  107. /**
  108. * Sets the character encoding using SQL standard SET NAMES statement.
  109. *
  110. * This method is invoked from the default initConnection() method and must
  111. * be overridden for an RDMBS which does _not_ support this SQL standard.
  112. *
  113. * @see initConnection()
  114. *
  115. * @param PDO $con A $PDO PDO connection instance.
  116. * @param string $charset The $string charset encoding.
  117. */
  118. public function setCharset(PDO $con, $charset)
  119. {
  120. $con->exec("SET NAMES '" . $charset . "'");
  121. }
  122. /**
  123. * This method is used to ignore case.
  124. *
  125. * @param string $in The string to transform to upper case.
  126. * @return string The upper case string.
  127. */
  128. public abstract function toUpperCase($in);
  129. /**
  130. * Returns the character used to indicate the beginning and end of
  131. * a piece of text used in a SQL statement (generally a single
  132. * quote).
  133. *
  134. * @return string The text delimeter.
  135. */
  136. public function getStringDelimiter()
  137. {
  138. return '\'';
  139. }
  140. /**
  141. * This method is used to ignore case.
  142. *
  143. * @param string $in The string whose case to ignore.
  144. * @return string The string in a case that can be ignored.
  145. */
  146. public abstract function ignoreCase($in);
  147. /**
  148. * This method is used to ignore case in an ORDER BY clause.
  149. * Usually it is the same as ignoreCase, but some databases
  150. * (Interbase for example) does not use the same SQL in ORDER BY
  151. * and other clauses.
  152. *
  153. * @param string $in The string whose case to ignore.
  154. * @return string The string in a case that can be ignored.
  155. */
  156. public function ignoreCaseInOrderBy($in)
  157. {
  158. return $this->ignoreCase($in);
  159. }
  160. /**
  161. * Returns SQL which concatenates the second string to the first.
  162. *
  163. * @param string $s1 String to concatenate.
  164. * @param string $s2 String to append.
  165. *
  166. * @return string
  167. */
  168. public abstract function concatString($s1, $s2);
  169. /**
  170. * Returns SQL which extracts a substring.
  171. *
  172. * @param string $s String to extract from.
  173. * @param integer $pos Offset to start from.
  174. * @param integer $len Number of characters to extract.
  175. *
  176. * @return string
  177. */
  178. public abstract function subString($s, $pos, $len);
  179. /**
  180. * Returns SQL which calculates the length (in chars) of a string.
  181. *
  182. * @param string $s String to calculate length of.
  183. * @return string
  184. */
  185. public abstract function strLength($s);
  186. /**
  187. * Quotes database objec identifiers (table names, col names, sequences, etc.).
  188. * @param string $text The identifier to quote.
  189. * @return string The quoted identifier.
  190. */
  191. public function quoteIdentifier($text)
  192. {
  193. return '"' . $text . '"';
  194. }
  195. /**
  196. * Quotes a database table which could have space seperating it from an alias, both should be identified seperately
  197. * This doesn't take care of dots which separate schema names from table names. Adapters for RDBMs which support
  198. * schemas have to implement that in the platform-specific way.
  199. *
  200. * @param string $table The table name to quo
  201. * @return string The quoted table name
  202. **/
  203. public function quoteIdentifierTable($table) {
  204. return implode(" ", array_map(array($this, "quoteIdentifier"), explode(" ", $table) ) );
  205. }
  206. /**
  207. * Returns the native ID method for this RDBMS.
  208. *
  209. * @return integer One of DBAdapter:ID_METHOD_SEQUENCE, DBAdapter::ID_METHOD_AUTOINCREMENT.
  210. */
  211. protected function getIdMethod()
  212. {
  213. return DBAdapter::ID_METHOD_AUTOINCREMENT;
  214. }
  215. /**
  216. * Whether this adapter uses an ID generation system that requires getting ID _before_ performing INSERT.
  217. *
  218. * @return boolean
  219. */
  220. public function isGetIdBeforeInsert()
  221. {
  222. return ($this->getIdMethod() === DBAdapter::ID_METHOD_SEQUENCE);
  223. }
  224. /**
  225. * Whether this adapter uses an ID generation system that requires getting ID _before_ performing INSERT.
  226. *
  227. * @return boolean
  228. */
  229. public function isGetIdAfterInsert()
  230. {
  231. return ($this->getIdMethod() === DBAdapter::ID_METHOD_AUTOINCREMENT);
  232. }
  233. /**
  234. * Gets the generated ID (either last ID for autoincrement or next sequence ID).
  235. * @param PDO $con
  236. * @param string $name
  237. *
  238. * @return mixed
  239. */
  240. public function getId(PDO $con, $name = null)
  241. {
  242. return $con->lastInsertId($name);
  243. }
  244. /**
  245. * Formats a temporal value brefore binding, given a ColumnMap object
  246. *
  247. * @param mixed $value The temporal value
  248. * @param ColumnMap $cMap
  249. *
  250. * @return string The formatted temporal value
  251. */
  252. protected function formatTemporalValue($value, ColumnMap $cMap)
  253. {
  254. /** @var $dt PropelDateTime */
  255. if ($dt = PropelDateTime::newInstance($value)) {
  256. switch($cMap->getType()) {
  257. case PropelColumnTypes::TIMESTAMP:
  258. case PropelColumnTypes::BU_TIMESTAMP:
  259. $value = $dt->format($this->getTimestampFormatter());
  260. break;
  261. case PropelColumnTypes::DATE:
  262. case PropelColumnTypes::BU_DATE:
  263. $value = $dt->format($this->getDateFormatter());
  264. break;
  265. case PropelColumnTypes::TIME:
  266. $value = $dt->format($this->getTimeFormatter());
  267. break;
  268. }
  269. }
  270. return $value;
  271. }
  272. /**
  273. * Returns timestamp formatter string for use in date() function.
  274. *
  275. * @return string
  276. */
  277. public function getTimestampFormatter()
  278. {
  279. return 'Y-m-d H:i:s';
  280. }
  281. /**
  282. * Returns date formatter string for use in date() function.
  283. *
  284. * @return string
  285. */
  286. public function getDateFormatter()
  287. {
  288. return "Y-m-d";
  289. }
  290. /**
  291. * Returns time formatter string for use in date() function.
  292. *
  293. * @return string
  294. */
  295. public function getTimeFormatter()
  296. {
  297. return "H:i:s";
  298. }
  299. /**
  300. * Should Column-Names get identifiers for inserts or updates.
  301. * By default false is returned -> backwards compability.
  302. *
  303. * it`s a workaround...!!!
  304. *
  305. * @todo should be abstract
  306. * @deprecated
  307. *
  308. * @return boolean
  309. */
  310. public function useQuoteIdentifier()
  311. {
  312. return false;
  313. }
  314. /**
  315. * Allows manipulation of the query string before PDOStatement is instantiated.
  316. *
  317. * @param string $sql The sql statement
  318. * @param array $params array('column' => ..., 'table' => ..., 'value' => ...)
  319. * @param Criteria $values
  320. * @param DatabaseMap $dbMap
  321. */
  322. public function cleanupSQL(&$sql, array &$params, Criteria $values, DatabaseMap $dbMap)
  323. {
  324. }
  325. /**
  326. * Modifies the passed-in SQL to add LIMIT and/or OFFSET.
  327. *
  328. * @param string $sql
  329. * @param integer $offset
  330. * @param integer $limit
  331. */
  332. public abstract function applyLimit(&$sql, $offset, $limit);
  333. /**
  334. * Gets the SQL string that this adapter uses for getting a random number.
  335. *
  336. * @param mixed $seed (optional) seed value for databases that support this
  337. */
  338. public abstract function random($seed = null);
  339. /**
  340. * Returns the "DELETE FROM <table> [AS <alias>]" part of DELETE query.
  341. *
  342. * @param Criteria $criteria
  343. * @param string $tableName
  344. *
  345. * @return string
  346. */
  347. public function getDeleteFromClause($criteria, $tableName)
  348. {
  349. $sql = 'DELETE ';
  350. if ($queryComment = $criteria->getComment()) {
  351. $sql .= '/* ' . $queryComment . ' */ ';
  352. }
  353. if ($realTableName = $criteria->getTableForAlias($tableName)) {
  354. if ($this->useQuoteIdentifier()) {
  355. $realTableName = $this->quoteIdentifierTable($realTableName);
  356. }
  357. $sql .= $tableName . ' FROM ' . $realTableName . ' AS ' . $tableName;
  358. } else {
  359. if ($this->useQuoteIdentifier()) {
  360. $tableName = $this->quoteIdentifierTable($tableName);
  361. }
  362. $sql .= 'FROM ' . $tableName;
  363. }
  364. return $sql;
  365. }
  366. /**
  367. * Builds the SELECT part of a SQL statement based on a Criteria
  368. * taking into account select columns and 'as' columns (i.e. columns aliases)
  369. * Move from BasePeer to DBAdapter and turn from static to non static
  370. *
  371. * @param Criteria $criteria
  372. * @param array $fromClause
  373. * @param boolean $aliasAll
  374. *
  375. * @return string
  376. */
  377. public function createSelectSqlPart(Criteria $criteria, &$fromClause, $aliasAll = false)
  378. {
  379. $selectClause = array();
  380. if ($aliasAll) {
  381. $this->turnSelectColumnsToAliases($criteria);
  382. // no select columns after that, they are all aliases
  383. } else {
  384. foreach ($criteria->getSelectColumns() as $columnName) {
  385. // expect every column to be of "table.column" formation
  386. // it could be a function: e.g. MAX(books.price)
  387. $tableName = null;
  388. $selectClause[] = $columnName; // the full column name: e.g. MAX(books.price)
  389. $parenPos = strrpos($columnName, '(');
  390. $dotPos = strrpos($columnName, '.', ($parenPos !== false ? $parenPos : 0));
  391. if ($dotPos !== false) {
  392. if ($parenPos === false) { // table.column
  393. $tableName = substr($columnName, 0, $dotPos);
  394. } else { // FUNC(table.column)
  395. // functions may contain qualifiers so only take the last
  396. // word as the table name.
  397. // COUNT(DISTINCT books.price)
  398. $lastSpace = strpos($tableName, ' ');
  399. if ($lastSpace !== false) { // COUNT(DISTINCT books.price)
  400. $tableName = substr($tableName, $lastSpace + 1);
  401. } else {
  402. $tableName = substr($columnName, $parenPos + 1, $dotPos - ($parenPos + 1));
  403. }
  404. }
  405. // is it a table alias?
  406. $tableName2 = $criteria->getTableForAlias($tableName);
  407. if ($tableName2 !== null) {
  408. $fromClause[] = $tableName2 . ' ' . $tableName;
  409. } else {
  410. $fromClause[] = $tableName;
  411. }
  412. } // if $dotPost !== false
  413. }
  414. }
  415. // set the aliases
  416. foreach ($criteria->getAsColumns() as $alias => $col) {
  417. $selectClause[] = $col . ' AS ' . $alias;
  418. }
  419. $selectModifiers = $criteria->getSelectModifiers();
  420. $queryComment = $criteria->getComment();
  421. // Build the SQL from the arrays we compiled
  422. $sql = "SELECT "
  423. . ($queryComment ? '/* ' . $queryComment . ' */ ' : '')
  424. . ($selectModifiers ? (implode(' ', $selectModifiers) . ' ') : '')
  425. . implode(", ", $selectClause);
  426. return $sql;
  427. }
  428. /**
  429. * Ensures uniqueness of select column names by turning them all into aliases
  430. * This is necessary for queries on more than one table when the tables share a column name
  431. * Moved from BasePeer to DBAdapter and turned from static to non static
  432. *
  433. * @see http://propel.phpdb.org/trac/ticket/795
  434. *
  435. * @param Criteria $criteria
  436. * @return Criteria The input, with Select columns replaced by aliases
  437. */
  438. public function turnSelectColumnsToAliases(Criteria $criteria)
  439. {
  440. $selectColumns = $criteria->getSelectColumns();
  441. // clearSelectColumns also clears the aliases, so get them too
  442. $asColumns = $criteria->getAsColumns();
  443. $criteria->clearSelectColumns();
  444. $columnAliases = $asColumns;
  445. // add the select columns back
  446. foreach ($selectColumns as $clause) {
  447. // Generate a unique alias
  448. $baseAlias = preg_replace('/\W/', '_', $clause);
  449. $alias = $baseAlias;
  450. // If it already exists, add a unique suffix
  451. $i = 0;
  452. while (isset($columnAliases[$alias])) {
  453. $i++;
  454. $alias = $baseAlias . '_' . $i;
  455. }
  456. // Add it as an alias
  457. $criteria->addAsColumn($alias, $clause);
  458. $columnAliases[$alias] = $clause;
  459. }
  460. // Add the aliases back, don't modify them
  461. foreach ($asColumns as $name => $clause) {
  462. $criteria->addAsColumn($name, $clause);
  463. }
  464. return $criteria;
  465. }
  466. /**
  467. * Binds values in a prepared statement.
  468. *
  469. * This method is designed to work with the BasePeer::createSelectSql() method, which creates
  470. * both the SELECT SQL statement and populates a passed-in array of parameter
  471. * values that should be substituted.
  472. *
  473. * <code>
  474. * $db = Propel::getDB($criteria->getDbName());
  475. * $sql = BasePeer::createSelectSql($criteria, $params);
  476. * $stmt = $con->prepare($sql);
  477. * $params = array();
  478. * $db->populateStmtValues($stmt, $params, Propel::getDatabaseMap($critera->getDbName()));
  479. * $stmt->execute();
  480. * </code>
  481. *
  482. * @param PDOStatement $stmt
  483. * @param array $params array('column' => ..., 'table' => ..., 'value' => ...)
  484. * @param DatabaseMap $dbMap
  485. */
  486. public function bindValues(PDOStatement $stmt, array $params, DatabaseMap $dbMap)
  487. {
  488. $position = 0;
  489. foreach ($params as $param) {
  490. $position++;
  491. $parameter = ':p' . $position;
  492. $value = $param['value'];
  493. if (null === $value) {
  494. $stmt->bindValue($parameter, null, PDO::PARAM_NULL);
  495. continue;
  496. }
  497. $tableName = $param['table'];
  498. if (null === $tableName) {
  499. $stmt->bindValue($parameter, $value);
  500. continue;
  501. }
  502. $cMap = $dbMap->getTable($tableName)->getColumn($param['column']);
  503. $this->bindValue($stmt, $parameter, $value, $cMap, $position);
  504. }
  505. }
  506. /**
  507. * Binds a value to a positioned parameted in a statement,
  508. * given a ColumnMap object to infer the binding type.
  509. *
  510. * @param PDOStatement $stmt The statement to bind
  511. * @param string $parameter Parameter identifier
  512. * @param mixed $value The value to bind
  513. * @param ColumnMap $cMap The ColumnMap of the column to bind
  514. * @param null|integer $position The position of the parameter to bind
  515. *
  516. * @return boolean
  517. */
  518. public function bindValue(PDOStatement $stmt, $parameter, $value, ColumnMap $cMap, $position = null)
  519. {
  520. if ($cMap->isTemporal()) {
  521. $value = $this->formatTemporalValue($value, $cMap);
  522. } elseif (is_resource($value) && $cMap->isLob()) {
  523. // we always need to make sure that the stream is rewound, otherwise nothing will
  524. // get written to database.
  525. rewind($value);
  526. }
  527. return $stmt->bindValue($parameter, $value, $cMap->getPdoType());
  528. }
  529. }