blob: 300ca29770c51dc334a1d36cd2220e246aa04ee3 [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 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Greg Aker0711dc82011-01-05 10:49:40 -06009 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Database Driver Class
20 *
21 * This is the platform-independent base DB implementation class.
22 * This class will not be called directly. Rather, the adapter
23 * class for the specific database will extend and instantiate it.
24 *
25 * @package CodeIgniter
26 * @subpackage Drivers
27 * @category Database
28 * @author ExpressionEngine Dev Team
29 * @link http://codeigniter.com/user_guide/database/
30 */
31class CI_DB_driver {
32
33 var $username;
34 var $password;
35 var $hostname;
36 var $database;
37 var $dbdriver = 'mysql';
38 var $dbprefix = '';
39 var $char_set = 'utf8';
40 var $dbcollat = 'utf8_general_ci';
41 var $autoinit = TRUE; // Whether to automatically initialize the DB
42 var $swap_pre = '';
43 var $port = '';
44 var $pconnect = FALSE;
45 var $conn_id = FALSE;
46 var $result_id = FALSE;
47 var $db_debug = FALSE;
48 var $benchmark = 0;
49 var $query_count = 0;
50 var $bind_marker = '?';
51 var $save_queries = TRUE;
52 var $queries = array();
53 var $query_times = array();
54 var $data_cache = array();
55 var $trans_enabled = TRUE;
56 var $trans_strict = TRUE;
57 var $_trans_depth = 0;
58 var $_trans_status = TRUE; // Used with transactions to determine if a rollback should occur
59 var $cache_on = FALSE;
60 var $cachedir = '';
61 var $cache_autodel = FALSE;
62 var $CACHE; // The cache class object
63
64 // Private variables
65 var $_protect_identifiers = TRUE;
66 var $_reserved_identifiers = array('*'); // Identifiers that should NOT be escaped
67
68 // These are use with Oracle
69 var $stmt_id;
70 var $curs_id;
71 var $limit_used;
72
73
Barry Mienydd671972010-10-04 16:33:58 +020074
Derek Allard2067d1a2008-11-13 22:59:24 +000075 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -050076 * Constructor. Accepts one parameter containing the database
Derek Allard2067d1a2008-11-13 22:59:24 +000077 * connection settings.
78 *
79 * @param array
Barry Mienydd671972010-10-04 16:33:58 +020080 */
Derek Allard2067d1a2008-11-13 22:59:24 +000081 function CI_DB_driver($params)
82 {
83 if (is_array($params))
84 {
85 foreach ($params as $key => $val)
86 {
87 $this->$key = $val;
88 }
89 }
90
91 log_message('debug', 'Database Driver Class Initialized');
92 }
Barry Mienydd671972010-10-04 16:33:58 +020093
Derek Allard2067d1a2008-11-13 22:59:24 +000094 // --------------------------------------------------------------------
95
96 /**
97 * Initialize Database Settings
98 *
99 * @access private Called by the constructor
100 * @param mixed
101 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200102 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000103 function initialize()
104 {
105 // If an existing connection resource is available
106 // there is no need to connect and select the database
107 if (is_resource($this->conn_id) OR is_object($this->conn_id))
108 {
109 return TRUE;
110 }
Barry Mienydd671972010-10-04 16:33:58 +0200111
Derek Allard2067d1a2008-11-13 22:59:24 +0000112 // ----------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200113
Derek Allard2067d1a2008-11-13 22:59:24 +0000114 // Connect to the database and set the connection ID
115 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
116
Derek Jones37f4b9c2011-07-01 17:56:50 -0500117 // No connection resource? Throw an error
Derek Allard2067d1a2008-11-13 22:59:24 +0000118 if ( ! $this->conn_id)
119 {
120 log_message('error', 'Unable to connect to the database');
Barry Mienydd671972010-10-04 16:33:58 +0200121
Derek Allard2067d1a2008-11-13 22:59:24 +0000122 if ($this->db_debug)
123 {
124 $this->display_error('db_unable_to_connect');
125 }
126 return FALSE;
127 }
128
129 // ----------------------------------------------------------------
130
131 // Select the DB... assuming a database name is specified in the config file
132 if ($this->database != '')
133 {
134 if ( ! $this->db_select())
135 {
136 log_message('error', 'Unable to select database: '.$this->database);
Barry Mienydd671972010-10-04 16:33:58 +0200137
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 if ($this->db_debug)
139 {
140 $this->display_error('db_unable_to_select', $this->database);
141 }
Barry Mienydd671972010-10-04 16:33:58 +0200142 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000143 }
144 else
145 {
146 // We've selected the DB. Now we set the character set
147 if ( ! $this->db_set_charset($this->char_set, $this->dbcollat))
148 {
149 return FALSE;
150 }
Barry Mienydd671972010-10-04 16:33:58 +0200151
Derek Allard2067d1a2008-11-13 22:59:24 +0000152 return TRUE;
153 }
154 }
155
156 return TRUE;
157 }
Barry Mienydd671972010-10-04 16:33:58 +0200158
Derek Allard2067d1a2008-11-13 22:59:24 +0000159 // --------------------------------------------------------------------
160
161 /**
162 * Set client character set
163 *
164 * @access public
165 * @param string
166 * @param string
167 * @return resource
168 */
169 function db_set_charset($charset, $collation)
170 {
171 if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat))
172 {
173 log_message('error', 'Unable to set database connection charset: '.$this->char_set);
Barry Mienydd671972010-10-04 16:33:58 +0200174
Derek Allard2067d1a2008-11-13 22:59:24 +0000175 if ($this->db_debug)
176 {
177 $this->display_error('db_unable_to_set_charset', $this->char_set);
178 }
Barry Mienydd671972010-10-04 16:33:58 +0200179
Derek Allard2067d1a2008-11-13 22:59:24 +0000180 return FALSE;
181 }
Barry Mienydd671972010-10-04 16:33:58 +0200182
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 return TRUE;
184 }
Barry Mienydd671972010-10-04 16:33:58 +0200185
Derek Allard2067d1a2008-11-13 22:59:24 +0000186 // --------------------------------------------------------------------
187
188 /**
189 * The name of the platform in use (mysql, mssql, etc...)
190 *
191 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200192 * @return string
193 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000194 function platform()
195 {
196 return $this->dbdriver;
197 }
198
199 // --------------------------------------------------------------------
200
201 /**
Derek Jones37f4b9c2011-07-01 17:56:50 -0500202 * Database Version Number. Returns a string containing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000203 * version of the database being used
204 *
205 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200206 * @return string
207 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000208 function version()
209 {
210 if (FALSE === ($sql = $this->_version()))
211 {
212 if ($this->db_debug)
213 {
214 return $this->display_error('db_unsupported_function');
215 }
216 return FALSE;
217 }
Derek Allard3683f772009-12-16 17:32:33 +0000218
219 // Some DBs have functions that return the version, and don't run special
220 // SQL queries per se. In these instances, just return the result.
Esen Sagynov2e087942011-08-09 23:35:01 -0700221 $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid');
Derek Allard3683f772009-12-16 17:32:33 +0000222
223 if (in_array($this->dbdriver, $driver_version_exceptions))
Derek Allard2067d1a2008-11-13 22:59:24 +0000224 {
225 return $sql;
226 }
Derek Allard3683f772009-12-16 17:32:33 +0000227 else
228 {
229 $query = $this->query($sql);
230 return $query->row('ver');
231 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 }
Barry Mienydd671972010-10-04 16:33:58 +0200233
Derek Allard2067d1a2008-11-13 22:59:24 +0000234 // --------------------------------------------------------------------
235
236 /**
237 * Execute the query
238 *
239 * Accepts an SQL string as input and returns a result object upon
Derek Jones37f4b9c2011-07-01 17:56:50 -0500240 * successful execution of a "read" type query. Returns boolean TRUE
Derek Allard2067d1a2008-11-13 22:59:24 +0000241 * upon successful execution of a "write" type query. Returns boolean
242 * FALSE upon failure, and if the $db_debug variable is set to TRUE
243 * will raise an error.
244 *
245 * @access public
246 * @param string An SQL query string
247 * @param array An array of binding data
Barry Mienydd671972010-10-04 16:33:58 +0200248 * @return mixed
249 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000250 function query($sql, $binds = FALSE, $return_object = TRUE)
251 {
252 if ($sql == '')
253 {
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200254 log_message('error', 'Invalid query: '.$sql);
255
Derek Allard2067d1a2008-11-13 22:59:24 +0000256 if ($this->db_debug)
257 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000258 return $this->display_error('db_invalid_query');
259 }
260 return FALSE;
261 }
262
263 // Verify table prefix and replace if necessary
264 if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
Derek Jonese7792202010-03-02 17:24:46 -0600265 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000266 $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
267 }
Derek Jonese7792202010-03-02 17:24:46 -0600268
Derek Jones37f4b9c2011-07-01 17:56:50 -0500269 // Is query caching enabled? If the query is a "read type"
Derek Allard2067d1a2008-11-13 22:59:24 +0000270 // we will load the caching class and return the previously
271 // cached query if it exists
272 if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
273 {
274 if ($this->_cache_init())
275 {
276 $this->load_rdriver();
277 if (FALSE !== ($cache = $this->CACHE->read($sql)))
278 {
279 return $cache;
280 }
281 }
282 }
Barry Mienydd671972010-10-04 16:33:58 +0200283
Derek Allard2067d1a2008-11-13 22:59:24 +0000284 // Compile binds if needed
285 if ($binds !== FALSE)
286 {
287 $sql = $this->compile_binds($sql, $binds);
288 }
289
Derek Jones37f4b9c2011-07-01 17:56:50 -0500290 // Save the query for debugging
Derek Allard2067d1a2008-11-13 22:59:24 +0000291 if ($this->save_queries == TRUE)
292 {
293 $this->queries[] = $sql;
294 }
Barry Mienydd671972010-10-04 16:33:58 +0200295
Derek Allard2067d1a2008-11-13 22:59:24 +0000296 // Start the Query Timer
297 $time_start = list($sm, $ss) = explode(' ', microtime());
Barry Mienydd671972010-10-04 16:33:58 +0200298
Derek Allard2067d1a2008-11-13 22:59:24 +0000299 // Run the Query
300 if (FALSE === ($this->result_id = $this->simple_query($sql)))
301 {
302 if ($this->save_queries == TRUE)
303 {
304 $this->query_times[] = 0;
305 }
Barry Mienydd671972010-10-04 16:33:58 +0200306
Derek Allard2067d1a2008-11-13 22:59:24 +0000307 // This will trigger a rollback if transactions are being used
308 $this->_trans_status = FALSE;
309
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200310 // Grab the error number and message now, as we might run some
311 // additional queries before displaying the error
312 $error_no = $this->_error_number();
313 $error_msg = $this->_error_message();
314
315 // Log errors
316 log_message('error', 'Query error: '.$error_msg);
317
Derek Allard2067d1a2008-11-13 22:59:24 +0000318 if ($this->db_debug)
319 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 // We call this function in order to roll-back queries
Derek Jones37f4b9c2011-07-01 17:56:50 -0500321 // if transactions are enabled. If we don't call this here
Barry Mienydd671972010-10-04 16:33:58 +0200322 // the error message will trigger an exit, causing the
Derek Allard2067d1a2008-11-13 22:59:24 +0000323 // transactions to remain in limbo.
324 $this->trans_complete();
325
Niklas Nilssond2018ee2011-08-30 13:39:42 +0200326 // Display errors
Derek Allard2067d1a2008-11-13 22:59:24 +0000327 return $this->display_error(
328 array(
329 'Error Number: '.$error_no,
330 $error_msg,
331 $sql
332 )
333 );
334 }
Barry Mienydd671972010-10-04 16:33:58 +0200335
Derek Allard2067d1a2008-11-13 22:59:24 +0000336 return FALSE;
337 }
Barry Mienydd671972010-10-04 16:33:58 +0200338
Derek Allard2067d1a2008-11-13 22:59:24 +0000339 // Stop and aggregate the query time results
340 $time_end = list($em, $es) = explode(' ', microtime());
341 $this->benchmark += ($em + $es) - ($sm + $ss);
342
343 if ($this->save_queries == TRUE)
344 {
345 $this->query_times[] = ($em + $es) - ($sm + $ss);
346 }
Barry Mienydd671972010-10-04 16:33:58 +0200347
Derek Allard2067d1a2008-11-13 22:59:24 +0000348 // Increment the query counter
349 $this->query_count++;
Barry Mienydd671972010-10-04 16:33:58 +0200350
Derek Allard2067d1a2008-11-13 22:59:24 +0000351 // Was the query a "write" type?
352 // If so we'll simply return true
353 if ($this->is_write_type($sql) === TRUE)
354 {
355 // If caching is enabled we'll auto-cleanup any
356 // existing files related to this particular URI
357 if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
358 {
359 $this->CACHE->delete();
360 }
Barry Mienydd671972010-10-04 16:33:58 +0200361
Derek Allard2067d1a2008-11-13 22:59:24 +0000362 return TRUE;
363 }
Barry Mienydd671972010-10-04 16:33:58 +0200364
Derek Allard2067d1a2008-11-13 22:59:24 +0000365 // Return TRUE if we don't need to create a result object
366 // Currently only the Oracle driver uses this when stored
367 // procedures are used
368 if ($return_object !== TRUE)
369 {
370 return TRUE;
371 }
Barry Mienydd671972010-10-04 16:33:58 +0200372
373 // Load and instantiate the result driver
374
375 $driver = $this->load_rdriver();
376 $RES = new $driver();
Derek Allard2067d1a2008-11-13 22:59:24 +0000377 $RES->conn_id = $this->conn_id;
378 $RES->result_id = $this->result_id;
379
380 if ($this->dbdriver == 'oci8')
381 {
382 $RES->stmt_id = $this->stmt_id;
383 $RES->curs_id = NULL;
384 $RES->limit_used = $this->limit_used;
385 $this->stmt_id = FALSE;
386 }
Barry Mienydd671972010-10-04 16:33:58 +0200387
Derek Allard2067d1a2008-11-13 22:59:24 +0000388 // oci8 vars must be set before calling this
389 $RES->num_rows = $RES->num_rows();
Barry Mienydd671972010-10-04 16:33:58 +0200390
Derek Jones37f4b9c2011-07-01 17:56:50 -0500391 // Is query caching enabled? If so, we'll serialize the
Derek Allard2067d1a2008-11-13 22:59:24 +0000392 // result object and save it to a cache file.
393 if ($this->cache_on == TRUE AND $this->_cache_init())
394 {
395 // We'll create a new instance of the result object
396 // only without the platform specific driver since
397 // we can't use it with cached data (the query result
398 // resource ID won't be any good once we've cached the
399 // result object, so we'll have to compile the data
400 // and save it)
401 $CR = new CI_DB_result();
Barry Mienydd671972010-10-04 16:33:58 +0200402 $CR->num_rows = $RES->num_rows();
Derek Allard2067d1a2008-11-13 22:59:24 +0000403 $CR->result_object = $RES->result_object();
404 $CR->result_array = $RES->result_array();
Barry Mienydd671972010-10-04 16:33:58 +0200405
Derek Allard2067d1a2008-11-13 22:59:24 +0000406 // Reset these since cached objects can not utilize resource IDs.
407 $CR->conn_id = NULL;
408 $CR->result_id = NULL;
409
410 $this->CACHE->write($sql, $CR);
411 }
Barry Mienydd671972010-10-04 16:33:58 +0200412
Derek Allard2067d1a2008-11-13 22:59:24 +0000413 return $RES;
414 }
415
416 // --------------------------------------------------------------------
417
418 /**
419 * Load the result drivers
420 *
421 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200422 * @return string the name of the result class
423 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000424 function load_rdriver()
425 {
426 $driver = 'CI_DB_'.$this->dbdriver.'_result';
427
428 if ( ! class_exists($driver))
429 {
Greg Aker3a746652011-04-19 10:59:47 -0500430 include_once(BASEPATH.'database/DB_result.php');
431 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
Derek Allard2067d1a2008-11-13 22:59:24 +0000432 }
Barry Mienydd671972010-10-04 16:33:58 +0200433
Derek Allard2067d1a2008-11-13 22:59:24 +0000434 return $driver;
435 }
Barry Mienydd671972010-10-04 16:33:58 +0200436
Derek Allard2067d1a2008-11-13 22:59:24 +0000437 // --------------------------------------------------------------------
438
439 /**
440 * Simple Query
Derek Jones37f4b9c2011-07-01 17:56:50 -0500441 * This is a simplified version of the query() function. Internally
Derek Allard2067d1a2008-11-13 22:59:24 +0000442 * we only use it when running transaction commands since they do
443 * not require all the features of the main query() function.
444 *
445 * @access public
446 * @param string the sql query
Barry Mienydd671972010-10-04 16:33:58 +0200447 * @return mixed
448 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 function simple_query($sql)
450 {
451 if ( ! $this->conn_id)
452 {
453 $this->initialize();
454 }
455
456 return $this->_execute($sql);
457 }
Barry Mienydd671972010-10-04 16:33:58 +0200458
Derek Allard2067d1a2008-11-13 22:59:24 +0000459 // --------------------------------------------------------------------
460
461 /**
462 * Disable Transactions
463 * This permits transactions to be disabled at run-time.
464 *
465 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200466 * @return void
467 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000468 function trans_off()
469 {
470 $this->trans_enabled = FALSE;
471 }
472
473 // --------------------------------------------------------------------
474
475 /**
476 * Enable/disable Transaction Strict Mode
477 * When strict mode is enabled, if you are running multiple groups of
478 * transactions, if one group fails all groups will be rolled back.
479 * If strict mode is disabled, each group is treated autonomously, meaning
480 * a failure of one group will not affect any others
481 *
482 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200483 * @return void
484 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000485 function trans_strict($mode = TRUE)
486 {
487 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
488 }
Barry Mienydd671972010-10-04 16:33:58 +0200489
Derek Allard2067d1a2008-11-13 22:59:24 +0000490 // --------------------------------------------------------------------
491
492 /**
493 * Start Transaction
494 *
495 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200496 * @return void
497 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000498 function trans_start($test_mode = FALSE)
Barry Mienydd671972010-10-04 16:33:58 +0200499 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000500 if ( ! $this->trans_enabled)
501 {
502 return FALSE;
503 }
504
505 // When transactions are nested we only begin/commit/rollback the outermost ones
506 if ($this->_trans_depth > 0)
507 {
508 $this->_trans_depth += 1;
509 return;
510 }
Barry Mienydd671972010-10-04 16:33:58 +0200511
Derek Allard2067d1a2008-11-13 22:59:24 +0000512 $this->trans_begin($test_mode);
513 }
514
515 // --------------------------------------------------------------------
516
517 /**
518 * Complete Transaction
519 *
520 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200521 * @return bool
522 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 function trans_complete()
524 {
525 if ( ! $this->trans_enabled)
526 {
527 return FALSE;
528 }
Barry Mienydd671972010-10-04 16:33:58 +0200529
Derek Allard2067d1a2008-11-13 22:59:24 +0000530 // When transactions are nested we only begin/commit/rollback the outermost ones
531 if ($this->_trans_depth > 1)
532 {
533 $this->_trans_depth -= 1;
534 return TRUE;
535 }
Barry Mienydd671972010-10-04 16:33:58 +0200536
Derek Allard2067d1a2008-11-13 22:59:24 +0000537 // The query() function will set this flag to FALSE in the event that a query failed
538 if ($this->_trans_status === FALSE)
539 {
540 $this->trans_rollback();
Barry Mienydd671972010-10-04 16:33:58 +0200541
Derek Allard2067d1a2008-11-13 22:59:24 +0000542 // If we are NOT running in strict mode, we will reset
543 // the _trans_status flag so that subsequent groups of transactions
544 // will be permitted.
545 if ($this->trans_strict === FALSE)
546 {
547 $this->_trans_status = TRUE;
548 }
549
550 log_message('debug', 'DB Transaction Failure');
551 return FALSE;
552 }
Barry Mienydd671972010-10-04 16:33:58 +0200553
Derek Allard2067d1a2008-11-13 22:59:24 +0000554 $this->trans_commit();
555 return TRUE;
556 }
557
558 // --------------------------------------------------------------------
559
560 /**
561 * Lets you retrieve the transaction flag to determine if it has failed
562 *
563 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200564 * @return bool
565 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000566 function trans_status()
567 {
568 return $this->_trans_status;
569 }
570
571 // --------------------------------------------------------------------
572
573 /**
574 * Compile Bindings
575 *
576 * @access public
577 * @param string the sql statement
578 * @param array an array of bind data
Barry Mienydd671972010-10-04 16:33:58 +0200579 * @return string
580 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000581 function compile_binds($sql, $binds)
582 {
583 if (strpos($sql, $this->bind_marker) === FALSE)
584 {
585 return $sql;
586 }
Barry Mienydd671972010-10-04 16:33:58 +0200587
Derek Allard2067d1a2008-11-13 22:59:24 +0000588 if ( ! is_array($binds))
589 {
590 $binds = array($binds);
591 }
Barry Mienydd671972010-10-04 16:33:58 +0200592
Derek Allard2067d1a2008-11-13 22:59:24 +0000593 // Get the sql segments around the bind markers
594 $segments = explode($this->bind_marker, $sql);
595
596 // The count of bind should be 1 less then the count of segments
597 // If there are more bind arguments trim it down
598 if (count($binds) >= count($segments)) {
599 $binds = array_slice($binds, 0, count($segments)-1);
600 }
601
602 // Construct the binded query
603 $result = $segments[0];
604 $i = 0;
605 foreach ($binds as $bind)
606 {
607 $result .= $this->escape($bind);
608 $result .= $segments[++$i];
609 }
610
611 return $result;
612 }
Barry Mienydd671972010-10-04 16:33:58 +0200613
Derek Allard2067d1a2008-11-13 22:59:24 +0000614 // --------------------------------------------------------------------
615
616 /**
617 * Determines if a query is a "write" type.
618 *
619 * @access public
620 * @param string An SQL query string
Barry Mienydd671972010-10-04 16:33:58 +0200621 * @return boolean
622 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000623 function is_write_type($sql)
624 {
Derek Allarde37ab382009-02-03 16:13:57 +0000625 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 +0000626 {
627 return FALSE;
628 }
629 return TRUE;
630 }
Barry Mienydd671972010-10-04 16:33:58 +0200631
Derek Allard2067d1a2008-11-13 22:59:24 +0000632 // --------------------------------------------------------------------
633
634 /**
635 * Calculate the aggregate query elapsed time
636 *
637 * @access public
638 * @param integer The number of decimal places
Barry Mienydd671972010-10-04 16:33:58 +0200639 * @return integer
640 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000641 function elapsed_time($decimals = 6)
642 {
643 return number_format($this->benchmark, $decimals);
644 }
Barry Mienydd671972010-10-04 16:33:58 +0200645
Derek Allard2067d1a2008-11-13 22:59:24 +0000646 // --------------------------------------------------------------------
647
648 /**
649 * Returns the total number of queries
650 *
651 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200652 * @return integer
653 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000654 function total_queries()
655 {
656 return $this->query_count;
657 }
Barry Mienydd671972010-10-04 16:33:58 +0200658
Derek Allard2067d1a2008-11-13 22:59:24 +0000659 // --------------------------------------------------------------------
660
661 /**
662 * Returns the last query that was executed
663 *
664 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200665 * @return void
666 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000667 function last_query()
668 {
669 return end($this->queries);
670 }
671
672 // --------------------------------------------------------------------
673
674 /**
675 * "Smart" Escape String
676 *
677 * Escapes data based on type
678 * Sets boolean and null types
679 *
680 * @access public
681 * @param string
Barry Mienydd671972010-10-04 16:33:58 +0200682 * @return mixed
683 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000684 function escape($str)
Barry Mienydd671972010-10-04 16:33:58 +0200685 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000686 if (is_string($str))
Derek Allard2067d1a2008-11-13 22:59:24 +0000687 {
Derek Jonesa377bdd2009-02-11 18:55:24 +0000688 $str = "'".$this->escape_str($str)."'";
689 }
690 elseif (is_bool($str))
691 {
692 $str = ($str === FALSE) ? 0 : 1;
693 }
694 elseif (is_null($str))
695 {
696 $str = 'NULL';
697 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000698
699 return $str;
700 }
701
702 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600703
Derek Jonese4ed5832009-02-20 21:44:59 +0000704 /**
Derek Jonesbdc7fb92009-02-20 21:55:10 +0000705 * Escape LIKE String
Derek Jonese4ed5832009-02-20 21:44:59 +0000706 *
707 * Calls the individual driver for platform
708 * specific escaping for LIKE conditions
Barry Mienydd671972010-10-04 16:33:58 +0200709 *
Derek Jonese4ed5832009-02-20 21:44:59 +0000710 * @access public
711 * @param string
712 * @return mixed
713 */
Barry Mienydd671972010-10-04 16:33:58 +0200714 function escape_like_str($str)
715 {
716 return $this->escape_str($str, TRUE);
Derek Jonese4ed5832009-02-20 21:44:59 +0000717 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000718
Derek Jonese4ed5832009-02-20 21:44:59 +0000719 // --------------------------------------------------------------------
Derek Jonese7792202010-03-02 17:24:46 -0600720
Derek Allard2067d1a2008-11-13 22:59:24 +0000721 /**
722 * Primary
723 *
Derek Jones37f4b9c2011-07-01 17:56:50 -0500724 * Retrieves the primary key. It assumes that the row in the first
Derek Allard2067d1a2008-11-13 22:59:24 +0000725 * position is the primary key
726 *
727 * @access public
728 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200729 * @return string
730 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000731 function primary($table = '')
Barry Mienydd671972010-10-04 16:33:58 +0200732 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000733 $fields = $this->list_fields($table);
Barry Mienydd671972010-10-04 16:33:58 +0200734
Derek Allard2067d1a2008-11-13 22:59:24 +0000735 if ( ! is_array($fields))
736 {
737 return FALSE;
738 }
739
740 return current($fields);
741 }
742
743 // --------------------------------------------------------------------
744
745 /**
746 * Returns an array of table names
747 *
748 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200749 * @return array
750 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000751 function list_tables($constrain_by_prefix = FALSE)
752 {
753 // Is there a cached result?
754 if (isset($this->data_cache['table_names']))
755 {
756 return $this->data_cache['table_names'];
757 }
Barry Mienydd671972010-10-04 16:33:58 +0200758
Derek Allard2067d1a2008-11-13 22:59:24 +0000759 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
760 {
761 if ($this->db_debug)
762 {
763 return $this->display_error('db_unsupported_function');
764 }
765 return FALSE;
766 }
767
768 $retval = array();
769 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200770
Derek Allard2067d1a2008-11-13 22:59:24 +0000771 if ($query->num_rows() > 0)
772 {
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500773 foreach ($query->result_array() as $row)
Derek Allard2067d1a2008-11-13 22:59:24 +0000774 {
775 if (isset($row['TABLE_NAME']))
776 {
777 $retval[] = $row['TABLE_NAME'];
778 }
779 else
780 {
781 $retval[] = array_shift($row);
782 }
783 }
784 }
785
786 $this->data_cache['table_names'] = $retval;
787 return $this->data_cache['table_names'];
788 }
Barry Mienydd671972010-10-04 16:33:58 +0200789
Derek Allard2067d1a2008-11-13 22:59:24 +0000790 // --------------------------------------------------------------------
791
792 /**
793 * Determine if a particular table exists
794 * @access public
795 * @return boolean
796 */
797 function table_exists($table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200798 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000799 return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE;
800 }
Barry Mienydd671972010-10-04 16:33:58 +0200801
Derek Allard2067d1a2008-11-13 22:59:24 +0000802 // --------------------------------------------------------------------
803
804 /**
805 * Fetch MySQL Field Names
806 *
807 * @access public
808 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200809 * @return array
Derek Allard2067d1a2008-11-13 22:59:24 +0000810 */
811 function list_fields($table = '')
812 {
813 // Is there a cached result?
814 if (isset($this->data_cache['field_names'][$table]))
815 {
816 return $this->data_cache['field_names'][$table];
817 }
Barry Mienydd671972010-10-04 16:33:58 +0200818
Derek Allard2067d1a2008-11-13 22:59:24 +0000819 if ($table == '')
820 {
821 if ($this->db_debug)
822 {
823 return $this->display_error('db_field_param_missing');
824 }
825 return FALSE;
826 }
Barry Mienydd671972010-10-04 16:33:58 +0200827
Greg Aker1edde302010-01-26 00:17:01 +0000828 if (FALSE === ($sql = $this->_list_columns($table)))
Derek Allard2067d1a2008-11-13 22:59:24 +0000829 {
830 if ($this->db_debug)
831 {
832 return $this->display_error('db_unsupported_function');
833 }
834 return FALSE;
835 }
Barry Mienydd671972010-10-04 16:33:58 +0200836
Derek Allard2067d1a2008-11-13 22:59:24 +0000837 $query = $this->query($sql);
Barry Mienydd671972010-10-04 16:33:58 +0200838
Derek Allard2067d1a2008-11-13 22:59:24 +0000839 $retval = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500840 foreach ($query->result_array() as $row)
Derek Allard2067d1a2008-11-13 22:59:24 +0000841 {
842 if (isset($row['COLUMN_NAME']))
843 {
844 $retval[] = $row['COLUMN_NAME'];
845 }
846 else
847 {
848 $retval[] = current($row);
Barry Mienydd671972010-10-04 16:33:58 +0200849 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000850 }
Barry Mienydd671972010-10-04 16:33:58 +0200851
Derek Allard2067d1a2008-11-13 22:59:24 +0000852 $this->data_cache['field_names'][$table] = $retval;
853 return $this->data_cache['field_names'][$table];
854 }
855
856 // --------------------------------------------------------------------
857
858 /**
859 * Determine if a particular field exists
860 * @access public
861 * @param string
862 * @param string
863 * @return boolean
864 */
865 function field_exists($field_name, $table_name)
Barry Mienydd671972010-10-04 16:33:58 +0200866 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000867 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
868 }
Barry Mienydd671972010-10-04 16:33:58 +0200869
Derek Allard2067d1a2008-11-13 22:59:24 +0000870 // --------------------------------------------------------------------
871
872 /**
873 * Returns an object with field data
874 *
875 * @access public
876 * @param string the table name
Barry Mienydd671972010-10-04 16:33:58 +0200877 * @return object
878 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000879 function field_data($table = '')
880 {
881 if ($table == '')
882 {
883 if ($this->db_debug)
884 {
885 return $this->display_error('db_field_param_missing');
886 }
887 return FALSE;
888 }
Barry Mienydd671972010-10-04 16:33:58 +0200889
Derek Allard2067d1a2008-11-13 22:59:24 +0000890 $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE)));
891
892 return $query->field_data();
Barry Mienydd671972010-10-04 16:33:58 +0200893 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000894
895 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200896
Derek Allard2067d1a2008-11-13 22:59:24 +0000897 /**
898 * Generate an insert string
899 *
900 * @access public
901 * @param string the table upon which the query will be performed
902 * @param array an associative array data of key/values
Barry Mienydd671972010-10-04 16:33:58 +0200903 * @return string
904 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000905 function insert_string($table, $data)
906 {
907 $fields = array();
908 $values = array();
Barry Mienydd671972010-10-04 16:33:58 +0200909
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500910 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000911 {
912 $fields[] = $this->_escape_identifiers($key);
913 $values[] = $this->escape($val);
914 }
Barry Mienydd671972010-10-04 16:33:58 +0200915
Derek Allard2067d1a2008-11-13 22:59:24 +0000916 return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
Barry Mienydd671972010-10-04 16:33:58 +0200917 }
918
Derek Allard2067d1a2008-11-13 22:59:24 +0000919 // --------------------------------------------------------------------
920
921 /**
922 * Generate an update string
923 *
924 * @access public
925 * @param string the table upon which the query will be performed
926 * @param array an associative array data of key/values
927 * @param mixed the "where" statement
Barry Mienydd671972010-10-04 16:33:58 +0200928 * @return string
929 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000930 function update_string($table, $data, $where)
931 {
932 if ($where == '')
933 {
934 return false;
935 }
Barry Mienydd671972010-10-04 16:33:58 +0200936
Derek Allard2067d1a2008-11-13 22:59:24 +0000937 $fields = array();
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500938 foreach ($data as $key => $val)
Derek Allard2067d1a2008-11-13 22:59:24 +0000939 {
940 $fields[$this->_protect_identifiers($key)] = $this->escape($val);
941 }
942
943 if ( ! is_array($where))
944 {
945 $dest = array($where);
946 }
947 else
948 {
949 $dest = array();
950 foreach ($where as $key => $val)
951 {
952 $prefix = (count($dest) == 0) ? '' : ' AND ';
Barry Mienydd671972010-10-04 16:33:58 +0200953
Derek Allard2067d1a2008-11-13 22:59:24 +0000954 if ($val !== '')
955 {
956 if ( ! $this->_has_operator($key))
957 {
958 $key .= ' =';
959 }
Barry Mienydd671972010-10-04 16:33:58 +0200960
Derek Allard2067d1a2008-11-13 22:59:24 +0000961 $val = ' '.$this->escape($val);
962 }
Barry Mienydd671972010-10-04 16:33:58 +0200963
Derek Allard2067d1a2008-11-13 22:59:24 +0000964 $dest[] = $prefix.$key.$val;
965 }
Barry Mienydd671972010-10-04 16:33:58 +0200966 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000967
968 return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest);
Barry Mienydd671972010-10-04 16:33:58 +0200969 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000970
971 // --------------------------------------------------------------------
972
973 /**
974 * Tests whether the string has an SQL operator
975 *
976 * @access private
977 * @param string
978 * @return bool
979 */
980 function _has_operator($str)
981 {
982 $str = trim($str);
983 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
984 {
985 return FALSE;
986 }
987
988 return TRUE;
989 }
990
991 // --------------------------------------------------------------------
992
993 /**
994 * Enables a native PHP function to be run, using a platform agnostic wrapper.
995 *
996 * @access public
997 * @param string the function name
998 * @param mixed any parameters needed by the function
Barry Mienydd671972010-10-04 16:33:58 +0200999 * @return mixed
1000 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001001 function call_function($function)
1002 {
1003 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
Barry Mienydd671972010-10-04 16:33:58 +02001004
Derek Allard2067d1a2008-11-13 22:59:24 +00001005 if (FALSE === strpos($driver, $function))
1006 {
1007 $function = $driver.$function;
1008 }
Barry Mienydd671972010-10-04 16:33:58 +02001009
Derek Allard2067d1a2008-11-13 22:59:24 +00001010 if ( ! function_exists($function))
1011 {
1012 if ($this->db_debug)
1013 {
1014 return $this->display_error('db_unsupported_function');
1015 }
1016 return FALSE;
1017 }
1018 else
1019 {
1020 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1021
1022 return call_user_func_array($function, $args);
1023 }
1024 }
1025
1026 // --------------------------------------------------------------------
1027
1028 /**
1029 * Set Cache Directory Path
1030 *
1031 * @access public
1032 * @param string the path to the cache directory
1033 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001034 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001035 function cache_set_path($path = '')
1036 {
1037 $this->cachedir = $path;
1038 }
1039
1040 // --------------------------------------------------------------------
1041
1042 /**
1043 * Enable Query Caching
1044 *
1045 * @access public
1046 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001047 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001048 function cache_on()
1049 {
1050 $this->cache_on = TRUE;
1051 return TRUE;
1052 }
1053
1054 // --------------------------------------------------------------------
1055
1056 /**
1057 * Disable Query Caching
1058 *
1059 * @access public
1060 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001061 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001062 function cache_off()
1063 {
1064 $this->cache_on = FALSE;
1065 return FALSE;
1066 }
Barry Mienydd671972010-10-04 16:33:58 +02001067
Derek Allard2067d1a2008-11-13 22:59:24 +00001068
1069 // --------------------------------------------------------------------
1070
1071 /**
1072 * Delete the cache files associated with a particular URI
1073 *
1074 * @access public
1075 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001076 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001077 function cache_delete($segment_one = '', $segment_two = '')
1078 {
1079 if ( ! $this->_cache_init())
1080 {
1081 return FALSE;
1082 }
1083 return $this->CACHE->delete($segment_one, $segment_two);
1084 }
1085
1086 // --------------------------------------------------------------------
1087
1088 /**
1089 * Delete All cache files
1090 *
1091 * @access public
1092 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001093 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001094 function cache_delete_all()
1095 {
1096 if ( ! $this->_cache_init())
1097 {
1098 return FALSE;
1099 }
1100
1101 return $this->CACHE->delete_all();
1102 }
1103
1104 // --------------------------------------------------------------------
1105
1106 /**
1107 * Initialize the Cache Class
1108 *
1109 * @access private
1110 * @return void
Barry Mienydd671972010-10-04 16:33:58 +02001111 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001112 function _cache_init()
1113 {
1114 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1115 {
1116 return TRUE;
1117 }
Derek Allarde37ab382009-02-03 16:13:57 +00001118
1119 if ( ! class_exists('CI_DB_Cache'))
Derek Allard2067d1a2008-11-13 22:59:24 +00001120 {
Greg Aker3a746652011-04-19 10:59:47 -05001121 if ( ! @include(BASEPATH.'database/DB_cache.php'))
Derek Allarde37ab382009-02-03 16:13:57 +00001122 {
1123 return $this->cache_off();
1124 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001125 }
Derek Allarde37ab382009-02-03 16:13:57 +00001126
Derek Allard2067d1a2008-11-13 22:59:24 +00001127 $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1128 return TRUE;
1129 }
1130
1131 // --------------------------------------------------------------------
1132
1133 /**
1134 * Close DB Connection
1135 *
1136 * @access public
Barry Mienydd671972010-10-04 16:33:58 +02001137 * @return void
1138 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001139 function close()
1140 {
1141 if (is_resource($this->conn_id) OR is_object($this->conn_id))
1142 {
1143 $this->_close($this->conn_id);
1144 }
1145 $this->conn_id = FALSE;
1146 }
Barry Mienydd671972010-10-04 16:33:58 +02001147
Derek Allard2067d1a2008-11-13 22:59:24 +00001148 // --------------------------------------------------------------------
1149
1150 /**
1151 * Display an error message
1152 *
1153 * @access public
1154 * @param string the error message
1155 * @param string any "swap" values
1156 * @param boolean whether to localize the message
Barry Mienydd671972010-10-04 16:33:58 +02001157 * @return string sends the application/error_db.php template
1158 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001159 function display_error($error = '', $swap = '', $native = FALSE)
1160 {
Derek Jonese7792202010-03-02 17:24:46 -06001161 $LANG =& load_class('Lang', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001162 $LANG->load('db');
1163
1164 $heading = $LANG->line('db_error_heading');
1165
1166 if ($native == TRUE)
1167 {
1168 $message = $error;
1169 }
1170 else
1171 {
1172 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
1173 }
Barry Mienydd671972010-10-04 16:33:58 +02001174
Pascal Kriete60f8c392010-08-25 18:03:28 +02001175 // Find the most likely culprit of the error by going through
1176 // the backtrace until the source file is no longer in the
1177 // database folder.
Barry Mienydd671972010-10-04 16:33:58 +02001178
Pascal Kriete60f8c392010-08-25 18:03:28 +02001179 $trace = debug_backtrace();
1180
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001181 foreach ($trace as $call)
Pascal Kriete60f8c392010-08-25 18:03:28 +02001182 {
1183 if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
1184 {
1185 // Found it - use a relative path for safety
1186 $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
1187 $message[] = 'Line Number: '.$call['line'];
Barry Mienydd671972010-10-04 16:33:58 +02001188
Pascal Kriete60f8c392010-08-25 18:03:28 +02001189 break;
1190 }
1191 }
Barry Mienydd671972010-10-04 16:33:58 +02001192
Derek Jonese7792202010-03-02 17:24:46 -06001193 $error =& load_class('Exceptions', 'core');
Derek Allard2067d1a2008-11-13 22:59:24 +00001194 echo $error->show_error($heading, $message, 'error_db');
1195 exit;
1196 }
1197
1198 // --------------------------------------------------------------------
1199
1200 /**
1201 * Protect Identifiers
1202 *
1203 * This function adds backticks if appropriate based on db type
1204 *
1205 * @access private
1206 * @param mixed the item to escape
1207 * @return mixed the item with backticks
1208 */
1209 function protect_identifiers($item, $prefix_single = FALSE)
1210 {
1211 return $this->_protect_identifiers($item, $prefix_single);
1212 }
1213
1214 // --------------------------------------------------------------------
1215
1216 /**
1217 * Protect Identifiers
1218 *
1219 * This function is used extensively by the Active Record class, and by
Barry Mienydd671972010-10-04 16:33:58 +02001220 * a couple functions in this class.
Derek Allard2067d1a2008-11-13 22:59:24 +00001221 * It takes a column or table name (optionally with an alias) and inserts
Derek Jones37f4b9c2011-07-01 17:56:50 -05001222 * the table prefix onto it. Some logic is necessary in order to deal with
1223 * column names that include the path. Consider a query like this:
Derek Allard2067d1a2008-11-13 22:59:24 +00001224 *
1225 * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
1226 *
1227 * Or a query with aliasing:
1228 *
1229 * SELECT m.member_id, m.member_name FROM members AS m
1230 *
1231 * Since the column name can include up to four segments (host, DB, table, column)
1232 * or also have an alias prefix, we need to do a bit of work to figure this out and
1233 * insert the table prefix (if it exists) in the proper position, and escape only
1234 * the correct identifiers.
1235 *
1236 * @access private
1237 * @param string
1238 * @param bool
1239 * @param mixed
1240 * @param bool
1241 * @return string
Barry Mienydd671972010-10-04 16:33:58 +02001242 */
Derek Allard2067d1a2008-11-13 22:59:24 +00001243 function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1244 {
1245 if ( ! is_bool($protect_identifiers))
1246 {
1247 $protect_identifiers = $this->_protect_identifiers;
1248 }
Derek Allarde37ab382009-02-03 16:13:57 +00001249
1250 if (is_array($item))
1251 {
1252 $escaped_array = array();
1253
Pascal Krietec3a4a8d2011-02-14 13:40:08 -05001254 foreach ($item as $k => $v)
Derek Allarde37ab382009-02-03 16:13:57 +00001255 {
1256 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v);
1257 }
1258
1259 return $escaped_array;
1260 }
1261
Derek Allard2067d1a2008-11-13 22:59:24 +00001262 // Convert tabs or multiple spaces into single spaces
Derek Jones7b3b96c2009-02-10 21:01:47 +00001263 $item = preg_replace('/[\t ]+/', ' ', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001264
Derek Allard2067d1a2008-11-13 22:59:24 +00001265 // If the item has an alias declaration we remove it and set it aside.
1266 // Basically we remove everything to the right of the first space
1267 $alias = '';
1268 if (strpos($item, ' ') !== FALSE)
Derek Allard911d3e02008-12-15 14:08:35 +00001269 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001270 $alias = strstr($item, " ");
1271 $item = substr($item, 0, - strlen($alias));
1272 }
1273
Derek Allard911d3e02008-12-15 14:08:35 +00001274 // This is basically a bug fix for queries that use MAX, MIN, etc.
Barry Mienydd671972010-10-04 16:33:58 +02001275 // If a parenthesis is found we know that we do not need to
Derek Jones37f4b9c2011-07-01 17:56:50 -05001276 // escape the data or add a prefix. There's probably a more graceful
Derek Allard911d3e02008-12-15 14:08:35 +00001277 // way to deal with this, but I'm not thinking of it -- Rick
1278 if (strpos($item, '(') !== FALSE)
1279 {
1280 return $item.$alias;
1281 }
1282
Derek Allard2067d1a2008-11-13 22:59:24 +00001283 // Break the string apart if it contains periods, then insert the table prefix
1284 // in the correct location, assuming the period doesn't indicate that we're dealing
1285 // with an alias. While we're at it, we will escape the components
1286 if (strpos($item, '.') !== FALSE)
1287 {
1288 $parts = explode('.', $item);
Barry Mienydd671972010-10-04 16:33:58 +02001289
Derek Allard2067d1a2008-11-13 22:59:24 +00001290 // Does the first segment of the exploded item match
Derek Jones37f4b9c2011-07-01 17:56:50 -05001291 // one of the aliases previously identified? If so,
Derek Allard2067d1a2008-11-13 22:59:24 +00001292 // we have nothing more to do other than escape the item
1293 if (in_array($parts[0], $this->ar_aliased_tables))
Derek Allard911d3e02008-12-15 14:08:35 +00001294 {
Derek Allard2067d1a2008-11-13 22:59:24 +00001295 if ($protect_identifiers === TRUE)
1296 {
1297 foreach ($parts as $key => $val)
1298 {
1299 if ( ! in_array($val, $this->_reserved_identifiers))
1300 {
1301 $parts[$key] = $this->_escape_identifiers($val);
1302 }
1303 }
Barry Mienydd671972010-10-04 16:33:58 +02001304
Derek Allard2067d1a2008-11-13 22:59:24 +00001305 $item = implode('.', $parts);
Barry Mienydd671972010-10-04 16:33:58 +02001306 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001307 return $item.$alias;
1308 }
Barry Mienydd671972010-10-04 16:33:58 +02001309
Derek Jones37f4b9c2011-07-01 17:56:50 -05001310 // Is there a table prefix defined in the config file? If not, no need to do anything
Derek Allard2067d1a2008-11-13 22:59:24 +00001311 if ($this->dbprefix != '')
1312 {
1313 // We now add the table prefix based on some logic.
1314 // Do we have 4 segments (hostname.database.table.column)?
1315 // If so, we add the table prefix to the column name in the 3rd segment.
1316 if (isset($parts[3]))
1317 {
1318 $i = 2;
1319 }
1320 // Do we have 3 segments (database.table.column)?
1321 // If so, we add the table prefix to the column name in 2nd position
1322 elseif (isset($parts[2]))
1323 {
1324 $i = 1;
1325 }
1326 // Do we have 2 segments (table.column)?
1327 // If so, we add the table prefix to the column name in 1st segment
1328 else
1329 {
1330 $i = 0;
1331 }
Barry Mienydd671972010-10-04 16:33:58 +02001332
Derek Allard2067d1a2008-11-13 22:59:24 +00001333 // This flag is set when the supplied $item does not contain a field name.
1334 // This can happen when this function is being called from a JOIN.
1335 if ($field_exists == FALSE)
1336 {
1337 $i++;
1338 }
Barry Mienydd671972010-10-04 16:33:58 +02001339
Derek Jones55acc8b2009-07-11 03:38:13 +00001340 // Verify table prefix and replace if necessary
1341 if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0)
1342 {
1343 $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]);
1344 }
Barry Mienydd671972010-10-04 16:33:58 +02001345
Derek Allard2067d1a2008-11-13 22:59:24 +00001346 // We only add the table prefix if it does not already exist
1347 if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix)
1348 {
1349 $parts[$i] = $this->dbprefix.$parts[$i];
1350 }
Barry Mienydd671972010-10-04 16:33:58 +02001351
Derek Allard2067d1a2008-11-13 22:59:24 +00001352 // Put the parts back together
1353 $item = implode('.', $parts);
1354 }
Barry Mienydd671972010-10-04 16:33:58 +02001355
Derek Allard2067d1a2008-11-13 22:59:24 +00001356 if ($protect_identifiers === TRUE)
1357 {
1358 $item = $this->_escape_identifiers($item);
1359 }
Barry Mienydd671972010-10-04 16:33:58 +02001360
Derek Allard2067d1a2008-11-13 22:59:24 +00001361 return $item.$alias;
1362 }
1363
Derek Jones37f4b9c2011-07-01 17:56:50 -05001364 // Is there a table prefix? If not, no need to insert it
Derek Allard2067d1a2008-11-13 22:59:24 +00001365 if ($this->dbprefix != '')
1366 {
Derek Jones55acc8b2009-07-11 03:38:13 +00001367 // Verify table prefix and replace if necessary
1368 if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0)
1369 {
1370 $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item);
1371 }
1372
Derek Allard2067d1a2008-11-13 22:59:24 +00001373 // Do we prefix an item with no segments?
1374 if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix)
1375 {
1376 $item = $this->dbprefix.$item;
Barry Mienydd671972010-10-04 16:33:58 +02001377 }
Derek Allard2067d1a2008-11-13 22:59:24 +00001378 }
1379
1380 if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers))
1381 {
1382 $item = $this->_escape_identifiers($item);
1383 }
Barry Mienydd671972010-10-04 16:33:58 +02001384
Derek Allard2067d1a2008-11-13 22:59:24 +00001385 return $item.$alias;
1386 }
1387
1388
1389}
1390
1391
1392/* End of file DB_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +00001393/* Location: ./system/database/DB_driver.php */