blob: cc40ba48acf119483bc7deee2bcbff17c01b95be [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
21 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc. (http://ellislab.com/)
22 * @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;
84
85
Barry Mienydd671972010-10-04 16:33:58 +020086
Derek Allard2067d1a2008-11-13 22:59:24 +000087 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -050088 * Constructor. Accepts one parameter containing the database
Derek Allard2067d1a2008-11-13 22:59:24 +000089 * connection settings.
90 *
91 * @param array
Barry Mienydd671972010-10-04 16:33:58 +020092 */
Timothy Warrena2097a02011-10-10 10:10:46 -040093 function __construct($params)
Derek Allard2067d1a2008-11-13 22:59:24 +000094 {
95 if (is_array($params))
96 {
97 foreach ($params as $key => $val)
98 {
99 $this->$key = $val;
100 }
101 }
102
103 log_message('debug', 'Database Driver Class Initialized');
104 }
Barry Mienydd671972010-10-04 16:33:58 +0200105
Derek Allard2067d1a2008-11-13 22:59:24 +0000106 // --------------------------------------------------------------------
107
108 /**
109 * Initialize Database Settings
110 *
111 * @access private Called by the constructor
112 * @param mixed
113 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200114 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000115 function initialize()
116 {
117 // If an existing connection resource is available
118 // there is no need to connect and select the database
119 if (is_resource($this->conn_id) OR is_object($this->conn_id))
120 {
121 return TRUE;
122 }
Barry Mienydd671972010-10-04 16:33:58 +0200123
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 // ----------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200125
Derek Allard2067d1a2008-11-13 22:59:24 +0000126 // Connect to the database and set the connection ID
127 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
128
Derek Jones37f4b9c2011-07-01 17:56:50 -0500129 // No connection resource? Throw an error
Derek Allard2067d1a2008-11-13 22:59:24 +0000130 if ( ! $this->conn_id)
131 {
132 log_message('error', 'Unable to connect to the database');
Barry Mienydd671972010-10-04 16:33:58 +0200133
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 if ($this->db_debug)
135 {
136 $this->display_error('db_unable_to_connect');
137 }
138 return FALSE;
139 }
140
141 // ----------------------------------------------------------------
142
143 // Select the DB... assuming a database name is specified in the config file
144 if ($this->database != '')
145 {
146 if ( ! $this->db_select())
147 {
148 log_message('error', 'Unable to select database: '.$this->database);
Barry Mienydd671972010-10-04 16:33:58 +0200149
Derek Allard2067d1a2008-11-13 22:59:24 +0000150 if ($this->db_debug)
151 {
152 $this->display_error('db_unable_to_select', $this->database);
153 }
Barry Mienydd671972010-10-04 16:33:58 +0200154 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000155 }
156 else
157 {
158 // We've selected the DB. Now we set the character set
159 if ( ! $this->db_set_charset($this->char_set, $this->dbcollat))
160 {
161 return FALSE;
162 }
Barry Mienydd671972010-10-04 16:33:58 +0200163
Derek Allard2067d1a2008-11-13 22:59:24 +0000164 return TRUE;
165 }
166 }
167
168 return TRUE;
169 }
Barry Mienydd671972010-10-04 16:33:58 +0200170
Derek Allard2067d1a2008-11-13 22:59:24 +0000171 // --------------------------------------------------------------------
172
173 /**
174 * Set client character set
175 *
176 * @access public
177 * @param string
178 * @param string
179 * @return resource
180 */
181 function db_set_charset($charset, $collation)
182 {
183 if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat))
184 {
185 log_message('error', 'Unable to set database connection charset: '.$this->char_set);
Barry Mienydd671972010-10-04 16:33:58 +0200186
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 if ($this->db_debug)
188 {
189 $this->display_error('db_unable_to_set_charset', $this->char_set);
190 }
Barry Mienydd671972010-10-04 16:33:58 +0200191
Derek Allard2067d1a2008-11-13 22:59:24 +0000192 return FALSE;
193 }
Barry Mienydd671972010-10-04 16:33:58 +0200194
Derek Allard2067d1a2008-11-13 22:59:24 +0000195 return TRUE;
196 }
Barry Mienydd671972010-10-04 16:33:58 +0200197
Derek Allard2067d1a2008-11-13 22:59:24 +0000198 // --------------------------------------------------------------------
199
200 /**
201 * The name of the platform in use (mysql, mssql, etc...)
202 *
203 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200204 * @return string
205 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000206 function platform()
207 {
208 return $this->dbdriver;
209 }
210
211 // --------------------------------------------------------------------
212
213 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -0500214 * Database Version Number. Returns a string containing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000215 * version of the database being used
216 *
217 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200218 * @return string
219 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000220 function version()
221 {
222 if (FALSE === ($sql = $this->_version()))
223 {
224 if ($this->db_debug)
225 {
226 return $this->display_error('db_unsupported_function');
227 }
228 return FALSE;
229 }
Derek Allard3683f772009-12-16 17:32:33 +0000230
231 // Some DBs have functions that return the version, and don't run special
232 // SQL queries per se. In these instances, just return the result.
Timothy Warren36fb8de2011-08-24 08:29:05 -0400233 $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid', 'pdo');
Derek Allard3683f772009-12-16 17:32:33 +0000234
235 if (in_array($this->dbdriver, $driver_version_exceptions))
Derek Allard2067d1a2008-11-13 22:59:24 +0000236 {
237 return $sql;
238 }
Derek Allard3683f772009-12-16 17:32:33 +0000239 else
240 {
241 $query = $this->query($sql);
242 return $query->row('ver');
243 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000244 }
Barry Mienydd671972010-10-04 16:33:58 +0200245
Derek Allard2067d1a2008-11-13 22:59:24 +0000246 // --------------------------------------------------------------------
247
248 /**
249 * Execute the query
250 *
251 * Accepts an SQL string as input and returns a result object upon
Derek Jones37f4b9c2011-07-01 17:56:50 -0500252 * successful execution of a "read" type query. Returns boolean TRUE
Derek Allard2067d1a2008-11-13 22:59:24 +0000253 * upon successful execution of a "write" type query. Returns boolean
254 * FALSE upon failure, and if the $db_debug variable is set to TRUE
255 * will raise an error.
256 *
257 * @access public
258 * @param string An SQL query string
259 * @param array An array of binding data
Barry Mienydd671972010-10-04 16:33:58 +0200260 * @return mixed
261 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000262 function query($sql, $binds = FALSE, $return_object = TRUE)
263 {
264 if ($sql == '')
265 {
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200266 log_message('error', 'Invalid query: '.$sql);
267
Derek Allard2067d1a2008-11-13 22:59:24 +0000268 if ($this->db_debug)
269 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000270 return $this->display_error('db_invalid_query');
271 }
272 return FALSE;
273 }
274
275 // Verify table prefix and replace if necessary
276 if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
Derek Jonese7792202010-03-02 17:24:46 -0600277 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000278 $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
279 }
Derek Jonese7792202010-03-02 17:24:46 -0600280
Derek Jones37f4b9c2011-07-01 17:56:50 -0500281 // Is query caching enabled? If the query is a "read type"
Derek Allard2067d1a2008-11-13 22:59:24 +0000282 // we will load the caching class and return the previously
283 // cached query if it exists
284 if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
285 {
286 if ($this->_cache_init())
287 {
288 $this->load_rdriver();
289 if (FALSE !== ($cache = $this->CACHE->read($sql)))
290 {
291 return $cache;
292 }
293 }
294 }
Barry Mienydd671972010-10-04 16:33:58 +0200295
Derek Allard2067d1a2008-11-13 22:59:24 +0000296 // Compile binds if needed
297 if ($binds !== FALSE)
298 {
299 $sql = $this->compile_binds($sql, $binds);
300 }
301
Derek Jones37f4b9c2011-07-01 17:56:50 -0500302 // Save the query for debugging
Derek Allard2067d1a2008-11-13 22:59:24 +0000303 if ($this->save_queries == TRUE)
304 {
305 $this->queries[] = $sql;
306 }
Barry Mienydd671972010-10-04 16:33:58 +0200307
Derek Allard2067d1a2008-11-13 22:59:24 +0000308 // Start the Query Timer
309 $time_start = list($sm, $ss) = explode(' ', microtime());
Barry Mienydd671972010-10-04 16:33:58 +0200310
Derek Allard2067d1a2008-11-13 22:59:24 +0000311 // Run the Query
312 if (FALSE === ($this->result_id = $this->simple_query($sql)))
313 {
314 if ($this->save_queries == TRUE)
315 {
316 $this->query_times[] = 0;
317 }
Barry Mienydd671972010-10-04 16:33:58 +0200318
Derek Allard2067d1a2008-11-13 22:59:24 +0000319 // This will trigger a rollback if transactions are being used
320 $this->_trans_status = FALSE;
321
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200322 // Grab the error number and message now, as we might run some
323 // additional queries before displaying the error
324 $error_no = $this->_error_number();
325 $error_msg = $this->_error_message();
326
327 // Log errors
328 log_message('error', 'Query error: '.$error_msg);
329
Derek Allard2067d1a2008-11-13 22:59:24 +0000330 if ($this->db_debug)
331 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000332 // We call this function in order to roll-back queries
Derek Jones37f4b9c2011-07-01 17:56:50 -0500333 // if transactions are enabled. If we don't call this here
Barry Mienydd671972010-10-04 16:33:58 +0200334 // the error message will trigger an exit, causing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000335 // transactions to remain in limbo.
336 $this->trans_complete();
337
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200338 // Display errors
Derek Allard2067d1a2008-11-13 22:59:24 +0000339 return $this->display_error(
340 array(
341 'Error Number: '.$error_no,
342 $error_msg,
343 $sql
344 )
345 );
346 }
Barry Mienydd671972010-10-04 16:33:58 +0200347
Derek Allard2067d1a2008-11-13 22:59:24 +0000348 return FALSE;
349 }
Barry Mienydd671972010-10-04 16:33:58 +0200350
Derek Allard2067d1a2008-11-13 22:59:24 +0000351 // Stop and aggregate the query time results
352 $time_end = list($em, $es) = explode(' ', microtime());
353 $this->benchmark += ($em + $es) - ($sm + $ss);
354
355 if ($this->save_queries == TRUE)
356 {
357 $this->query_times[] = ($em + $es) - ($sm + $ss);
358 }
Barry Mienydd671972010-10-04 16:33:58 +0200359
Derek Allard2067d1a2008-11-13 22:59:24 +0000360 // Increment the query counter
361 $this->query_count++;
Barry Mienydd671972010-10-04 16:33:58 +0200362
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 // Was the query a "write" type?
364 // If so we'll simply return true
365 if ($this->is_write_type($sql) === TRUE)
366 {
367 // If caching is enabled we'll auto-cleanup any
368 // existing files related to this particular URI
369 if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
370 {
371 $this->CACHE->delete();
372 }
Barry Mienydd671972010-10-04 16:33:58 +0200373
Derek Allard2067d1a2008-11-13 22:59:24 +0000374 return TRUE;
375 }
Barry Mienydd671972010-10-04 16:33:58 +0200376
Derek Allard2067d1a2008-11-13 22:59:24 +0000377 // Return TRUE if we don't need to create a result object
378 // Currently only the Oracle driver uses this when stored
379 // procedures are used
380 if ($return_object !== TRUE)
381 {
382 return TRUE;
383 }
Barry Mienydd671972010-10-04 16:33:58 +0200384
385 // Load and instantiate the result driver
386
387 $driver = $this->load_rdriver();
388 $RES = new $driver();
Derek Allard2067d1a2008-11-13 22:59:24 +0000389 $RES->conn_id = $this->conn_id;
390 $RES->result_id = $this->result_id;
391
392 if ($this->dbdriver == 'oci8')
393 {
394 $RES->stmt_id = $this->stmt_id;
395 $RES->curs_id = NULL;
396 $RES->limit_used = $this->limit_used;
397 $this->stmt_id = FALSE;
398 }
Barry Mienydd671972010-10-04 16:33:58 +0200399
Derek Allard2067d1a2008-11-13 22:59:24 +0000400 // oci8 vars must be set before calling this
401 $RES->num_rows = $RES->num_rows();
Barry Mienydd671972010-10-04 16:33:58 +0200402
Derek Jones37f4b9c2011-07-01 17:56:50 -0500403 // Is query caching enabled? If so, we'll serialize the
Derek Allard2067d1a2008-11-13 22:59:24 +0000404 // result object and save it to a cache file.
405 if ($this->cache_on == TRUE AND $this->_cache_init())
406 {
407 // We'll create a new instance of the result object
408 // only without the platform specific driver since
409 // we can't use it with cached data (the query result
410 // resource ID won't be any good once we've cached the
411 // result object, so we'll have to compile the data
412 // and save it)
413 $CR = new CI_DB_result();
Barry Mienydd671972010-10-04 16:33:58 +0200414 $CR->num_rows = $RES->num_rows();
Derek Allard2067d1a2008-11-13 22:59:24 +0000415 $CR->result_object = $RES->result_object();
416 $CR->result_array = $RES->result_array();
Barry Mienydd671972010-10-04 16:33:58 +0200417
Derek Allard2067d1a2008-11-13 22:59:24 +0000418 // Reset these since cached objects can not utilize resource IDs.
419 $CR->conn_id = NULL;
420 $CR->result_id = NULL;
421
422 $this->CACHE->write($sql, $CR);
423 }
Barry Mienydd671972010-10-04 16:33:58 +0200424
Derek Allard2067d1a2008-11-13 22:59:24 +0000425 return $RES;
426 }
427
428 // --------------------------------------------------------------------
429
430 /**
431 * Load the result drivers
432 *
433 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200434 * @return string the name of the result class
435 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000436 function load_rdriver()
437 {
438 $driver = 'CI_DB_'.$this->dbdriver.'_result';
439
440 if ( ! class_exists($driver))
441 {
Greg Aker3a746652011-04-19 10:59:47 -0500442 include_once(BASEPATH.'database/DB_result.php');
443 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
Derek Allard2067d1a2008-11-13 22:59:24 +0000444 }
Barry Mienydd671972010-10-04 16:33:58 +0200445
Derek Allard2067d1a2008-11-13 22:59:24 +0000446 return $driver;
447 }
Barry Mienydd671972010-10-04 16:33:58 +0200448
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 // --------------------------------------------------------------------
450
451 /**
452 * Simple Query
Derek Jones37f4b9c2011-07-01 17:56:50 -0500453 * This is a simplified version of the query() function. Internally
Derek Allard2067d1a2008-11-13 22:59:24 +0000454 * we only use it when running transaction commands since they do
455 * not require all the features of the main query() function.
456 *
457 * @access public
458 * @param string the sql query
Barry Mienydd671972010-10-04 16:33:58 +0200459 * @return mixed
460 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000461 function simple_query($sql)
462 {
463 if ( ! $this->conn_id)
464 {
465 $this->initialize();
466 }
467
468 return $this->_execute($sql);
469 }
Barry Mienydd671972010-10-04 16:33:58 +0200470
Derek Allard2067d1a2008-11-13 22:59:24 +0000471 // --------------------------------------------------------------------
472
473 /**
474 * Disable Transactions
475 * This permits transactions to be disabled at run-time.
476 *
477 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200478 * @return void
479 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000480 function trans_off()
481 {
482 $this->trans_enabled = FALSE;
483 }
484
485 // --------------------------------------------------------------------
486
487 /**
488 * Enable/disable Transaction Strict Mode
489 * When strict mode is enabled, if you are running multiple groups of
490 * transactions, if one group fails all groups will be rolled back.
491 * If strict mode is disabled, each group is treated autonomously, meaning
492 * a failure of one group will not affect any others
493 *
494 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200495 * @return void
496 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000497 function trans_strict($mode = TRUE)
498 {
499 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
500 }
Barry Mienydd671972010-10-04 16:33:58 +0200501
Derek Allard2067d1a2008-11-13 22:59:24 +0000502 // --------------------------------------------------------------------
503
504 /**
505 * Start Transaction
506 *
507 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200508 * @return void
509 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000510 function trans_start($test_mode = FALSE)
Barry Mienydd671972010-10-04 16:33:58 +0200511 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000512 if ( ! $this->trans_enabled)
513 {
514 return FALSE;
515 }
516
517 // When transactions are nested we only begin/commit/rollback the outermost ones
518 if ($this->_trans_depth > 0)
519 {
520 $this->_trans_depth += 1;
521 return;
522 }
Barry Mienydd671972010-10-04 16:33:58 +0200523
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 $this->trans_begin($test_mode);
Jacob Terry07fcedf2011-11-22 11:21:36 -0500525 $this->_trans_depth += 1;
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 }
527
528 // --------------------------------------------------------------------
529
530 /**
531 * Complete Transaction
532 *
533 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200534 * @return bool
535 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 function trans_complete()
537 {
538 if ( ! $this->trans_enabled)
539 {
540 return FALSE;
541 }
Barry Mienydd671972010-10-04 16:33:58 +0200542
Derek Allard2067d1a2008-11-13 22:59:24 +0000543 // When transactions are nested we only begin/commit/rollback the outermost ones
544 if ($this->_trans_depth > 1)
545 {
546 $this->_trans_depth -= 1;
547 return TRUE;
548 }
Jacob Terry07fcedf2011-11-22 11:21:36 -0500549 else
550 {
551 $this->_trans_depth = 0;
552 }
Barry Mienydd671972010-10-04 16:33:58 +0200553
Derek Allard2067d1a2008-11-13 22:59:24 +0000554 // The query() function will set this flag to FALSE in the event that a query failed
555 if ($this->_trans_status === FALSE)
556 {
557 $this->trans_rollback();
Barry Mienydd671972010-10-04 16:33:58 +0200558
Derek Allard2067d1a2008-11-13 22:59:24 +0000559 // If we are NOT running in strict mode, we will reset
560 // the _trans_status flag so that subsequent groups of transactions
561 // will be permitted.
562 if ($this->trans_strict === FALSE)
563 {
564 $this->_trans_status = TRUE;
565 }
566
567 log_message('debug', 'DB Transaction Failure');
568 return FALSE;
569 }
Barry Mienydd671972010-10-04 16:33:58 +0200570
Derek Allard2067d1a2008-11-13 22:59:24 +0000571 $this->trans_commit();
572 return TRUE;
573 }
574
575 // --------------------------------------------------------------------
576
577 /**
578 * Lets you retrieve the transaction flag to determine if it has failed
579 *
580 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200581 * @return bool
582 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000583 function trans_status()
584 {
585 return $this->_trans_status;
586 }
587
588 // --------------------------------------------------------------------
589
590 /**
591 * Compile Bindings
592 *
593 * @access public
594 * @param string the sql statement
595 * @param array an array of bind data
Barry Mienydd671972010-10-04 16:33:58 +0200596 * @return string
597 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000598 function compile_binds($sql, $binds)
599 {
600 if (strpos($sql, $this->bind_marker) === FALSE)
601 {
602 return $sql;
603 }
Barry Mienydd671972010-10-04 16:33:58 +0200604
Derek Allard2067d1a2008-11-13 22:59:24 +0000605 if ( ! is_array($binds))
606 {
607 $binds = array($binds);
608 }
Barry Mienydd671972010-10-04 16:33:58 +0200609
Derek Allard2067d1a2008-11-13 22:59:24 +0000610 // Get the sql segments around the bind markers
611 $segments = explode($this->bind_marker, $sql);
612
613 // The count of bind should be 1 less then the count of segments
614 // If there are more bind arguments trim it down
615 if (count($binds) >= count($segments)) {
616 $binds = array_slice($binds, 0, count($segments)-1);
617 }
618
619 // Construct the binded query
620 $result = $segments[0];
621 $i = 0;
622 foreach ($binds as $bind)
623 {
624 $result .= $this->escape($bind);
625 $result .= $segments[++$i];
626 }
627
628 return $result;
629 }
Barry Mienydd671972010-10-04 16:33:58 +0200630
Derek Allard2067d1a2008-11-13 22:59:24 +0000631 // --------------------------------------------------------------------
632
633 /**
634 * Determines if a query is a "write" type.
635 *
636 * @access public
637 * @param string An SQL query string
Barry Mienydd671972010-10-04 16:33:58 +0200638 * @return boolean
639 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000640 function is_write_type($sql)
641 {
Derek Allarde37ab382009-02-03 16:13:57 +0000642 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 +0000643 {
644 return FALSE;
645 }
646 return TRUE;
647 }
Barry Mienydd671972010-10-04 16:33:58 +0200648
Derek Allard2067d1a2008-11-13 22:59:24 +0000649 // --------------------------------------------------------------------
650
651 /**
652 * Calculate the aggregate query elapsed time
653 *
654 * @access public
655 * @param integer The number of decimal places
Barry Mienydd671972010-10-04 16:33:58 +0200656 * @return integer
657 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000658 function elapsed_time($decimals = 6)
659 {
660 return number_format($this->benchmark, $decimals);
661 }
Barry Mienydd671972010-10-04 16:33:58 +0200662
Derek Allard2067d1a2008-11-13 22:59:24 +0000663 // --------------------------------------------------------------------
664
665 /**
666 * Returns the total number of queries
667 *
668 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200669 * @return integer
670 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000671 function total_queries()
672 {
673 return $this->query_count;
674 }
Barry Mienydd671972010-10-04 16:33:58 +0200675
Derek Allard2067d1a2008-11-13 22:59:24 +0000676 // --------------------------------------------------------------------
677
678 /**
679 * Returns the last query that was executed
680 *
681 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200682 * @return void
683 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000684 function last_query()
685 {
686 return end($this->queries);
687 }
688
689 // --------------------------------------------------------------------
690
691 /**
692 * "Smart" Escape String
693 *
694 * Escapes data based on type
695 * Sets boolean and null types
696 *
697 * @access public
698 * @param string
Barry Mienydd671972010-10-04 16:33:58 +0200699 * @return mixed
700 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000701 function escape($str)
Barry Mienydd671972010-10-04 16:33:58 +0200702 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000703 if (is_string($str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000704 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000705 $str = "'".$this->escape_str($str)."'";
706 }
707 elseif (is_bool($str))
708 {
709 $str = ($str === FALSE) ? 0 : 1;
710 }
711 elseif (is_null($str))
712 {
713 $str = 'NULL';
714 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000715
716 return $str;
717 }
718
719 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600720
Derek Jonese4ed5832009-02-20 21:44:59 +0000721 /**
Derek Jonesbdc7fb92009-02-20 21:55:10 +0000722 * Escape LIKE String
Derek Jonese4ed5832009-02-20 21:44:59 +0000723 *
724 * Calls the individual driver for platform
725 * specific escaping for LIKE conditions
Barry Mienydd671972010-10-04 16:33:58 +0200726 *
Derek Jonese4ed5832009-02-20 21:44:59 +0000727 * @access public
728 * @param string
729 * @return mixed
730 */
Barry Mienydd671972010-10-04 16:33:58 +0200731 function escape_like_str($str)
732 {
733 return $this->escape_str($str, TRUE);
Derek Jonese4ed5832009-02-20 21:44:59 +0000734 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000735
Derek Jonese4ed5832009-02-20 21:44:59 +0000736 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600737
Derek Allard2067d1a2008-11-13 22:59:24 +0000738 /**
739 * Primary
740 *
Derek Jones37f4b9c2011-07-01 17:56:50 -0500741 * Retrieves the primary key. It assumes that the row in the first
Derek Allard2067d1a2008-11-13 22:59:24 +0000742 * position is the primary key
743 *
744 * @access public
745 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200746 * @return string
747 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000748 function primary($table = '')
Barry Mienydd671972010-10-04 16:33:58 +0200749 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000750 $fields = $this->list_fields($table);
Barry Mienydd671972010-10-04 16:33:58 +0200751
Derek Allard2067d1a2008-11-13 22:59:24 +0000752 if ( ! is_array($fields))
753 {
754 return FALSE;
755 }
756
757 return current($fields);
758 }
759
760 // --------------------------------------------------------------------
761
762 /**
763 * Returns an array of table names
764 *
765 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200766 * @return array
767 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000768 function list_tables($constrain_by_prefix = FALSE)
769 {
770 // Is there a cached result?
771 if (isset($this->data_cache['table_names']))
772 {
773 return $this->data_cache['table_names'];
774 }
Barry Mienydd671972010-10-04 16:33:58 +0200775
Derek Allard2067d1a2008-11-13 22:59:24 +0000776 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
777 {
778 if ($this->db_debug)
779 {
780 return $this->display_error('db_unsupported_function');
781 }
782 return FALSE;
783 }
784
785 $retval = array();
786 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200787
Derek Allard2067d1a2008-11-13 22:59:24 +0000788 if ($query->num_rows() > 0)
789 {
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500790 foreach ($query->result_array() as $row)
Derek Allard2067d1a2008-11-13 22:59:24 +0000791 {
792 if (isset($row['TABLE_NAME']))
793 {
794 $retval[] = $row['TABLE_NAME'];
795 }
796 else
797 {
798 $retval[] = array_shift($row);
799 }
800 }
801 }
802
803 $this->data_cache['table_names'] = $retval;
804 return $this->data_cache['table_names'];
805 }
Barry Mienydd671972010-10-04 16:33:58 +0200806
Derek Allard2067d1a2008-11-13 22:59:24 +0000807 // --------------------------------------------------------------------
808
809 /**
810 * Determine if a particular table exists
811 * @access public
812 * @return boolean
813 */
814 function table_exists($table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200815 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000816 return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE;
817 }
Barry Mienydd671972010-10-04 16:33:58 +0200818
Derek Allard2067d1a2008-11-13 22:59:24 +0000819 // --------------------------------------------------------------------
820
821 /**
822 * Fetch MySQL Field Names
823 *
824 * @access public
825 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200826 * @return array
Derek Allard2067d1a2008-11-13 22:59:24 +0000827 */
828 function list_fields($table = '')
829 {
830 // Is there a cached result?
831 if (isset($this->data_cache['field_names'][$table]))
832 {
833 return $this->data_cache['field_names'][$table];
834 }
Barry Mienydd671972010-10-04 16:33:58 +0200835
Derek Allard2067d1a2008-11-13 22:59:24 +0000836 if ($table == '')
837 {
838 if ($this->db_debug)
839 {
840 return $this->display_error('db_field_param_missing');
841 }
842 return FALSE;
843 }
Barry Mienydd671972010-10-04 16:33:58 +0200844
Greg Aker1edde302010-01-26 00:17:01 +0000845 if (FALSE === ($sql = $this->_list_columns($table)))
Derek Allard2067d1a2008-11-13 22:59:24 +0000846 {
847 if ($this->db_debug)
848 {
849 return $this->display_error('db_unsupported_function');
850 }
851 return FALSE;
852 }
Barry Mienydd671972010-10-04 16:33:58 +0200853
Derek Allard2067d1a2008-11-13 22:59:24 +0000854 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200855
Derek Allard2067d1a2008-11-13 22:59:24 +0000856 $retval = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500857 foreach ($query->result_array() as $row)
Derek Allard2067d1a2008-11-13 22:59:24 +0000858 {
859 if (isset($row['COLUMN_NAME']))
860 {
861 $retval[] = $row['COLUMN_NAME'];
862 }
863 else
864 {
865 $retval[] = current($row);
Barry Mienydd671972010-10-04 16:33:58 +0200866 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000867 }
Barry Mienydd671972010-10-04 16:33:58 +0200868
Derek Allard2067d1a2008-11-13 22:59:24 +0000869 $this->data_cache['field_names'][$table] = $retval;
870 return $this->data_cache['field_names'][$table];
871 }
872
873 // --------------------------------------------------------------------
874
875 /**
876 * Determine if a particular field exists
877 * @access public
878 * @param string
879 * @param string
880 * @return boolean
881 */
882 function field_exists($field_name, $table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200883 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000884 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
885 }
Barry Mienydd671972010-10-04 16:33:58 +0200886
Derek Allard2067d1a2008-11-13 22:59:24 +0000887 // --------------------------------------------------------------------
888
889 /**
890 * Returns an object with field data
891 *
892 * @access public
893 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200894 * @return object
895 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000896 function field_data($table = '')
897 {
898 if ($table == '')
899 {
900 if ($this->db_debug)
901 {
902 return $this->display_error('db_field_param_missing');
903 }
904 return FALSE;
905 }
Barry Mienydd671972010-10-04 16:33:58 +0200906
Derek Allard2067d1a2008-11-13 22:59:24 +0000907 $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE)));
908
909 return $query->field_data();
Barry Mienydd671972010-10-04 16:33:58 +0200910 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000911
912 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200913
Derek Allard2067d1a2008-11-13 22:59:24 +0000914 /**
915 * Generate an insert string
916 *
917 * @access public
918 * @param string the table upon which the query will be performed
919 * @param array an associative array data of key/values
Barry Mienydd671972010-10-04 16:33:58 +0200920 * @return string
921 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000922 function insert_string($table, $data)
923 {
924 $fields = array();
925 $values = array();
Barry Mienydd671972010-10-04 16:33:58 +0200926
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500927 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000928 {
929 $fields[] = $this->_escape_identifiers($key);
930 $values[] = $this->escape($val);
931 }
Barry Mienydd671972010-10-04 16:33:58 +0200932
Derek Allard2067d1a2008-11-13 22:59:24 +0000933 return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
Barry Mienydd671972010-10-04 16:33:58 +0200934 }
935
Derek Allard2067d1a2008-11-13 22:59:24 +0000936 // --------------------------------------------------------------------
937
938 /**
939 * Generate an update string
940 *
941 * @access public
942 * @param string the table upon which the query will be performed
943 * @param array an associative array data of key/values
944 * @param mixed the "where" statement
Barry Mienydd671972010-10-04 16:33:58 +0200945 * @return string
946 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000947 function update_string($table, $data, $where)
948 {
949 if ($where == '')
950 {
951 return false;
952 }
Barry Mienydd671972010-10-04 16:33:58 +0200953
Derek Allard2067d1a2008-11-13 22:59:24 +0000954 $fields = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500955 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000956 {
957 $fields[$this->_protect_identifiers($key)] = $this->escape($val);
958 }
959
960 if ( ! is_array($where))
961 {
962 $dest = array($where);
963 }
964 else
965 {
966 $dest = array();
967 foreach ($where as $key => $val)
968 {
969 $prefix = (count($dest) == 0) ? '' : ' AND ';
Andrey Andreevdc46d992011-09-24 16:25:23 +0300970 $key = $this->_protect_identifiers($key);
Barry Mienydd671972010-10-04 16:33:58 +0200971
Derek Allard2067d1a2008-11-13 22:59:24 +0000972 if ($val !== '')
973 {
974 if ( ! $this->_has_operator($key))
975 {
976 $key .= ' =';
977 }
Barry Mienydd671972010-10-04 16:33:58 +0200978
Derek Allard2067d1a2008-11-13 22:59:24 +0000979 $val = ' '.$this->escape($val);
980 }
Barry Mienydd671972010-10-04 16:33:58 +0200981
Derek Allard2067d1a2008-11-13 22:59:24 +0000982 $dest[] = $prefix.$key.$val;
983 }
Barry Mienydd671972010-10-04 16:33:58 +0200984 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000985
986 return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest);
Barry Mienydd671972010-10-04 16:33:58 +0200987 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000988
989 // --------------------------------------------------------------------
990
991 /**
992 * Tests whether the string has an SQL operator
993 *
994 * @access private
995 * @param string
996 * @return bool
997 */
998 function _has_operator($str)
999 {
1000 $str = trim($str);
1001 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
1002 {
1003 return FALSE;
1004 }
1005
1006 return TRUE;
1007 }
1008
1009 // --------------------------------------------------------------------
1010
1011 /**
1012 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1013 *
1014 * @access public
1015 * @param string the function name
1016 * @param mixed any parameters needed by the function
Barry Mienydd671972010-10-04 16:33:58 +02001017 * @return mixed
1018 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001019 function call_function($function)
1020 {
1021 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
Barry Mienydd671972010-10-04 16:33:58 +02001022
Derek Allard2067d1a2008-11-13 22:59:24 +00001023 if (FALSE === strpos($driver, $function))
1024 {
1025 $function = $driver.$function;
1026 }
Barry Mienydd671972010-10-04 16:33:58 +02001027
Derek Allard2067d1a2008-11-13 22:59:24 +00001028 if ( ! function_exists($function))
1029 {
1030 if ($this->db_debug)
1031 {
1032 return $this->display_error('db_unsupported_function');
1033 }
1034 return FALSE;
1035 }
1036 else
1037 {
1038 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1039
1040 return call_user_func_array($function, $args);
1041 }
1042 }
1043
1044 // --------------------------------------------------------------------
1045
1046 /**
1047 * Set Cache Directory Path
1048 *
1049 * @access public
1050 * @param string the path to the cache directory
1051 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001052 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001053 function cache_set_path($path = '')
1054 {
1055 $this->cachedir = $path;
1056 }
1057
1058 // --------------------------------------------------------------------
1059
1060 /**
1061 * Enable Query Caching
1062 *
1063 * @access public
1064 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001065 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001066 function cache_on()
1067 {
1068 $this->cache_on = TRUE;
1069 return TRUE;
1070 }
1071
1072 // --------------------------------------------------------------------
1073
1074 /**
1075 * Disable Query Caching
1076 *
1077 * @access public
1078 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001079 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001080 function cache_off()
1081 {
1082 $this->cache_on = FALSE;
1083 return FALSE;
1084 }
Barry Mienydd671972010-10-04 16:33:58 +02001085
Derek Allard2067d1a2008-11-13 22:59:24 +00001086
1087 // --------------------------------------------------------------------
1088
1089 /**
1090 * Delete the cache files associated with a particular URI
1091 *
1092 * @access public
1093 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001094 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001095 function cache_delete($segment_one = '', $segment_two = '')
1096 {
1097 if ( ! $this->_cache_init())
1098 {
1099 return FALSE;
1100 }
1101 return $this->CACHE->delete($segment_one, $segment_two);
1102 }
1103
1104 // --------------------------------------------------------------------
1105
1106 /**
1107 * Delete All cache files
1108 *
1109 * @access public
1110 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001111 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001112 function cache_delete_all()
1113 {
1114 if ( ! $this->_cache_init())
1115 {
1116 return FALSE;
1117 }
1118
1119 return $this->CACHE->delete_all();
1120 }
1121
1122 // --------------------------------------------------------------------
1123
1124 /**
1125 * Initialize the Cache Class
1126 *
1127 * @access private
1128 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001129 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001130 function _cache_init()
1131 {
1132 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1133 {
1134 return TRUE;
1135 }
Derek Allarde37ab382009-02-03 16:13:57 +00001136
1137 if ( ! class_exists('CI_DB_Cache'))
Derek Allard2067d1a2008-11-13 22:59:24 +00001138 {
Greg Aker3a746652011-04-19 10:59:47 -05001139 if ( ! @include(BASEPATH.'database/DB_cache.php'))
Derek Allarde37ab382009-02-03 16:13:57 +00001140 {
1141 return $this->cache_off();
1142 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001143 }
Derek Allarde37ab382009-02-03 16:13:57 +00001144
Derek Allard2067d1a2008-11-13 22:59:24 +00001145 $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1146 return TRUE;
1147 }
1148
1149 // --------------------------------------------------------------------
1150
1151 /**
1152 * Close DB Connection
1153 *
1154 * @access public
Barry Mienydd671972010-10-04 16:33:58 +02001155 * @return void
1156 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001157 function close()
1158 {
1159 if (is_resource($this->conn_id) OR is_object($this->conn_id))
1160 {
1161 $this->_close($this->conn_id);
1162 }
1163 $this->conn_id = FALSE;
1164 }
Barry Mienydd671972010-10-04 16:33:58 +02001165
Derek Allard2067d1a2008-11-13 22:59:24 +00001166 // --------------------------------------------------------------------
1167
1168 /**
1169 * Display an error message
1170 *
1171 * @access public
1172 * @param string the error message
1173 * @param string any "swap" values
1174 * @param boolean whether to localize the message
Barry Mienydd671972010-10-04 16:33:58 +02001175 * @return string sends the application/error_db.php template
1176 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001177 function display_error($error = '', $swap = '', $native = FALSE)
1178 {
Derek Jonese7792202010-03-02 17:24:46 -06001179 $LANG =& load_class('Lang', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001180 $LANG->load('db');
1181
1182 $heading = $LANG->line('db_error_heading');
1183
1184 if ($native == TRUE)
1185 {
Andrey Andreev85a99cc2011-09-24 17:17:37 +03001186 $message = (array) $error;
Derek Allard2067d1a2008-11-13 22:59:24 +00001187 }
1188 else
1189 {
1190 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
1191 }
Barry Mienydd671972010-10-04 16:33:58 +02001192
Pascal Kriete60f8c392010-08-25 18:03:28 +02001193 // Find the most likely culprit of the error by going through
1194 // the backtrace until the source file is no longer in the
1195 // database folder.
Barry Mienydd671972010-10-04 16:33:58 +02001196
Pascal Kriete60f8c392010-08-25 18:03:28 +02001197 $trace = debug_backtrace();
1198
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001199 foreach ($trace as $call)
Pascal Kriete60f8c392010-08-25 18:03:28 +02001200 {
1201 if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
1202 {
1203 // Found it - use a relative path for safety
1204 $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
1205 $message[] = 'Line Number: '.$call['line'];
Barry Mienydd671972010-10-04 16:33:58 +02001206
Pascal Kriete60f8c392010-08-25 18:03:28 +02001207 break;
1208 }
1209 }
Barry Mienydd671972010-10-04 16:33:58 +02001210
Derek Jonese7792202010-03-02 17:24:46 -06001211 $error =& load_class('Exceptions', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001212 echo $error->show_error($heading, $message, 'error_db');
1213 exit;
1214 }
1215
1216 // --------------------------------------------------------------------
1217
1218 /**
1219 * Protect Identifiers
1220 *
1221 * This function adds backticks if appropriate based on db type
1222 *
1223 * @access private
1224 * @param mixed the item to escape
1225 * @return mixed the item with backticks
1226 */
1227 function protect_identifiers($item, $prefix_single = FALSE)
1228 {
1229 return $this->_protect_identifiers($item, $prefix_single);
1230 }
1231
1232 // --------------------------------------------------------------------
1233
1234 /**
1235 * Protect Identifiers
1236 *
1237 * This function is used extensively by the Active Record class, and by
Barry Mienydd671972010-10-04 16:33:58 +02001238 * a couple functions in this class.
Derek Allard2067d1a2008-11-13 22:59:24 +00001239 * It takes a column or table name (optionally with an alias) and inserts
Derek Jones37f4b9c2011-07-01 17:56:50 -05001240 * the table prefix onto it. Some logic is necessary in order to deal with
1241 * column names that include the path. Consider a query like this:
Derek Allard2067d1a2008-11-13 22:59:24 +00001242 *
1243 * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
1244 *
1245 * Or a query with aliasing:
1246 *
1247 * SELECT m.member_id, m.member_name FROM members AS m
1248 *
1249 * Since the column name can include up to four segments (host, DB, table, column)
1250 * or also have an alias prefix, we need to do a bit of work to figure this out and
1251 * insert the table prefix (if it exists) in the proper position, and escape only
1252 * the correct identifiers.
1253 *
1254 * @access private
1255 * @param string
1256 * @param bool
1257 * @param mixed
1258 * @param bool
1259 * @return string
Barry Mienydd671972010-10-04 16:33:58 +02001260 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001261 function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1262 {
1263 if ( ! is_bool($protect_identifiers))
1264 {
1265 $protect_identifiers = $this->_protect_identifiers;
1266 }
Derek Allarde37ab382009-02-03 16:13:57 +00001267
1268 if (is_array($item))
1269 {
1270 $escaped_array = array();
1271
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001272 foreach ($item as $k => $v)
Derek Allarde37ab382009-02-03 16:13:57 +00001273 {
1274 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v);
1275 }
1276
1277 return $escaped_array;
1278 }
1279
Derek Allard2067d1a2008-11-13 22:59:24 +00001280 // Convert tabs or multiple spaces into single spaces
Derek Jones7b3b96c2009-02-10 21:01:47 +00001281 $item = preg_replace('/[\t ]+/', ' ', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001282
Derek Allard2067d1a2008-11-13 22:59:24 +00001283 // If the item has an alias declaration we remove it and set it aside.
1284 // Basically we remove everything to the right of the first space
1285 $alias = '';
1286 if (strpos($item, ' ') !== FALSE)
Derek Allard911d3e02008-12-15 14:08:35 +00001287 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001288 $alias = strstr($item, " ");
1289 $item = substr($item, 0, - strlen($alias));
1290 }
1291
Derek Allard911d3e02008-12-15 14:08:35 +00001292 // This is basically a bug fix for queries that use MAX, MIN, etc.
Barry Mienydd671972010-10-04 16:33:58 +02001293 // If a parenthesis is found we know that we do not need to
Derek Jones37f4b9c2011-07-01 17:56:50 -05001294 // escape the data or add a prefix. There's probably a more graceful
Derek Allard911d3e02008-12-15 14:08:35 +00001295 // way to deal with this, but I'm not thinking of it -- Rick
1296 if (strpos($item, '(') !== FALSE)
1297 {
1298 return $item.$alias;
1299 }
1300
Derek Allard2067d1a2008-11-13 22:59:24 +00001301 // Break the string apart if it contains periods, then insert the table prefix
1302 // in the correct location, assuming the period doesn't indicate that we're dealing
1303 // with an alias. While we're at it, we will escape the components
1304 if (strpos($item, '.') !== FALSE)
1305 {
1306 $parts = explode('.', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001307
Derek Allard2067d1a2008-11-13 22:59:24 +00001308 // Does the first segment of the exploded item match
Derek Jones37f4b9c2011-07-01 17:56:50 -05001309 // one of the aliases previously identified? If so,
Derek Allard2067d1a2008-11-13 22:59:24 +00001310 // we have nothing more to do other than escape the item
1311 if (in_array($parts[0], $this->ar_aliased_tables))
Derek Allard911d3e02008-12-15 14:08:35 +00001312 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001313 if ($protect_identifiers === TRUE)
1314 {
1315 foreach ($parts as $key => $val)
1316 {
1317 if ( ! in_array($val, $this->_reserved_identifiers))
1318 {
1319 $parts[$key] = $this->_escape_identifiers($val);
1320 }
1321 }
Barry Mienydd671972010-10-04 16:33:58 +02001322
Derek Allard2067d1a2008-11-13 22:59:24 +00001323 $item = implode('.', $parts);
Barry Mienydd671972010-10-04 16:33:58 +02001324 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001325 return $item.$alias;
1326 }
Barry Mienydd671972010-10-04 16:33:58 +02001327
Derek Jones37f4b9c2011-07-01 17:56:50 -05001328 // Is there a table prefix defined in the config file? If not, no need to do anything
Derek Allard2067d1a2008-11-13 22:59:24 +00001329 if ($this->dbprefix != '')
1330 {
1331 // We now add the table prefix based on some logic.
1332 // Do we have 4 segments (hostname.database.table.column)?
1333 // If so, we add the table prefix to the column name in the 3rd segment.
1334 if (isset($parts[3]))
1335 {
1336 $i = 2;
1337 }
1338 // Do we have 3 segments (database.table.column)?
1339 // If so, we add the table prefix to the column name in 2nd position
1340 elseif (isset($parts[2]))
1341 {
1342 $i = 1;
1343 }
1344 // Do we have 2 segments (table.column)?
1345 // If so, we add the table prefix to the column name in 1st segment
1346 else
1347 {
1348 $i = 0;
1349 }
Barry Mienydd671972010-10-04 16:33:58 +02001350
Derek Allard2067d1a2008-11-13 22:59:24 +00001351 // This flag is set when the supplied $item does not contain a field name.
1352 // This can happen when this function is being called from a JOIN.
1353 if ($field_exists == FALSE)
1354 {
1355 $i++;
1356 }
Barry Mienydd671972010-10-04 16:33:58 +02001357
Derek Jones55acc8b2009-07-11 03:38:13 +00001358 // Verify table prefix and replace if necessary
1359 if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0)
1360 {
1361 $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]);
1362 }
Barry Mienydd671972010-10-04 16:33:58 +02001363
Derek Allard2067d1a2008-11-13 22:59:24 +00001364 // We only add the table prefix if it does not already exist
1365 if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix)
1366 {
1367 $parts[$i] = $this->dbprefix.$parts[$i];
1368 }
Barry Mienydd671972010-10-04 16:33:58 +02001369
Derek Allard2067d1a2008-11-13 22:59:24 +00001370 // Put the parts back together
1371 $item = implode('.', $parts);
1372 }
Barry Mienydd671972010-10-04 16:33:58 +02001373
Derek Allard2067d1a2008-11-13 22:59:24 +00001374 if ($protect_identifiers === TRUE)
1375 {
1376 $item = $this->_escape_identifiers($item);
1377 }
Barry Mienydd671972010-10-04 16:33:58 +02001378
Derek Allard2067d1a2008-11-13 22:59:24 +00001379 return $item.$alias;
1380 }
1381
Derek Jones37f4b9c2011-07-01 17:56:50 -05001382 // Is there a table prefix? If not, no need to insert it
Derek Allard2067d1a2008-11-13 22:59:24 +00001383 if ($this->dbprefix != '')
1384 {
Derek Jones55acc8b2009-07-11 03:38:13 +00001385 // Verify table prefix and replace if necessary
1386 if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0)
1387 {
1388 $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item);
1389 }
1390
Derek Allard2067d1a2008-11-13 22:59:24 +00001391 // Do we prefix an item with no segments?
1392 if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix)
1393 {
1394 $item = $this->dbprefix.$item;
Barry Mienydd671972010-10-04 16:33:58 +02001395 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001396 }
1397
1398 if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers))
1399 {
1400 $item = $this->_escape_identifiers($item);
1401 }
Barry Mienydd671972010-10-04 16:33:58 +02001402
Derek Allard2067d1a2008-11-13 22:59:24 +00001403 return $item.$alias;
1404 }
1405
1406
1407}
1408
1409
1410/* End of file DB_driver.php */
Andrey Andreevdc46d992011-09-24 16:25:23 +03001411/* Location: ./system/database/DB_driver.php */