Ensuring tests pass after bringing back support for db DSN connection. Rel. [501...
[akelos.git] / lib / AkActiveRecord / AkDbAdapter.php
blob35f4c9725771922fc6cf3fa4fdf9919509363def
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
11 /**
12 * @package ActiveRecord
13 * @subpackage Base
14 * @component DbAdapter
15 * @author Bermi Ferrer <bermi a.t akelos c.om> 2004 - 2007
16 * @author Kaste 2007
17 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
18 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
21 defined('AK_AVAILABLE_DATABASES') ? null : define('AK_AVAILABLE_DATABASES', 'mysql,pgsql,sqlite');
23 require_once(AK_LIB_DIR.DS.'AkObject.php');
25 class AkDbAdapter extends AkObject
28 var $connection;
29 var $settings;
30 var $dictionary;
31 var $debug=false;
32 var $logger;
34 /**
35 * @param array $database_settings
37 function __construct($database_settings, $auto_connect = false)
39 $this->settings = $database_settings;
40 if ($auto_connect){
41 $this->connect();
43 if (AK_LOG_EVENTS){
44 $this->logger =& Ak::getLogger();
48 function __destruct()
52 function connect()
54 $dsn = $this->_constructDsn($this->settings);
55 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb.inc.php');
56 $this->connection = AK_DEBUG ? NewADOConnection($dsn) : @NewADOConnection($dsn);
57 if (!$this->connection){
58 error_reporting(E_ALL);
59 if(defined('AK_DATABASE_CONNECTION_FAILURE_CALLBACK') && function_exists(AK_DATABASE_CONNECTION_FAILURE_CALLBACK)){
60 $fn = AK_DATABASE_CONNECTION_FAILURE_CALLBACK;
61 $fn();
63 if(!AK_PHP5 && $this->type() == 'sqlite'){
64 trigger_error(Ak::t("\nWarning, sqlite support is not available by default on PHP4.\n Check your PHP version by running \"env php -v\", and change the first line in your scripts/ so they point to a php5 binary\n\n"),E_USER_WARNING);
66 trigger_error(Ak::t("Connection to the database failed. %dsn",
67 array('%dsn'=> AK_DEBUG ? preg_replace('/\/\/(\w+):(.*)@/i','//$1:******@', urldecode($dsn))."\n" : '')),
68 E_USER_ERROR);
69 } else {
70 $this->connection->debug = AK_DEBUG == 2;
71 $this->connection->SetFetchMode(ADODB_FETCH_ASSOC);
72 defined('AK_DATABASE_CONNECTION_AVAILABLE') ? null : define('AK_DATABASE_CONNECTION_AVAILABLE', true);
76 function connected()
78 return !empty($this->connection);
82 /**
83 * @param array $database_settings
85 /* static */
86 function &getInstance($database_specifications = AK_DEFAULT_DATABASE_PROFILE, $auto_connect = true)
88 static $connections;
90 $settings_hash = is_string($database_specifications) ? $database_specifications : AkDbAdapter::_hash($database_specifications);
92 if (empty($connections[$settings_hash])){
93 global $database_settings;
94 if (is_string($database_specifications)){
95 if (!empty($database_settings[$database_specifications])){
96 $database_specifications = $database_settings[$database_specifications];
97 } elseif(strstr($database_specifications, '://')) {
98 $database_specifications = AkDbAdapter::_getDbSettingsFromDsn($database_specifications);
99 $settings_hash = AK_ENVIRONMENT;
100 } else {
101 trigger_error(Ak::t("Could not find the database profile '%profile_name' in config/config.php.",array('%profile_name'=>$database_specifications)),E_USER_ERROR);
102 $return = false;
103 return $return;
105 }elseif (!empty($database_settings[$settings_hash])){
106 $database_specifications = $database_settings[$settings_hash];
109 $available_adapters = Ak::toArray(AK_AVAILABLE_DATABASES);
110 $class_name = 'AkDbAdapter';
111 $designated_database = strtolower($database_specifications['type']);
112 if (in_array($designated_database, $available_adapters)) {
113 $class_name = 'Ak'.ucfirst($designated_database).'DbAdapter';
114 require_once(AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkDbAdapters'.DS.$class_name.'.php');
116 $connections[$settings_hash] =& new $class_name($database_specifications,$auto_connect);
118 return $connections[$settings_hash];
122 * @param array $settings
123 * @return string
125 function _hash($settings)
127 if(!is_array($settings)){
128 return AK_ENVIRONMENT;
130 if (isset($settings['password'])){
131 unset($settings['password']);
133 return join(':',$settings);
136 function &getDictionary()
138 if (empty($this->dictionary)){
139 if (!$this->connected()){
140 $this->connect();
142 require_once(AK_CONTRIB_DIR.DS.'adodb'.DS.'adodb.inc.php');
143 $this->dictionary =& NewDataDictionary($this->connection);
145 return $this->dictionary;
149 * @param array $database_settings
150 * @return string
152 function _constructDsn($database_settings)
154 if(is_string($database_settings)){
155 return $database_settings;
157 $dsn = $database_settings['type'].'://';
158 $dsn .= $database_settings['user'].':'.$database_settings['password'];
159 $dsn .= !empty($database_settings['host']) ? '@'.$database_settings['host'] : '@localhost';
160 $dsn .= !empty($database_settings['port']) ? ':'.$database_settings['port'] : '';
161 $dsn .= '/'.$database_settings['database_name'];
162 $dsn .= !empty($database_settings['options']) ? $database_settings['options'] : '';
163 return $dsn;
167 function _getDbSettingsFromDsn($dsn)
169 $settings = $result = parse_url($dsn);
170 $result['type'] = $settings['scheme'];
171 $result['password'] = $settings['pass'];
172 $result['database_name'] = trim($settings['path'],'/');
173 return $result;
176 function type()
178 return $this->settings['type'];
181 function debug($on = 'switch')
183 if ($on == 'switch') {
184 $this->debug = !$this->debug;
185 }else{
186 $this->debug = $on;
188 return $this->debug;
191 function _log($message)
193 if (!AK_LOG_EVENTS){
194 return;
196 $this->logger->message($message);
199 function addLimitAndOffset(&$sql,$options)
201 if (isset($options['limit']) && $limit = $options['limit']){
202 $sql .= " LIMIT $limit";
203 if (isset($options['offset']) && $offset = $options['offset']){
204 $sql .= " OFFSET $offset";
207 return $sql;
210 /* DATABASE STATEMENTS - CRUD */
212 function execute($sql, $message = 'SQL')
214 if (is_array($sql)) {
215 $sql_string = array_shift($sql);
216 $bindings = $sql;
217 } else $sql_string = $sql;
219 $this->_log($message.': '.$sql_string);
220 $result = isset($bindings) ? $this->connection->Execute($sql_string, $bindings) : $this->connection->Execute($sql_string);
222 if (!$result){
223 $error_message = '['.$this->connection->ErrorNo().'] '.$this->connection->ErrorMsg();
224 $this->_log('SQL Error: '.$error_message);
225 if ($this->debug || AK_DEBUG) trigger_error("Tried '$sql_string'. Got: $error_message", E_USER_NOTICE);
227 return $result;
230 function incrementsPrimaryKeyAutomatically()
232 return true;
235 function getLastInsertedId($table,$pk)
237 return $this->connection->Insert_ID($table,$pk);
240 function getAffectedRows()
242 return $this->connection->Affected_Rows();
245 function insert($sql,$id=null,$pk=null,$table=null,$message = '')
247 $result = $this->execute($sql,$message);
248 if (!$result){
249 return false;
251 return is_null($id) ? $this->getLastInsertedId($table,$pk) : $id;
254 function update($sql,$message = '')
256 $result = $this->execute($sql,$message);
257 return ($result) ? $this->getAffectedRows() : false;
260 function delete($sql,$message = '')
262 $result = $this->execute($sql,$message);
263 return ($result) ? $this->getAffectedRows() : false;
267 * Returns a single value, the first column from the first row, from a record
269 function selectValue($sql)
271 $result = $this->selectOne($sql);
272 return !is_null($result) ? array_shift($result) : null;
276 * Returns an array of the values of the first column in a select:
277 * sqlSelectValues("SELECT id FROM companies LIMIT 3") => array(1,2,3)
279 function selectValues($sql)
281 $values = array();
282 if($results = $this->select($sql)){
283 foreach ($results as $result){
284 $values[] = array_shift($result);
287 return $values;
291 * Returns a record array of the first row with the column names as keys and column values
292 * as values.
294 function selectOne($sql)
296 $result = $this->select($sql);
297 return !is_null($result) ? array_shift($result) : null;
301 * alias for select
303 function selectAll($sql)
305 return $this->select($sql);
309 * Returns an array of record hashes with the column names as keys and
310 * column values as values.
312 function select($sql, $message = '')
314 $result = $this->execute($sql, $message);
315 if (!$result){
316 return array();
319 $records = array();
320 while ($record = $result->FetchRow()) {
321 $records[] = $record;
323 $result->Close();
324 return $records;
327 /* TRANSACTIONS */
329 function startTransaction()
331 return $this->connection->StartTrans();
334 function stopTransaction()
336 return $this->connection->CompleteTrans();
339 function failTransaction()
341 return $this->connection->FailTrans();
344 function hasTransactionFailed()
346 return $this->connection->HasFailedTrans();
349 /* SCHEMA */
351 function renameColumn($table_name,$column_name,$new_name)
353 trigger_error(Ak::t('renameColumn is not available for your DbAdapter. Using %db_type.',array('%db_type'=>$this->type())));
356 /* META */
358 function availableTables()
360 return $this->connection->MetaTables();
363 function getColumnDetails($table_name)
365 return $this->connection->MetaColumns($table_name);
368 function getIndexes($table_name)
370 return $this->connection->MetaIndexes($table_name);
373 /* QUOTING */
375 function quote_string($value)
377 return $this->connection->qstr($value);
380 function quote_datetime($value)
382 return $this->connection->DBTimeStamp($value);
385 function quote_date($value)
387 return $this->connection->DBDate($value);
390 // will be moved to postgre
391 function escape_blob($value)
393 return $this->connection->BlobEncode($value);
396 // will be moved to postgre
397 function unescape_blob($value)
399 return $this->connection->BlobDecode($value);