blob: 271a70ec45082d24c3f122e713f674de38caf7b8 [file] [log] [blame]
Derek Jones37f4b9c2011-07-01 17:56:50 -05001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
8 *
9 * Licensed under the Open Software License version 3.0
10 *
11 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Derek Allard2067d1a2008-11-13 22:59:24 +000019 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
Greg Aker0defe5d2012-01-01 18:46:41 -060021 * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
Derek Jonesf4a4bd82011-10-20 12:18:42 -050022 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Derek Allard2067d1a2008-11-13 22:59:24 +000023 * @link http://codeigniter.com
24 * @since Version 1.0
25 * @filesource
26 */
27
28// ------------------------------------------------------------------------
29
30/**
31 * Database Driver Class
32 *
33 * This is the platform-independent base DB implementation class.
34 * This class will not be called directly. Rather, the adapter
35 * class for the specific database will extend and instantiate it.
36 *
37 * @package CodeIgniter
38 * @subpackage Drivers
39 * @category Database
Derek Jonesf4a4bd82011-10-20 12:18:42 -050040 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000041 * @link http://codeigniter.com/user_guide/database/
42 */
43class CI_DB_driver {
44
45 var $username;
46 var $password;
47 var $hostname;
48 var $database;
49 var $dbdriver = 'mysql';
50 var $dbprefix = '';
51 var $char_set = 'utf8';
52 var $dbcollat = 'utf8_general_ci';
53 var $autoinit = TRUE; // Whether to automatically initialize the DB
54 var $swap_pre = '';
55 var $port = '';
56 var $pconnect = FALSE;
57 var $conn_id = FALSE;
58 var $result_id = FALSE;
59 var $db_debug = FALSE;
60 var $benchmark = 0;
61 var $query_count = 0;
62 var $bind_marker = '?';
63 var $save_queries = TRUE;
64 var $queries = array();
65 var $query_times = array();
66 var $data_cache = array();
67 var $trans_enabled = TRUE;
68 var $trans_strict = TRUE;
69 var $_trans_depth = 0;
70 var $_trans_status = TRUE; // Used with transactions to determine if a rollback should occur
71 var $cache_on = FALSE;
72 var $cachedir = '';
73 var $cache_autodel = FALSE;
74 var $CACHE; // The cache class object
75
76 // Private variables
77 var $_protect_identifiers = TRUE;
78 var $_reserved_identifiers = array('*'); // Identifiers that should NOT be escaped
79
80 // These are use with Oracle
81 var $stmt_id;
82 var $curs_id;
83 var $limit_used;
Taufan Aditya18209332012-02-09 16:07:27 +070084
Barry Mienydd671972010-10-04 16:33:58 +020085
Derek Allard2067d1a2008-11-13 22:59:24 +000086 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -050087 * Constructor. Accepts one parameter containing the database
Derek Allard2067d1a2008-11-13 22:59:24 +000088 * connection settings.
89 *
90 * @param array
Barry Mienydd671972010-10-04 16:33:58 +020091 */
Timothy Warrena2097a02011-10-10 10:10:46 -040092 function __construct($params)
Derek Allard2067d1a2008-11-13 22:59:24 +000093 {
94 if (is_array($params))
95 {
96 foreach ($params as $key => $val)
97 {
98 $this->$key = $val;
99 }
100 }
101
102 log_message('debug', 'Database Driver Class Initialized');
103 }
Barry Mienydd671972010-10-04 16:33:58 +0200104
Derek Allard2067d1a2008-11-13 22:59:24 +0000105 // --------------------------------------------------------------------
106
107 /**
108 * Initialize Database Settings
109 *
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200110 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200111 */
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200112 public function initialize()
Derek Allard2067d1a2008-11-13 22:59:24 +0000113 {
114 // If an existing connection resource is available
115 // there is no need to connect and select the database
116 if (is_resource($this->conn_id) OR is_object($this->conn_id))
117 {
118 return TRUE;
119 }
Barry Mienydd671972010-10-04 16:33:58 +0200120
Derek Allard2067d1a2008-11-13 22:59:24 +0000121 // ----------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200122
Derek Allard2067d1a2008-11-13 22:59:24 +0000123 // Connect to the database and set the connection ID
124 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
125
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200126 // No connection resource? Check if there is a failover else throw an error
Derek Allard2067d1a2008-11-13 22:59:24 +0000127 if ( ! $this->conn_id)
128 {
Felix Balfoort5d581b62011-11-29 15:53:01 +0100129 // Check if there is a failover set
130 if ( ! empty($this->failover) && is_array($this->failover))
Derek Allard2067d1a2008-11-13 22:59:24 +0000131 {
Felix Balfoort5d581b62011-11-29 15:53:01 +0100132 // Go over all the failovers
133 foreach ($this->failover as $failover)
134 {
135 // Replace the current settings with those of the failover
136 foreach ($failover as $key => $val)
137 {
138 $this->$key = $val;
139 }
140
141 // Try to connect
142 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
143
144 // If a connection is made break the foreach loop
145 if ($this->conn_id)
146 {
147 break;
148 }
149 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000150 }
Felix Balfoort5d581b62011-11-29 15:53:01 +0100151
152 // We still don't have a connection?
153 if ( ! $this->conn_id)
154 {
155 log_message('error', 'Unable to connect to the database');
156
157 if ($this->db_debug)
158 {
159 $this->display_error('db_unable_to_connect');
160 }
161 return FALSE;
162 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000163 }
164
165 // ----------------------------------------------------------------
166
167 // Select the DB... assuming a database name is specified in the config file
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200168 if ($this->database !== '' && ! $this->db_select())
Derek Allard2067d1a2008-11-13 22:59:24 +0000169 {
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200170 log_message('error', 'Unable to select database: '.$this->database);
Barry Mienydd671972010-10-04 16:33:58 +0200171
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200172 if ($this->db_debug)
Derek Allard2067d1a2008-11-13 22:59:24 +0000173 {
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200174 $this->display_error('db_unable_to_select', $this->database);
Derek Allard2067d1a2008-11-13 22:59:24 +0000175 }
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200176 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000177 }
178
Andrey Andreev82e8ac12012-02-22 19:35:34 +0200179 // Now we set the character set and that's all
180 return $this->db_set_charset($this->char_set, $this->dbcollat);
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 }
Barry Mienydd671972010-10-04 16:33:58 +0200182
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 // --------------------------------------------------------------------
184
185 /**
186 * Set client character set
187 *
188 * @access public
189 * @param string
190 * @param string
191 * @return resource
192 */
193 function db_set_charset($charset, $collation)
194 {
195 if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat))
196 {
197 log_message('error', 'Unable to set database connection charset: '.$this->char_set);
Barry Mienydd671972010-10-04 16:33:58 +0200198
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 if ($this->db_debug)
200 {
201 $this->display_error('db_unable_to_set_charset', $this->char_set);
202 }
Barry Mienydd671972010-10-04 16:33:58 +0200203
Derek Allard2067d1a2008-11-13 22:59:24 +0000204 return FALSE;
205 }
Barry Mienydd671972010-10-04 16:33:58 +0200206
Derek Allard2067d1a2008-11-13 22:59:24 +0000207 return TRUE;
208 }
Barry Mienydd671972010-10-04 16:33:58 +0200209
Derek Allard2067d1a2008-11-13 22:59:24 +0000210 // --------------------------------------------------------------------
211
212 /**
213 * The name of the platform in use (mysql, mssql, etc...)
214 *
215 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200216 * @return string
217 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000218 function platform()
219 {
220 return $this->dbdriver;
221 }
222
223 // --------------------------------------------------------------------
224
225 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -0500226 * Database Version Number. Returns a string containing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000227 * version of the database being used
228 *
229 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200230 * @return string
231 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 function version()
233 {
234 if (FALSE === ($sql = $this->_version()))
235 {
236 if ($this->db_debug)
237 {
238 return $this->display_error('db_unsupported_function');
239 }
240 return FALSE;
241 }
Derek Allard3683f772009-12-16 17:32:33 +0000242
243 // Some DBs have functions that return the version, and don't run special
244 // SQL queries per se. In these instances, just return the result.
Andrey Andreev1ff49e02012-01-27 11:30:41 +0200245 $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid', 'pdo', 'mysqli');
Derek Allard3683f772009-12-16 17:32:33 +0000246
247 if (in_array($this->dbdriver, $driver_version_exceptions))
Derek Allard2067d1a2008-11-13 22:59:24 +0000248 {
249 return $sql;
250 }
Derek Allard3683f772009-12-16 17:32:33 +0000251 else
252 {
253 $query = $this->query($sql);
254 return $query->row('ver');
255 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000256 }
Barry Mienydd671972010-10-04 16:33:58 +0200257
Derek Allard2067d1a2008-11-13 22:59:24 +0000258 // --------------------------------------------------------------------
259
260 /**
261 * Execute the query
262 *
263 * Accepts an SQL string as input and returns a result object upon
Derek Jones37f4b9c2011-07-01 17:56:50 -0500264 * successful execution of a "read" type query. Returns boolean TRUE
Derek Allard2067d1a2008-11-13 22:59:24 +0000265 * upon successful execution of a "write" type query. Returns boolean
266 * FALSE upon failure, and if the $db_debug variable is set to TRUE
267 * will raise an error.
268 *
269 * @access public
270 * @param string An SQL query string
271 * @param array An array of binding data
Barry Mienydd671972010-10-04 16:33:58 +0200272 * @return mixed
273 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000274 function query($sql, $binds = FALSE, $return_object = TRUE)
275 {
276 if ($sql == '')
277 {
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200278 log_message('error', 'Invalid query: '.$sql);
279
Derek Allard2067d1a2008-11-13 22:59:24 +0000280 if ($this->db_debug)
281 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000282 return $this->display_error('db_invalid_query');
283 }
284 return FALSE;
285 }
286
287 // Verify table prefix and replace if necessary
288 if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
Derek Jonese7792202010-03-02 17:24:46 -0600289 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000290 $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
291 }
Derek Jonese7792202010-03-02 17:24:46 -0600292
Derek Jones37f4b9c2011-07-01 17:56:50 -0500293 // Is query caching enabled? If the query is a "read type"
Derek Allard2067d1a2008-11-13 22:59:24 +0000294 // we will load the caching class and return the previously
295 // cached query if it exists
296 if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
297 {
298 if ($this->_cache_init())
299 {
300 $this->load_rdriver();
301 if (FALSE !== ($cache = $this->CACHE->read($sql)))
302 {
303 return $cache;
304 }
305 }
306 }
Barry Mienydd671972010-10-04 16:33:58 +0200307
Derek Allard2067d1a2008-11-13 22:59:24 +0000308 // Compile binds if needed
309 if ($binds !== FALSE)
310 {
311 $sql = $this->compile_binds($sql, $binds);
312 }
313
Derek Jones37f4b9c2011-07-01 17:56:50 -0500314 // Save the query for debugging
Derek Allard2067d1a2008-11-13 22:59:24 +0000315 if ($this->save_queries == TRUE)
316 {
317 $this->queries[] = $sql;
318 }
Barry Mienydd671972010-10-04 16:33:58 +0200319
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 // Start the Query Timer
321 $time_start = list($sm, $ss) = explode(' ', microtime());
Barry Mienydd671972010-10-04 16:33:58 +0200322
Derek Allard2067d1a2008-11-13 22:59:24 +0000323 // Run the Query
324 if (FALSE === ($this->result_id = $this->simple_query($sql)))
325 {
326 if ($this->save_queries == TRUE)
327 {
328 $this->query_times[] = 0;
329 }
Barry Mienydd671972010-10-04 16:33:58 +0200330
Derek Allard2067d1a2008-11-13 22:59:24 +0000331 // This will trigger a rollback if transactions are being used
332 $this->_trans_status = FALSE;
333
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200334 // Grab the error number and message now, as we might run some
335 // additional queries before displaying the error
336 $error_no = $this->_error_number();
337 $error_msg = $this->_error_message();
338
339 // Log errors
340 log_message('error', 'Query error: '.$error_msg);
341
Derek Allard2067d1a2008-11-13 22:59:24 +0000342 if ($this->db_debug)
343 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 // We call this function in order to roll-back queries
Derek Jones37f4b9c2011-07-01 17:56:50 -0500345 // if transactions are enabled. If we don't call this here
Barry Mienydd671972010-10-04 16:33:58 +0200346 // the error message will trigger an exit, causing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000347 // transactions to remain in limbo.
348 $this->trans_complete();
349
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200350 // Display errors
Derek Allard2067d1a2008-11-13 22:59:24 +0000351 return $this->display_error(
352 array(
353 'Error Number: '.$error_no,
354 $error_msg,
355 $sql
356 )
357 );
358 }
Barry Mienydd671972010-10-04 16:33:58 +0200359
Derek Allard2067d1a2008-11-13 22:59:24 +0000360 return FALSE;
361 }
Barry Mienydd671972010-10-04 16:33:58 +0200362
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 // Stop and aggregate the query time results
364 $time_end = list($em, $es) = explode(' ', microtime());
365 $this->benchmark += ($em + $es) - ($sm + $ss);
366
367 if ($this->save_queries == TRUE)
368 {
369 $this->query_times[] = ($em + $es) - ($sm + $ss);
370 }
Barry Mienydd671972010-10-04 16:33:58 +0200371
Derek Allard2067d1a2008-11-13 22:59:24 +0000372 // Increment the query counter
373 $this->query_count++;
Barry Mienydd671972010-10-04 16:33:58 +0200374
Derek Allard2067d1a2008-11-13 22:59:24 +0000375 // Was the query a "write" type?
376 // If so we'll simply return true
377 if ($this->is_write_type($sql) === TRUE)
378 {
379 // If caching is enabled we'll auto-cleanup any
380 // existing files related to this particular URI
381 if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
382 {
383 $this->CACHE->delete();
384 }
Barry Mienydd671972010-10-04 16:33:58 +0200385
Derek Allard2067d1a2008-11-13 22:59:24 +0000386 return TRUE;
387 }
Barry Mienydd671972010-10-04 16:33:58 +0200388
Derek Allard2067d1a2008-11-13 22:59:24 +0000389 // Return TRUE if we don't need to create a result object
390 // Currently only the Oracle driver uses this when stored
391 // procedures are used
392 if ($return_object !== TRUE)
393 {
394 return TRUE;
395 }
Barry Mienydd671972010-10-04 16:33:58 +0200396
397 // Load and instantiate the result driver
398
399 $driver = $this->load_rdriver();
400 $RES = new $driver();
Derek Allard2067d1a2008-11-13 22:59:24 +0000401 $RES->conn_id = $this->conn_id;
402 $RES->result_id = $this->result_id;
403
404 if ($this->dbdriver == 'oci8')
405 {
406 $RES->stmt_id = $this->stmt_id;
407 $RES->curs_id = NULL;
408 $RES->limit_used = $this->limit_used;
409 $this->stmt_id = FALSE;
410 }
Barry Mienydd671972010-10-04 16:33:58 +0200411
Derek Allard2067d1a2008-11-13 22:59:24 +0000412 // oci8 vars must be set before calling this
413 $RES->num_rows = $RES->num_rows();
Barry Mienydd671972010-10-04 16:33:58 +0200414
Derek Jones37f4b9c2011-07-01 17:56:50 -0500415 // Is query caching enabled? If so, we'll serialize the
Derek Allard2067d1a2008-11-13 22:59:24 +0000416 // result object and save it to a cache file.
417 if ($this->cache_on == TRUE AND $this->_cache_init())
418 {
419 // We'll create a new instance of the result object
420 // only without the platform specific driver since
421 // we can't use it with cached data (the query result
422 // resource ID won't be any good once we've cached the
423 // result object, so we'll have to compile the data
424 // and save it)
425 $CR = new CI_DB_result();
Barry Mienydd671972010-10-04 16:33:58 +0200426 $CR->num_rows = $RES->num_rows();
Derek Allard2067d1a2008-11-13 22:59:24 +0000427 $CR->result_object = $RES->result_object();
428 $CR->result_array = $RES->result_array();
Barry Mienydd671972010-10-04 16:33:58 +0200429
Derek Allard2067d1a2008-11-13 22:59:24 +0000430 // Reset these since cached objects can not utilize resource IDs.
431 $CR->conn_id = NULL;
432 $CR->result_id = NULL;
433
434 $this->CACHE->write($sql, $CR);
435 }
Barry Mienydd671972010-10-04 16:33:58 +0200436
Derek Allard2067d1a2008-11-13 22:59:24 +0000437 return $RES;
438 }
439
440 // --------------------------------------------------------------------
441
442 /**
443 * Load the result drivers
444 *
445 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200446 * @return string the name of the result class
447 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000448 function load_rdriver()
449 {
450 $driver = 'CI_DB_'.$this->dbdriver.'_result';
451
452 if ( ! class_exists($driver))
453 {
Greg Aker3a746652011-04-19 10:59:47 -0500454 include_once(BASEPATH.'database/DB_result.php');
455 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
Derek Allard2067d1a2008-11-13 22:59:24 +0000456 }
Barry Mienydd671972010-10-04 16:33:58 +0200457
Derek Allard2067d1a2008-11-13 22:59:24 +0000458 return $driver;
459 }
Barry Mienydd671972010-10-04 16:33:58 +0200460
Derek Allard2067d1a2008-11-13 22:59:24 +0000461 // --------------------------------------------------------------------
462
463 /**
464 * Simple Query
Derek Jones37f4b9c2011-07-01 17:56:50 -0500465 * This is a simplified version of the query() function. Internally
Derek Allard2067d1a2008-11-13 22:59:24 +0000466 * we only use it when running transaction commands since they do
467 * not require all the features of the main query() function.
468 *
469 * @access public
470 * @param string the sql query
Barry Mienydd671972010-10-04 16:33:58 +0200471 * @return mixed
472 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000473 function simple_query($sql)
474 {
475 if ( ! $this->conn_id)
476 {
477 $this->initialize();
478 }
479
480 return $this->_execute($sql);
481 }
Barry Mienydd671972010-10-04 16:33:58 +0200482
Derek Allard2067d1a2008-11-13 22:59:24 +0000483 // --------------------------------------------------------------------
484
485 /**
486 * Disable Transactions
487 * This permits transactions to be disabled at run-time.
488 *
489 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200490 * @return void
491 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000492 function trans_off()
493 {
494 $this->trans_enabled = FALSE;
495 }
496
497 // --------------------------------------------------------------------
498
499 /**
500 * Enable/disable Transaction Strict Mode
501 * When strict mode is enabled, if you are running multiple groups of
502 * transactions, if one group fails all groups will be rolled back.
503 * If strict mode is disabled, each group is treated autonomously, meaning
504 * a failure of one group will not affect any others
505 *
506 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200507 * @return void
508 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000509 function trans_strict($mode = TRUE)
510 {
511 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
512 }
Barry Mienydd671972010-10-04 16:33:58 +0200513
Derek Allard2067d1a2008-11-13 22:59:24 +0000514 // --------------------------------------------------------------------
515
516 /**
517 * Start Transaction
518 *
519 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200520 * @return void
521 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000522 function trans_start($test_mode = FALSE)
Barry Mienydd671972010-10-04 16:33:58 +0200523 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 if ( ! $this->trans_enabled)
525 {
526 return FALSE;
527 }
528
529 // When transactions are nested we only begin/commit/rollback the outermost ones
530 if ($this->_trans_depth > 0)
531 {
532 $this->_trans_depth += 1;
533 return;
534 }
Barry Mienydd671972010-10-04 16:33:58 +0200535
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 $this->trans_begin($test_mode);
Jacob Terry07fcedf2011-11-22 11:21:36 -0500537 $this->_trans_depth += 1;
Derek Allard2067d1a2008-11-13 22:59:24 +0000538 }
539
540 // --------------------------------------------------------------------
541
542 /**
543 * Complete Transaction
544 *
545 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200546 * @return bool
547 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000548 function trans_complete()
549 {
550 if ( ! $this->trans_enabled)
551 {
552 return FALSE;
553 }
Barry Mienydd671972010-10-04 16:33:58 +0200554
Derek Allard2067d1a2008-11-13 22:59:24 +0000555 // When transactions are nested we only begin/commit/rollback the outermost ones
556 if ($this->_trans_depth > 1)
557 {
558 $this->_trans_depth -= 1;
559 return TRUE;
560 }
Jacob Terry07fcedf2011-11-22 11:21:36 -0500561 else
562 {
563 $this->_trans_depth = 0;
564 }
Barry Mienydd671972010-10-04 16:33:58 +0200565
Derek Allard2067d1a2008-11-13 22:59:24 +0000566 // The query() function will set this flag to FALSE in the event that a query failed
567 if ($this->_trans_status === FALSE)
568 {
569 $this->trans_rollback();
Barry Mienydd671972010-10-04 16:33:58 +0200570
Derek Allard2067d1a2008-11-13 22:59:24 +0000571 // If we are NOT running in strict mode, we will reset
572 // the _trans_status flag so that subsequent groups of transactions
573 // will be permitted.
574 if ($this->trans_strict === FALSE)
575 {
576 $this->_trans_status = TRUE;
577 }
578
579 log_message('debug', 'DB Transaction Failure');
580 return FALSE;
581 }
Barry Mienydd671972010-10-04 16:33:58 +0200582
Derek Allard2067d1a2008-11-13 22:59:24 +0000583 $this->trans_commit();
584 return TRUE;
585 }
586
587 // --------------------------------------------------------------------
588
589 /**
590 * Lets you retrieve the transaction flag to determine if it has failed
591 *
592 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200593 * @return bool
594 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000595 function trans_status()
596 {
597 return $this->_trans_status;
598 }
599
600 // --------------------------------------------------------------------
601
602 /**
603 * Compile Bindings
604 *
605 * @access public
606 * @param string the sql statement
607 * @param array an array of bind data
Barry Mienydd671972010-10-04 16:33:58 +0200608 * @return string
609 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000610 function compile_binds($sql, $binds)
611 {
612 if (strpos($sql, $this->bind_marker) === FALSE)
613 {
614 return $sql;
615 }
Barry Mienydd671972010-10-04 16:33:58 +0200616
Derek Allard2067d1a2008-11-13 22:59:24 +0000617 if ( ! is_array($binds))
618 {
619 $binds = array($binds);
620 }
Barry Mienydd671972010-10-04 16:33:58 +0200621
Derek Allard2067d1a2008-11-13 22:59:24 +0000622 // Get the sql segments around the bind markers
623 $segments = explode($this->bind_marker, $sql);
624
625 // The count of bind should be 1 less then the count of segments
626 // If there are more bind arguments trim it down
627 if (count($binds) >= count($segments)) {
628 $binds = array_slice($binds, 0, count($segments)-1);
629 }
630
631 // Construct the binded query
632 $result = $segments[0];
633 $i = 0;
634 foreach ($binds as $bind)
635 {
636 $result .= $this->escape($bind);
637 $result .= $segments[++$i];
638 }
639
640 return $result;
641 }
Barry Mienydd671972010-10-04 16:33:58 +0200642
Derek Allard2067d1a2008-11-13 22:59:24 +0000643 // --------------------------------------------------------------------
644
645 /**
646 * Determines if a query is a "write" type.
647 *
648 * @access public
649 * @param string An SQL query string
Barry Mienydd671972010-10-04 16:33:58 +0200650 * @return boolean
651 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000652 function is_write_type($sql)
653 {
Derek Allarde37ab382009-02-03 16:13:57 +0000654 if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
Derek Allard2067d1a2008-11-13 22:59:24 +0000655 {
656 return FALSE;
657 }
658 return TRUE;
659 }
Barry Mienydd671972010-10-04 16:33:58 +0200660
Derek Allard2067d1a2008-11-13 22:59:24 +0000661 // --------------------------------------------------------------------
662
663 /**
664 * Calculate the aggregate query elapsed time
665 *
666 * @access public
667 * @param integer The number of decimal places
Barry Mienydd671972010-10-04 16:33:58 +0200668 * @return integer
669 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000670 function elapsed_time($decimals = 6)
671 {
672 return number_format($this->benchmark, $decimals);
673 }
Barry Mienydd671972010-10-04 16:33:58 +0200674
Derek Allard2067d1a2008-11-13 22:59:24 +0000675 // --------------------------------------------------------------------
676
677 /**
678 * Returns the total number of queries
679 *
680 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200681 * @return integer
682 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000683 function total_queries()
684 {
685 return $this->query_count;
686 }
Barry Mienydd671972010-10-04 16:33:58 +0200687
Derek Allard2067d1a2008-11-13 22:59:24 +0000688 // --------------------------------------------------------------------
689
690 /**
691 * Returns the last query that was executed
692 *
693 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200694 * @return void
695 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000696 function last_query()
697 {
698 return end($this->queries);
699 }
700
701 // --------------------------------------------------------------------
702
703 /**
704 * "Smart" Escape String
705 *
706 * Escapes data based on type
707 * Sets boolean and null types
708 *
709 * @access public
710 * @param string
Barry Mienydd671972010-10-04 16:33:58 +0200711 * @return mixed
712 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000713 function escape($str)
Barry Mienydd671972010-10-04 16:33:58 +0200714 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000715 if (is_string($str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000716 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000717 $str = "'".$this->escape_str($str)."'";
718 }
719 elseif (is_bool($str))
720 {
721 $str = ($str === FALSE) ? 0 : 1;
722 }
723 elseif (is_null($str))
724 {
725 $str = 'NULL';
726 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000727
728 return $str;
729 }
730
731 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600732
Derek Jonese4ed5832009-02-20 21:44:59 +0000733 /**
Derek Jonesbdc7fb92009-02-20 21:55:10 +0000734 * Escape LIKE String
Derek Jonese4ed5832009-02-20 21:44:59 +0000735 *
736 * Calls the individual driver for platform
737 * specific escaping for LIKE conditions
Barry Mienydd671972010-10-04 16:33:58 +0200738 *
Derek Jonese4ed5832009-02-20 21:44:59 +0000739 * @access public
740 * @param string
741 * @return mixed
742 */
Barry Mienydd671972010-10-04 16:33:58 +0200743 function escape_like_str($str)
744 {
745 return $this->escape_str($str, TRUE);
Derek Jonese4ed5832009-02-20 21:44:59 +0000746 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000747
Derek Jonese4ed5832009-02-20 21:44:59 +0000748 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600749
Derek Allard2067d1a2008-11-13 22:59:24 +0000750 /**
751 * Primary
752 *
Derek Jones37f4b9c2011-07-01 17:56:50 -0500753 * Retrieves the primary key. It assumes that the row in the first
Derek Allard2067d1a2008-11-13 22:59:24 +0000754 * position is the primary key
755 *
756 * @access public
757 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200758 * @return string
759 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000760 function primary($table = '')
Barry Mienydd671972010-10-04 16:33:58 +0200761 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000762 $fields = $this->list_fields($table);
Barry Mienydd671972010-10-04 16:33:58 +0200763
Derek Allard2067d1a2008-11-13 22:59:24 +0000764 if ( ! is_array($fields))
765 {
766 return FALSE;
767 }
768
769 return current($fields);
770 }
771
772 // --------------------------------------------------------------------
773
774 /**
775 * Returns an array of table names
776 *
777 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200778 * @return array
779 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000780 function list_tables($constrain_by_prefix = FALSE)
781 {
782 // Is there a cached result?
783 if (isset($this->data_cache['table_names']))
784 {
785 return $this->data_cache['table_names'];
786 }
Barry Mienydd671972010-10-04 16:33:58 +0200787
Derek Allard2067d1a2008-11-13 22:59:24 +0000788 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
789 {
790 if ($this->db_debug)
791 {
792 return $this->display_error('db_unsupported_function');
793 }
794 return FALSE;
795 }
796
797 $retval = array();
798 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200799
Derek Allard2067d1a2008-11-13 22:59:24 +0000800 if ($query->num_rows() > 0)
801 {
Taufan Aditya18209332012-02-09 16:07:27 +0700802 $table = FALSE;
803 $rows = $query->result_array();
804 $key = (($row = current($rows)) && in_array('table_name', array_map('strtolower', array_keys($row))));
805
806 if ($key)
Derek Allard2067d1a2008-11-13 22:59:24 +0000807 {
Taufan Aditya18209332012-02-09 16:07:27 +0700808 $table = array_key_exists('TABLE_NAME', $row) ? 'TABLE_NAME' : 'table_name';
809 }
810
811 foreach ($rows as $row)
812 {
813 $retval[] = ( ! $table) ? current($row) : $row[$table];
Derek Allard2067d1a2008-11-13 22:59:24 +0000814 }
815 }
816
817 $this->data_cache['table_names'] = $retval;
Taufan Aditya18209332012-02-09 16:07:27 +0700818
Derek Allard2067d1a2008-11-13 22:59:24 +0000819 return $this->data_cache['table_names'];
820 }
Barry Mienydd671972010-10-04 16:33:58 +0200821
Derek Allard2067d1a2008-11-13 22:59:24 +0000822 // --------------------------------------------------------------------
823
824 /**
825 * Determine if a particular table exists
826 * @access public
827 * @return boolean
828 */
829 function table_exists($table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200830 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000831 return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE;
832 }
Barry Mienydd671972010-10-04 16:33:58 +0200833
Derek Allard2067d1a2008-11-13 22:59:24 +0000834 // --------------------------------------------------------------------
835
836 /**
837 * Fetch MySQL Field Names
838 *
839 * @access public
840 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200841 * @return array
Derek Allard2067d1a2008-11-13 22:59:24 +0000842 */
843 function list_fields($table = '')
844 {
845 // Is there a cached result?
846 if (isset($this->data_cache['field_names'][$table]))
847 {
848 return $this->data_cache['field_names'][$table];
849 }
Barry Mienydd671972010-10-04 16:33:58 +0200850
Derek Allard2067d1a2008-11-13 22:59:24 +0000851 if ($table == '')
852 {
853 if ($this->db_debug)
854 {
855 return $this->display_error('db_field_param_missing');
856 }
857 return FALSE;
858 }
Barry Mienydd671972010-10-04 16:33:58 +0200859
Greg Aker1edde302010-01-26 00:17:01 +0000860 if (FALSE === ($sql = $this->_list_columns($table)))
Derek Allard2067d1a2008-11-13 22:59:24 +0000861 {
862 if ($this->db_debug)
863 {
864 return $this->display_error('db_unsupported_function');
865 }
866 return FALSE;
867 }
Barry Mienydd671972010-10-04 16:33:58 +0200868
Derek Allard2067d1a2008-11-13 22:59:24 +0000869 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200870
Derek Allard2067d1a2008-11-13 22:59:24 +0000871 $retval = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500872 foreach ($query->result_array() as $row)
Derek Allard2067d1a2008-11-13 22:59:24 +0000873 {
874 if (isset($row['COLUMN_NAME']))
875 {
876 $retval[] = $row['COLUMN_NAME'];
877 }
878 else
879 {
880 $retval[] = current($row);
Barry Mienydd671972010-10-04 16:33:58 +0200881 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000882 }
Barry Mienydd671972010-10-04 16:33:58 +0200883
Derek Allard2067d1a2008-11-13 22:59:24 +0000884 $this->data_cache['field_names'][$table] = $retval;
885 return $this->data_cache['field_names'][$table];
886 }
887
888 // --------------------------------------------------------------------
889
890 /**
891 * Determine if a particular field exists
892 * @access public
893 * @param string
894 * @param string
895 * @return boolean
896 */
897 function field_exists($field_name, $table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200898 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000899 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
900 }
Barry Mienydd671972010-10-04 16:33:58 +0200901
Derek Allard2067d1a2008-11-13 22:59:24 +0000902 // --------------------------------------------------------------------
903
904 /**
905 * Returns an object with field data
906 *
907 * @access public
908 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200909 * @return object
910 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000911 function field_data($table = '')
912 {
913 if ($table == '')
914 {
915 if ($this->db_debug)
916 {
917 return $this->display_error('db_field_param_missing');
918 }
919 return FALSE;
920 }
Barry Mienydd671972010-10-04 16:33:58 +0200921
Derek Allard2067d1a2008-11-13 22:59:24 +0000922 $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE)));
923
924 return $query->field_data();
Barry Mienydd671972010-10-04 16:33:58 +0200925 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000926
927 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200928
Derek Allard2067d1a2008-11-13 22:59:24 +0000929 /**
930 * Generate an insert string
931 *
932 * @access public
933 * @param string the table upon which the query will be performed
934 * @param array an associative array data of key/values
Barry Mienydd671972010-10-04 16:33:58 +0200935 * @return string
936 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000937 function insert_string($table, $data)
938 {
939 $fields = array();
940 $values = array();
Barry Mienydd671972010-10-04 16:33:58 +0200941
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500942 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000943 {
944 $fields[] = $this->_escape_identifiers($key);
945 $values[] = $this->escape($val);
946 }
Barry Mienydd671972010-10-04 16:33:58 +0200947
Derek Allard2067d1a2008-11-13 22:59:24 +0000948 return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
Barry Mienydd671972010-10-04 16:33:58 +0200949 }
950
Derek Allard2067d1a2008-11-13 22:59:24 +0000951 // --------------------------------------------------------------------
952
953 /**
954 * Generate an update string
955 *
956 * @access public
957 * @param string the table upon which the query will be performed
958 * @param array an associative array data of key/values
959 * @param mixed the "where" statement
Barry Mienydd671972010-10-04 16:33:58 +0200960 * @return string
961 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000962 function update_string($table, $data, $where)
963 {
964 if ($where == '')
965 {
966 return false;
967 }
Barry Mienydd671972010-10-04 16:33:58 +0200968
Derek Allard2067d1a2008-11-13 22:59:24 +0000969 $fields = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500970 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000971 {
972 $fields[$this->_protect_identifiers($key)] = $this->escape($val);
973 }
974
975 if ( ! is_array($where))
976 {
977 $dest = array($where);
978 }
979 else
980 {
981 $dest = array();
982 foreach ($where as $key => $val)
983 {
984 $prefix = (count($dest) == 0) ? '' : ' AND ';
Andrey Andreevdc46d992011-09-24 16:25:23 +0300985 $key = $this->_protect_identifiers($key);
Barry Mienydd671972010-10-04 16:33:58 +0200986
Derek Allard2067d1a2008-11-13 22:59:24 +0000987 if ($val !== '')
988 {
989 if ( ! $this->_has_operator($key))
990 {
991 $key .= ' =';
992 }
Barry Mienydd671972010-10-04 16:33:58 +0200993
Derek Allard2067d1a2008-11-13 22:59:24 +0000994 $val = ' '.$this->escape($val);
995 }
Barry Mienydd671972010-10-04 16:33:58 +0200996
Derek Allard2067d1a2008-11-13 22:59:24 +0000997 $dest[] = $prefix.$key.$val;
998 }
Barry Mienydd671972010-10-04 16:33:58 +0200999 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001000
1001 return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest);
Barry Mienydd671972010-10-04 16:33:58 +02001002 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001003
1004 // --------------------------------------------------------------------
1005
1006 /**
1007 * Tests whether the string has an SQL operator
1008 *
1009 * @access private
1010 * @param string
1011 * @return bool
1012 */
1013 function _has_operator($str)
1014 {
1015 $str = trim($str);
1016 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
1017 {
1018 return FALSE;
1019 }
1020
1021 return TRUE;
1022 }
1023
1024 // --------------------------------------------------------------------
1025
1026 /**
1027 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1028 *
1029 * @access public
1030 * @param string the function name
1031 * @param mixed any parameters needed by the function
Barry Mienydd671972010-10-04 16:33:58 +02001032 * @return mixed
1033 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001034 function call_function($function)
1035 {
1036 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
Barry Mienydd671972010-10-04 16:33:58 +02001037
Derek Allard2067d1a2008-11-13 22:59:24 +00001038 if (FALSE === strpos($driver, $function))
1039 {
1040 $function = $driver.$function;
1041 }
Barry Mienydd671972010-10-04 16:33:58 +02001042
Derek Allard2067d1a2008-11-13 22:59:24 +00001043 if ( ! function_exists($function))
1044 {
1045 if ($this->db_debug)
1046 {
1047 return $this->display_error('db_unsupported_function');
1048 }
1049 return FALSE;
1050 }
1051 else
1052 {
1053 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1054
Repox71b78092011-12-01 09:19:43 +01001055 if (is_null($args))
1056 {
1057 return call_user_func($function);
1058 }
1059 else
1060 {
1061 return call_user_func_array($function, $args);
1062 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001063 }
1064 }
1065
1066 // --------------------------------------------------------------------
1067
1068 /**
1069 * Set Cache Directory Path
1070 *
1071 * @access public
1072 * @param string the path to the cache directory
1073 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001074 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001075 function cache_set_path($path = '')
1076 {
1077 $this->cachedir = $path;
1078 }
1079
1080 // --------------------------------------------------------------------
1081
1082 /**
1083 * Enable Query Caching
1084 *
1085 * @access public
1086 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001087 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001088 function cache_on()
1089 {
1090 $this->cache_on = TRUE;
1091 return TRUE;
1092 }
1093
1094 // --------------------------------------------------------------------
1095
1096 /**
1097 * Disable Query Caching
1098 *
1099 * @access public
1100 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001101 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001102 function cache_off()
1103 {
1104 $this->cache_on = FALSE;
1105 return FALSE;
1106 }
Barry Mienydd671972010-10-04 16:33:58 +02001107
Derek Allard2067d1a2008-11-13 22:59:24 +00001108
1109 // --------------------------------------------------------------------
1110
1111 /**
1112 * Delete the cache files associated with a particular URI
1113 *
1114 * @access public
1115 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001116 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001117 function cache_delete($segment_one = '', $segment_two = '')
1118 {
1119 if ( ! $this->_cache_init())
1120 {
1121 return FALSE;
1122 }
1123 return $this->CACHE->delete($segment_one, $segment_two);
1124 }
1125
1126 // --------------------------------------------------------------------
1127
1128 /**
1129 * Delete All cache files
1130 *
1131 * @access public
1132 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001133 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001134 function cache_delete_all()
1135 {
1136 if ( ! $this->_cache_init())
1137 {
1138 return FALSE;
1139 }
1140
1141 return $this->CACHE->delete_all();
1142 }
1143
1144 // --------------------------------------------------------------------
1145
1146 /**
1147 * Initialize the Cache Class
1148 *
1149 * @access private
1150 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001151 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001152 function _cache_init()
1153 {
1154 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1155 {
1156 return TRUE;
1157 }
Derek Allarde37ab382009-02-03 16:13:57 +00001158
1159 if ( ! class_exists('CI_DB_Cache'))
Derek Allard2067d1a2008-11-13 22:59:24 +00001160 {
Greg Aker3a746652011-04-19 10:59:47 -05001161 if ( ! @include(BASEPATH.'database/DB_cache.php'))
Derek Allarde37ab382009-02-03 16:13:57 +00001162 {
1163 return $this->cache_off();
1164 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001165 }
Derek Allarde37ab382009-02-03 16:13:57 +00001166
Derek Allard2067d1a2008-11-13 22:59:24 +00001167 $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1168 return TRUE;
1169 }
1170
1171 // --------------------------------------------------------------------
1172
1173 /**
1174 * Close DB Connection
1175 *
1176 * @access public
Barry Mienydd671972010-10-04 16:33:58 +02001177 * @return void
1178 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001179 function close()
1180 {
1181 if (is_resource($this->conn_id) OR is_object($this->conn_id))
1182 {
1183 $this->_close($this->conn_id);
1184 }
1185 $this->conn_id = FALSE;
1186 }
Barry Mienydd671972010-10-04 16:33:58 +02001187
Derek Allard2067d1a2008-11-13 22:59:24 +00001188 // --------------------------------------------------------------------
1189
1190 /**
1191 * Display an error message
1192 *
1193 * @access public
1194 * @param string the error message
1195 * @param string any "swap" values
1196 * @param boolean whether to localize the message
Barry Mienydd671972010-10-04 16:33:58 +02001197 * @return string sends the application/error_db.php template
1198 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001199 function display_error($error = '', $swap = '', $native = FALSE)
1200 {
Derek Jonese7792202010-03-02 17:24:46 -06001201 $LANG =& load_class('Lang', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001202 $LANG->load('db');
1203
1204 $heading = $LANG->line('db_error_heading');
1205
1206 if ($native == TRUE)
1207 {
Andrey Andreev85a99cc2011-09-24 17:17:37 +03001208 $message = (array) $error;
Derek Allard2067d1a2008-11-13 22:59:24 +00001209 }
1210 else
1211 {
1212 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
1213 }
Barry Mienydd671972010-10-04 16:33:58 +02001214
Pascal Kriete60f8c392010-08-25 18:03:28 +02001215 // Find the most likely culprit of the error by going through
1216 // the backtrace until the source file is no longer in the
1217 // database folder.
Barry Mienydd671972010-10-04 16:33:58 +02001218
Pascal Kriete60f8c392010-08-25 18:03:28 +02001219 $trace = debug_backtrace();
1220
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001221 foreach ($trace as $call)
Pascal Kriete60f8c392010-08-25 18:03:28 +02001222 {
1223 if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
1224 {
1225 // Found it - use a relative path for safety
1226 $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
1227 $message[] = 'Line Number: '.$call['line'];
Barry Mienydd671972010-10-04 16:33:58 +02001228
Pascal Kriete60f8c392010-08-25 18:03:28 +02001229 break;
1230 }
1231 }
Barry Mienydd671972010-10-04 16:33:58 +02001232
Derek Jonese7792202010-03-02 17:24:46 -06001233 $error =& load_class('Exceptions', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001234 echo $error->show_error($heading, $message, 'error_db');
1235 exit;
1236 }
1237
1238 // --------------------------------------------------------------------
1239
1240 /**
1241 * Protect Identifiers
1242 *
1243 * This function adds backticks if appropriate based on db type
1244 *
1245 * @access private
1246 * @param mixed the item to escape
1247 * @return mixed the item with backticks
1248 */
1249 function protect_identifiers($item, $prefix_single = FALSE)
1250 {
1251 return $this->_protect_identifiers($item, $prefix_single);
1252 }
1253
1254 // --------------------------------------------------------------------
1255
1256 /**
1257 * Protect Identifiers
1258 *
1259 * This function is used extensively by the Active Record class, and by
Barry Mienydd671972010-10-04 16:33:58 +02001260 * a couple functions in this class.
Derek Allard2067d1a2008-11-13 22:59:24 +00001261 * It takes a column or table name (optionally with an alias) and inserts
Derek Jones37f4b9c2011-07-01 17:56:50 -05001262 * the table prefix onto it. Some logic is necessary in order to deal with
1263 * column names that include the path. Consider a query like this:
Derek Allard2067d1a2008-11-13 22:59:24 +00001264 *
1265 * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
1266 *
1267 * Or a query with aliasing:
1268 *
1269 * SELECT m.member_id, m.member_name FROM members AS m
1270 *
1271 * Since the column name can include up to four segments (host, DB, table, column)
1272 * or also have an alias prefix, we need to do a bit of work to figure this out and
1273 * insert the table prefix (if it exists) in the proper position, and escape only
1274 * the correct identifiers.
1275 *
1276 * @access private
1277 * @param string
1278 * @param bool
1279 * @param mixed
1280 * @param bool
1281 * @return string
Barry Mienydd671972010-10-04 16:33:58 +02001282 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001283 function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1284 {
1285 if ( ! is_bool($protect_identifiers))
1286 {
1287 $protect_identifiers = $this->_protect_identifiers;
1288 }
Derek Allarde37ab382009-02-03 16:13:57 +00001289
1290 if (is_array($item))
1291 {
1292 $escaped_array = array();
1293
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001294 foreach ($item as $k => $v)
Derek Allarde37ab382009-02-03 16:13:57 +00001295 {
1296 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v);
1297 }
1298
1299 return $escaped_array;
1300 }
1301
Derek Allard2067d1a2008-11-13 22:59:24 +00001302 // Convert tabs or multiple spaces into single spaces
Derek Jones7b3b96c2009-02-10 21:01:47 +00001303 $item = preg_replace('/[\t ]+/', ' ', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001304
Derek Allard2067d1a2008-11-13 22:59:24 +00001305 // If the item has an alias declaration we remove it and set it aside.
1306 // Basically we remove everything to the right of the first space
1307 $alias = '';
1308 if (strpos($item, ' ') !== FALSE)
Derek Allard911d3e02008-12-15 14:08:35 +00001309 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001310 $alias = strstr($item, " ");
1311 $item = substr($item, 0, - strlen($alias));
1312 }
1313
Derek Allard911d3e02008-12-15 14:08:35 +00001314 // This is basically a bug fix for queries that use MAX, MIN, etc.
Barry Mienydd671972010-10-04 16:33:58 +02001315 // If a parenthesis is found we know that we do not need to
Derek Jones37f4b9c2011-07-01 17:56:50 -05001316 // escape the data or add a prefix. There's probably a more graceful
Derek Allard911d3e02008-12-15 14:08:35 +00001317 // way to deal with this, but I'm not thinking of it -- Rick
1318 if (strpos($item, '(') !== FALSE)
1319 {
1320 return $item.$alias;
1321 }
1322
Derek Allard2067d1a2008-11-13 22:59:24 +00001323 // Break the string apart if it contains periods, then insert the table prefix
1324 // in the correct location, assuming the period doesn't indicate that we're dealing
1325 // with an alias. While we're at it, we will escape the components
1326 if (strpos($item, '.') !== FALSE)
1327 {
1328 $parts = explode('.', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001329
Derek Allard2067d1a2008-11-13 22:59:24 +00001330 // Does the first segment of the exploded item match
Derek Jones37f4b9c2011-07-01 17:56:50 -05001331 // one of the aliases previously identified? If so,
Derek Allard2067d1a2008-11-13 22:59:24 +00001332 // we have nothing more to do other than escape the item
1333 if (in_array($parts[0], $this->ar_aliased_tables))
Derek Allard911d3e02008-12-15 14:08:35 +00001334 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001335 if ($protect_identifiers === TRUE)
1336 {
1337 foreach ($parts as $key => $val)
1338 {
1339 if ( ! in_array($val, $this->_reserved_identifiers))
1340 {
1341 $parts[$key] = $this->_escape_identifiers($val);
1342 }
1343 }
Barry Mienydd671972010-10-04 16:33:58 +02001344
Derek Allard2067d1a2008-11-13 22:59:24 +00001345 $item = implode('.', $parts);
Barry Mienydd671972010-10-04 16:33:58 +02001346 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001347 return $item.$alias;
1348 }
Barry Mienydd671972010-10-04 16:33:58 +02001349
Derek Jones37f4b9c2011-07-01 17:56:50 -05001350 // Is there a table prefix defined in the config file? If not, no need to do anything
Derek Allard2067d1a2008-11-13 22:59:24 +00001351 if ($this->dbprefix != '')
1352 {
1353 // We now add the table prefix based on some logic.
1354 // Do we have 4 segments (hostname.database.table.column)?
1355 // If so, we add the table prefix to the column name in the 3rd segment.
1356 if (isset($parts[3]))
1357 {
1358 $i = 2;
1359 }
1360 // Do we have 3 segments (database.table.column)?
1361 // If so, we add the table prefix to the column name in 2nd position
1362 elseif (isset($parts[2]))
1363 {
1364 $i = 1;
1365 }
1366 // Do we have 2 segments (table.column)?
1367 // If so, we add the table prefix to the column name in 1st segment
1368 else
1369 {
1370 $i = 0;
1371 }
Barry Mienydd671972010-10-04 16:33:58 +02001372
Derek Allard2067d1a2008-11-13 22:59:24 +00001373 // This flag is set when the supplied $item does not contain a field name.
1374 // This can happen when this function is being called from a JOIN.
1375 if ($field_exists == FALSE)
1376 {
1377 $i++;
1378 }
Barry Mienydd671972010-10-04 16:33:58 +02001379
Derek Jones55acc8b2009-07-11 03:38:13 +00001380 // Verify table prefix and replace if necessary
1381 if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0)
1382 {
1383 $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]);
1384 }
Barry Mienydd671972010-10-04 16:33:58 +02001385
Derek Allard2067d1a2008-11-13 22:59:24 +00001386 // We only add the table prefix if it does not already exist
1387 if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix)
1388 {
1389 $parts[$i] = $this->dbprefix.$parts[$i];
1390 }
Barry Mienydd671972010-10-04 16:33:58 +02001391
Derek Allard2067d1a2008-11-13 22:59:24 +00001392 // Put the parts back together
1393 $item = implode('.', $parts);
1394 }
Barry Mienydd671972010-10-04 16:33:58 +02001395
Derek Allard2067d1a2008-11-13 22:59:24 +00001396 if ($protect_identifiers === TRUE)
1397 {
1398 $item = $this->_escape_identifiers($item);
1399 }
Barry Mienydd671972010-10-04 16:33:58 +02001400
Derek Allard2067d1a2008-11-13 22:59:24 +00001401 return $item.$alias;
1402 }
1403
Derek Jones37f4b9c2011-07-01 17:56:50 -05001404 // Is there a table prefix? If not, no need to insert it
Derek Allard2067d1a2008-11-13 22:59:24 +00001405 if ($this->dbprefix != '')
1406 {
Derek Jones55acc8b2009-07-11 03:38:13 +00001407 // Verify table prefix and replace if necessary
1408 if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0)
1409 {
1410 $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item);
1411 }
1412
Derek Allard2067d1a2008-11-13 22:59:24 +00001413 // Do we prefix an item with no segments?
1414 if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix)
1415 {
1416 $item = $this->dbprefix.$item;
Barry Mienydd671972010-10-04 16:33:58 +02001417 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001418 }
1419
1420 if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers))
1421 {
1422 $item = $this->_escape_identifiers($item);
1423 }
Barry Mienydd671972010-10-04 16:33:58 +02001424
Derek Allard2067d1a2008-11-13 22:59:24 +00001425 return $item.$alias;
1426 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001427}
1428
Derek Allard2067d1a2008-11-13 22:59:24 +00001429/* End of file DB_driver.php */
Andrey Andreevdc46d992011-09-24 16:25:23 +03001430/* Location: ./system/database/DB_driver.php */