blob: 1450a06445f8e8cd7e9b8a7313b3b73ab342eef3 [file] [log] [blame]
Derek Jones0b59f272008-05-13 04:22:33 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allardd2df9bc2007-04-15 17:41:17 +00002/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Rick Ellis37b3ecf2008-09-12 23:34:18 +00009 * @copyright Copyright (c) 2008, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @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
Derek Allard3d879d52008-01-18 19:41:32 +000028 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000029 * @link http://codeigniter.com/user_guide/database/
Derek Allardd2df9bc2007-04-15 17:41:17 +000030 */
31class CI_DB_driver {
32
33 var $username;
34 var $password;
35 var $hostname;
36 var $database;
37 var $dbdriver = 'mysql';
38 var $dbprefix = '';
Derek Jonescafd63e2008-05-14 15:08:17 +000039 var $char_set = 'utf8';
40 var $dbcollat = 'utf8_general_ci';
Derek Allard39b622d2008-01-16 21:10:09 +000041 var $autoinit = TRUE; // Whether to automatically initialize the DB
42 var $swap_pre = '';
Derek Allardd2df9bc2007-04-15 17:41:17 +000043 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 = '?';
Rick Ellis40990462007-07-17 21:40:44 +000051 var $save_queries = TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +000052 var $queries = array();
Derek Jones56e9fa52008-01-23 17:26:37 +000053 var $query_times = array();
Derek Allardd2df9bc2007-04-15 17:41:17 +000054 var $data_cache = array();
55 var $trans_enabled = TRUE;
Rick Ellis244b4c72008-05-12 18:21:33 +000056 var $trans_strict = TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +000057 var $_trans_depth = 0;
Rick Ellis28239ad2007-06-11 04:26:39 +000058 var $_trans_status = TRUE; // Used with transactions to determine if a rollback should occur
Derek Allardd2df9bc2007-04-15 17:41:17 +000059 var $cache_on = FALSE;
60 var $cachedir = '';
61 var $cache_autodel = FALSE;
62 var $CACHE; // The cache class object
63
64
65 // These are use with Oracle
66 var $stmt_id;
67 var $curs_id;
68 var $limit_used;
69
70
71
72 /**
73 * Constructor. Accepts one parameter containing the database
74 * connection settings.
75 *
Derek Jones7dd38382008-02-13 04:52:58 +000076 * @param array
Derek Allardd2df9bc2007-04-15 17:41:17 +000077 */
78 function CI_DB_driver($params)
79 {
Derek Allardd2df9bc2007-04-15 17:41:17 +000080 if (is_array($params))
81 {
Derek Allard39b622d2008-01-16 21:10:09 +000082 foreach ($params as $key => $val)
Derek Allardd2df9bc2007-04-15 17:41:17 +000083 {
Derek Allard39b622d2008-01-16 21:10:09 +000084 $this->$key = $val;
Derek Allardd2df9bc2007-04-15 17:41:17 +000085 }
86 }
Derek Allard39b622d2008-01-16 21:10:09 +000087
88 log_message('debug', 'Database Driver Class Initialized');
89 }
90
91 // --------------------------------------------------------------------
92
93 /**
94 * Initialize Database Settings
95 *
96 * @access private Called by the constructor
97 * @param mixed
98 * @return void
99 */
100 function initialize($create_db = FALSE)
101 {
Derek Allardd2df9bc2007-04-15 17:41:17 +0000102 // If an existing DB connection resource is supplied
103 // there is no need to connect and select the database
Derek Jones05097752008-05-07 19:58:23 +0000104 if (is_resource($this->conn_id) OR is_object($this->conn_id))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000105 {
106 return TRUE;
107 }
108
109 // Connect to the database
110 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
111
112 // No connection? Throw an error
Rick Ellis244b4c72008-05-12 18:21:33 +0000113 if ( ! $this->conn_id)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000114 {
115 log_message('error', 'Unable to connect to the database');
116
117 if ($this->db_debug)
118 {
119 $this->display_error('db_unable_to_connect');
120 }
121 return FALSE;
122 }
123
124 // Select the database
125 if ($this->database != '')
126 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000127 if ( ! $this->db_select())
Derek Allardd2df9bc2007-04-15 17:41:17 +0000128 {
Derek Allard39b622d2008-01-16 21:10:09 +0000129 // Should we attempt to create the database?
130 if ($create_db == TRUE)
131 {
132 // Load the DB utility class
133 $CI =& get_instance();
134 $CI->load->dbutil();
135
136 // Create the DB
Rick Ellis244b4c72008-05-12 18:21:33 +0000137 if ( ! $CI->dbutil->create_database($this->database))
Derek Allard39b622d2008-01-16 21:10:09 +0000138 {
139 log_message('error', 'Unable to create database: '.$this->database);
140
141 if ($this->db_debug)
142 {
143 $this->display_error('db_unable_to_create', $this->database);
144 }
Derek Allard993925b2008-08-21 12:43:31 +0000145 return FALSE;
Derek Allard39b622d2008-01-16 21:10:09 +0000146 }
147 else
148 {
149 // In the event the DB was created we need to select it
150 if ($this->db_select())
151 {
Derek Jones0b59f272008-05-13 04:22:33 +0000152 if ( ! $this->db_set_charset($this->char_set, $this->dbcollat))
Derek Allard39b622d2008-01-16 21:10:09 +0000153 {
154 log_message('error', 'Unable to set database connection charset: '.$this->char_set);
155
156 if ($this->db_debug)
157 {
158 $this->display_error('db_unable_to_set_charset', $this->char_set);
159 }
160
161 return FALSE;
162 }
163
164 return TRUE;
165 }
166 }
167 }
168
Derek Allardd2df9bc2007-04-15 17:41:17 +0000169 log_message('error', 'Unable to select database: '.$this->database);
170
171 if ($this->db_debug)
172 {
173 $this->display_error('db_unable_to_select', $this->database);
174 }
175 return FALSE;
176 }
Derek Allard39b622d2008-01-16 21:10:09 +0000177
Derek Jones0b59f272008-05-13 04:22:33 +0000178 if ( ! $this->db_set_charset($this->char_set, $this->dbcollat))
Derek Allard39b622d2008-01-16 21:10:09 +0000179 {
180 log_message('error', 'Unable to set database connection charset: '.$this->char_set);
181
182 if ($this->db_debug)
183 {
184 $this->display_error('db_unable_to_set_charset', $this->char_set);
185 }
186
187 return FALSE;
188 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000189 }
190
191 return TRUE;
192 }
193
194 // --------------------------------------------------------------------
195
196 /**
197 * The name of the platform in use (mysql, mssql, etc...)
198 *
199 * @access public
200 * @return string
201 */
202 function platform()
203 {
204 return $this->dbdriver;
205 }
206
207 // --------------------------------------------------------------------
208
209 /**
210 * Database Version Number. Returns a string containing the
211 * version of the database being used
212 *
213 * @access public
214 * @return string
215 */
216 function version()
217 {
218 if (FALSE === ($sql = $this->_version()))
219 {
220 if ($this->db_debug)
221 {
222 return $this->display_error('db_unsupported_function');
223 }
Derek Allard993925b2008-08-21 12:43:31 +0000224 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000225 }
226
227 if ($this->dbdriver == 'oci8')
228 {
229 return $sql;
230 }
231
232 $query = $this->query($sql);
Derek Allard39b622d2008-01-16 21:10:09 +0000233 return $query->row('ver');
Derek Allardd2df9bc2007-04-15 17:41:17 +0000234 }
235
236 // --------------------------------------------------------------------
237
238 /**
239 * Execute the query
240 *
241 * Accepts an SQL string as input and returns a result object upon
242 * successful execution of a "read" type query. Returns boolean TRUE
243 * upon successful execution of a "write" type query. Returns boolean
244 * FALSE upon failure, and if the $db_debug variable is set to TRUE
245 * will raise an error.
246 *
247 * @access public
248 * @param string An SQL query string
249 * @param array An array of binding data
250 * @return mixed
251 */
252 function query($sql, $binds = FALSE, $return_object = TRUE)
253 {
254 if ($sql == '')
255 {
256 if ($this->db_debug)
257 {
258 log_message('error', 'Invalid query: '.$sql);
259 return $this->display_error('db_invalid_query');
260 }
Derek Allard993925b2008-08-21 12:43:31 +0000261 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000262 }
Derek Allard39b622d2008-01-16 21:10:09 +0000263
264 // Verify table prefix and replace if necessary
265 if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
266 {
267 $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
268 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000269
270 // Is query caching enabled? If the query is a "read type"
271 // we will load the caching class and return the previously
272 // cached query if it exists
273 if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
274 {
275 if ($this->_cache_init())
276 {
277 $this->load_rdriver();
278 if (FALSE !== ($cache = $this->CACHE->read($sql)))
279 {
280 return $cache;
281 }
282 }
283 }
284
285 // Compile binds if needed
286 if ($binds !== FALSE)
287 {
288 $sql = $this->compile_binds($sql, $binds);
289 }
290
291 // Save the query for debugging
Rick Ellis40990462007-07-17 21:40:44 +0000292 if ($this->save_queries == TRUE)
293 {
294 $this->queries[] = $sql;
295 }
Derek Allard39b622d2008-01-16 21:10:09 +0000296
Derek Allardd2df9bc2007-04-15 17:41:17 +0000297 // Start the Query Timer
298 $time_start = list($sm, $ss) = explode(' ', microtime());
299
300 // Run the Query
301 if (FALSE === ($this->result_id = $this->simple_query($sql)))
302 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000303 if ($this->save_queries == TRUE)
304 {
305 $this->query_times[] = 0;
306 }
307
Derek Allardd2df9bc2007-04-15 17:41:17 +0000308 // This will trigger a rollback if transactions are being used
Rick Ellis28239ad2007-06-11 04:26:39 +0000309 $this->_trans_status = FALSE;
Rick Ellis244b4c72008-05-12 18:21:33 +0000310
Derek Allardd2df9bc2007-04-15 17:41:17 +0000311 if ($this->db_debug)
312 {
Derek Jonesf38fe092008-05-13 20:28:11 +0000313 // grab the error number and message now, as we might run some
314 // additional queries before displaying the error
315 $error_no = $this->_error_number();
316 $error_msg = $this->_error_message();
317
Rick Ellis244b4c72008-05-12 18:21:33 +0000318 // We call this function in order to roll-back queries
319 // if transactions are enabled. If we don't call this here
320 // the error message will trigger an exit, causing the
321 // transactions to remain in limbo.
322 $this->trans_complete();
Derek Jonesf38fe092008-05-13 20:28:11 +0000323
Rick Ellis244b4c72008-05-12 18:21:33 +0000324 // Log and display errors
Derek Allardd2df9bc2007-04-15 17:41:17 +0000325 log_message('error', 'Query error: '.$this->_error_message());
326 return $this->display_error(
327 array(
Derek Jonesf38fe092008-05-13 20:28:11 +0000328 'Error Number: '.$error_no,
329 $error_msg,
Derek Allardd2df9bc2007-04-15 17:41:17 +0000330 $sql
331 )
332 );
333 }
334
Derek Allard39b622d2008-01-16 21:10:09 +0000335 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000336 }
337
338 // Stop and aggregate the query time results
339 $time_end = list($em, $es) = explode(' ', microtime());
340 $this->benchmark += ($em + $es) - ($sm + $ss);
341
Derek Jones56e9fa52008-01-23 17:26:37 +0000342 if ($this->save_queries == TRUE)
343 {
344 $this->query_times[] = ($em + $es) - ($sm + $ss);
345 }
346
Derek Allardd2df9bc2007-04-15 17:41:17 +0000347 // Increment the query counter
348 $this->query_count++;
349
350 // Was the query a "write" type?
351 // If so we'll simply return true
352 if ($this->is_write_type($sql) === TRUE)
353 {
354 // If caching is enabled we'll auto-cleanup any
355 // existing files related to this particular URI
356 if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
357 {
358 $this->CACHE->delete();
359 }
360
361 return TRUE;
362 }
363
364 // Return TRUE if we don't need to create a result object
365 // Currently only the Oracle driver uses this when stored
366 // procedures are used
367 if ($return_object !== TRUE)
368 {
369 return TRUE;
370 }
371
372 // Load and instantiate the result driver
373
374 $driver = $this->load_rdriver();
375 $RES = new $driver();
376 $RES->conn_id = $this->conn_id;
377 $RES->result_id = $this->result_id;
Derek Allard39b622d2008-01-16 21:10:09 +0000378 $RES->num_rows = $RES->num_rows();
Derek Allard060052d2007-07-14 14:26:13 +0000379
Derek Allardd2df9bc2007-04-15 17:41:17 +0000380 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 }
Derek Allard39b622d2008-01-16 21:10:09 +0000386
Derek Allardd2df9bc2007-04-15 17:41:17 +0000387 // Is query caching enabled? If so, we'll serialize the
388 // result object and save it to a cache file.
389 if ($this->cache_on == TRUE AND $this->_cache_init())
390 {
391 // We'll create a new instance of the result object
392 // only without the platform specific driver since
393 // we can't use it with cached data (the query result
394 // resource ID won't be any good once we've cached the
395 // result object, so we'll have to compile the data
396 // and save it)
397 $CR = new CI_DB_result();
398 $CR->num_rows = $RES->num_rows();
399 $CR->result_object = $RES->result_object();
400 $CR->result_array = $RES->result_array();
401
402 // Reset these since cached objects can not utilize resource IDs.
403 $CR->conn_id = NULL;
404 $CR->result_id = NULL;
405
406 $this->CACHE->write($sql, $CR);
407 }
408
409 return $RES;
410 }
411
412 // --------------------------------------------------------------------
413
414 /**
415 * Load the result drivers
416 *
417 * @access public
418 * @return string the name of the result class
419 */
420 function load_rdriver()
421 {
422 $driver = 'CI_DB_'.$this->dbdriver.'_result';
423
Rick Ellis244b4c72008-05-12 18:21:33 +0000424 if ( ! class_exists($driver))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000425 {
426 include_once(BASEPATH.'database/DB_result'.EXT);
427 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result'.EXT);
428 }
429
430 return $driver;
431 }
432
433 // --------------------------------------------------------------------
434
435 /**
436 * Simple Query
437 * This is a simplified version of the query() function. Internally
438 * we only use it when running transaction commands since they do
439 * not require all the features of the main query() function.
440 *
441 * @access public
442 * @param string the sql query
443 * @return mixed
444 */
445 function simple_query($sql)
446 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000447 if ( ! $this->conn_id)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000448 {
449 $this->initialize();
450 }
451
452 return $this->_execute($sql);
453 }
454
455 // --------------------------------------------------------------------
456
457 /**
458 * Disable Transactions
459 * This permits transactions to be disabled at run-time.
460 *
461 * @access public
462 * @return void
463 */
464 function trans_off()
465 {
466 $this->trans_enabled = FALSE;
467 }
468
469 // --------------------------------------------------------------------
470
471 /**
Rick Ellis244b4c72008-05-12 18:21:33 +0000472 * Enable/disable Transaction Strict Mode
473 * When strict mode is enabled, if you are running multiple groups of
474 * transactions, if one group fails all groups will be rolled back.
475 * If strict mode is disabled, each group is treated autonomously, meaning
476 * a failure of one group will not affect any others
477 *
478 * @access public
479 * @return void
480 */
481 function trans_strict($mode = TRUE)
482 {
483 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
484 }
485
486 // --------------------------------------------------------------------
487
488 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000489 * Start Transaction
490 *
491 * @access public
492 * @return void
493 */
494 function trans_start($test_mode = FALSE)
495 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000496 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000497 {
498 return FALSE;
499 }
500
501 // When transactions are nested we only begin/commit/rollback the outermost ones
502 if ($this->_trans_depth > 0)
503 {
504 $this->_trans_depth += 1;
505 return;
506 }
507
508 $this->trans_begin($test_mode);
509 }
510
511 // --------------------------------------------------------------------
512
513 /**
514 * Complete Transaction
515 *
516 * @access public
517 * @return bool
518 */
519 function trans_complete()
520 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000521 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000522 {
523 return FALSE;
524 }
525
526 // When transactions are nested we only begin/commit/rollback the outermost ones
527 if ($this->_trans_depth > 1)
528 {
529 $this->_trans_depth -= 1;
530 return TRUE;
531 }
532
Rick Ellis244b4c72008-05-12 18:21:33 +0000533 // The query() function will set this flag to FALSE in the event that a query failed
Rick Ellis28239ad2007-06-11 04:26:39 +0000534 if ($this->_trans_status === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000535 {
536 $this->trans_rollback();
537
Rick Ellis244b4c72008-05-12 18:21:33 +0000538 // If we are NOT running in strict mode, we will reset
539 // the _trans_status flag so that subsequent groups of transactions
540 // will be permitted.
541 if ($this->trans_strict === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000542 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000543 $this->_trans_status = TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000544 }
Rick Ellis244b4c72008-05-12 18:21:33 +0000545
Derek Allard993925b2008-08-21 12:43:31 +0000546 log_message('debug', 'DB Transaction Failure');
547 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000548 }
549
550 $this->trans_commit();
Derek Allard993925b2008-08-21 12:43:31 +0000551 return TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000552 }
553
554 // --------------------------------------------------------------------
555
556 /**
557 * Lets you retrieve the transaction flag to determine if it has failed
558 *
559 * @access public
560 * @return bool
561 */
562 function trans_status()
563 {
Rick Ellis28239ad2007-06-11 04:26:39 +0000564 return $this->_trans_status;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000565 }
566
567 // --------------------------------------------------------------------
568
569 /**
570 * Compile Bindings
571 *
572 * @access public
573 * @param string the sql statement
574 * @param array an array of bind data
575 * @return string
576 */
577 function compile_binds($sql, $binds)
Derek Allardc0743382008-02-11 05:54:44 +0000578 {
579 if (strpos($sql, $this->bind_marker) === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000580 {
581 return $sql;
582 }
583
Rick Ellis244b4c72008-05-12 18:21:33 +0000584 if ( ! is_array($binds))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000585 {
586 $binds = array($binds);
587 }
588
Derek Allardc0743382008-02-11 05:54:44 +0000589 // Get the sql segments around the bind markers
590 $segments = explode($this->bind_marker, $sql);
591
592 // The count of bind should be 1 less then the count of segments
593 // If there are more bind arguments trim it down
594 if (count($binds) >= count($segments)) {
595 $binds = array_slice($binds, 0, count($segments)-1);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000596 }
597
Derek Allardc0743382008-02-11 05:54:44 +0000598 // Construct the binded query
599 $result = $segments[0];
600 $i = 0;
601 foreach ($binds as $bind)
602 {
603 $result .= $this->escape($bind);
604 $result .= $segments[++$i];
605 }
606
607 return $result;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000608 }
609
610 // --------------------------------------------------------------------
611
612 /**
613 * Determines if a query is a "write" type.
614 *
615 * @access public
616 * @param string An SQL query string
617 * @return boolean
618 */
619 function is_write_type($sql)
620 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000621 if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000622 {
623 return FALSE;
624 }
625 return TRUE;
626 }
627
628 // --------------------------------------------------------------------
629
630 /**
631 * Calculate the aggregate query elapsed time
632 *
633 * @access public
634 * @param integer The number of decimal places
635 * @return integer
636 */
637 function elapsed_time($decimals = 6)
638 {
639 return number_format($this->benchmark, $decimals);
640 }
641
642 // --------------------------------------------------------------------
643
644 /**
645 * Returns the total number of queries
646 *
647 * @access public
648 * @return integer
649 */
650 function total_queries()
651 {
652 return $this->query_count;
653 }
654
655 // --------------------------------------------------------------------
656
657 /**
658 * Returns the last query that was executed
659 *
660 * @access public
661 * @return void
662 */
663 function last_query()
664 {
665 return end($this->queries);
666 }
667
668 // --------------------------------------------------------------------
669
670 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000671 * Protect Identifiers
672 *
673 * This function adds backticks if appropriate based on db type
674 *
675 * @access private
676 * @param mixed the item to escape
677 * @param boolean only affect the first word
678 * @return mixed the item with backticks
679 */
680 function protect_identifiers($item, $first_word_only = FALSE)
681 {
Derek Allard1a704ce2008-01-27 15:03:06 +0000682 return $this->_protect_identifiers($item, $first_word_only);
Derek Allard39b622d2008-01-16 21:10:09 +0000683 }
684
685 // --------------------------------------------------------------------
686
687 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000688 * "Smart" Escape String
689 *
690 * Escapes data based on type
691 * Sets boolean and null types
692 *
693 * @access public
694 * @param string
695 * @return integer
696 */
697 function escape($str)
698 {
699 switch (gettype($str))
700 {
701 case 'string' : $str = "'".$this->escape_str($str)."'";
702 break;
703 case 'boolean' : $str = ($str === FALSE) ? 0 : 1;
704 break;
705 default : $str = ($str === NULL) ? 'NULL' : $str;
706 break;
707 }
708
709 return $str;
710 }
711
712 // --------------------------------------------------------------------
713
714 /**
715 * Primary
716 *
717 * Retrieves the primary key. It assumes that the row in the first
718 * position is the primary key
719 *
720 * @access public
721 * @param string the table name
722 * @return string
723 */
724 function primary($table = '')
725 {
726 $fields = $this->list_fields($table);
727
Rick Ellis244b4c72008-05-12 18:21:33 +0000728 if ( ! is_array($fields))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000729 {
730 return FALSE;
731 }
732
733 return current($fields);
734 }
735
736 // --------------------------------------------------------------------
737
738 /**
739 * Returns an array of table names
740 *
741 * @access public
742 * @return array
743 */
Derek Allard39b622d2008-01-16 21:10:09 +0000744 function list_tables($constrain_by_prefix = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000745 {
746 // Is there a cached result?
747 if (isset($this->data_cache['table_names']))
748 {
749 return $this->data_cache['table_names'];
750 }
751
Derek Allard39b622d2008-01-16 21:10:09 +0000752 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000753 {
754 if ($this->db_debug)
755 {
756 return $this->display_error('db_unsupported_function');
757 }
Derek Allard993925b2008-08-21 12:43:31 +0000758 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000759 }
760
761 $retval = array();
762 $query = $this->query($sql);
763
764 if ($query->num_rows() > 0)
765 {
766 foreach($query->result_array() as $row)
767 {
768 if (isset($row['TABLE_NAME']))
769 {
770 $retval[] = $row['TABLE_NAME'];
771 }
772 else
773 {
774 $retval[] = array_shift($row);
775 }
776 }
777 }
778
779 $this->data_cache['table_names'] = $retval;
780 return $this->data_cache['table_names'];
781 }
782
783 // --------------------------------------------------------------------
784
785 /**
786 * Determine if a particular table exists
787 * @access public
788 * @return boolean
789 */
790 function table_exists($table_name)
791 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000792 return ( ! in_array($this->prep_tablename($table_name), $this->list_tables())) ? FALSE : TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000793 }
794
795 // --------------------------------------------------------------------
796
797 /**
798 * Fetch MySQL Field Names
799 *
800 * @access public
801 * @param string the table name
802 * @return array
803 */
804 function list_fields($table = '')
805 {
806 // Is there a cached result?
807 if (isset($this->data_cache['field_names'][$table]))
808 {
809 return $this->data_cache['field_names'][$table];
810 }
811
812 if ($table == '')
813 {
814 if ($this->db_debug)
815 {
816 return $this->display_error('db_field_param_missing');
817 }
Derek Allard993925b2008-08-21 12:43:31 +0000818 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000819 }
820
Derek Allard39b622d2008-01-16 21:10:09 +0000821 if (FALSE === ($sql = $this->_list_columns($this->prep_tablename($table))))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000822 {
823 if ($this->db_debug)
824 {
825 return $this->display_error('db_unsupported_function');
826 }
Derek Allard993925b2008-08-21 12:43:31 +0000827 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000828 }
829
830 $query = $this->query($sql);
831
832 $retval = array();
833 foreach($query->result_array() as $row)
834 {
835 if (isset($row['COLUMN_NAME']))
836 {
837 $retval[] = $row['COLUMN_NAME'];
838 }
839 else
840 {
841 $retval[] = current($row);
842 }
843 }
844
845 $this->data_cache['field_names'][$table] = $retval;
846 return $this->data_cache['field_names'][$table];
847 }
848
849 // --------------------------------------------------------------------
850
851 /**
852 * Determine if a particular field exists
853 * @access public
854 * @param string
855 * @param string
856 * @return boolean
857 */
858 function field_exists($field_name, $table_name)
859 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000860 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000861 }
862
863 // --------------------------------------------------------------------
864
865 /**
866 * DEPRECATED - use list_fields()
867 */
868 function field_names($table = '')
869 {
870 return $this->list_fields($table);
871 }
872
873 // --------------------------------------------------------------------
874
875 /**
876 * Returns an object with field data
877 *
878 * @access public
879 * @param string the table name
880 * @return object
881 */
882 function field_data($table = '')
883 {
884 if ($table == '')
885 {
886 if ($this->db_debug)
887 {
888 return $this->display_error('db_field_param_missing');
889 }
Derek Allard993925b2008-08-21 12:43:31 +0000890 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000891 }
892
Derek Allard39b622d2008-01-16 21:10:09 +0000893 $query = $this->query($this->_field_data($this->prep_tablename($table)));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000894 return $query->field_data();
895 }
896
897 // --------------------------------------------------------------------
898
899 /**
900 * Generate an insert string
901 *
902 * @access public
903 * @param string the table upon which the query will be performed
904 * @param array an associative array data of key/values
905 * @return string
906 */
907 function insert_string($table, $data)
908 {
Derek Allard993925b2008-08-21 12:43:31 +0000909 $fields = array();
Derek Allardd2df9bc2007-04-15 17:41:17 +0000910 $values = array();
911
912 foreach($data as $key => $val)
913 {
914 $fields[] = $key;
915 $values[] = $this->escape($val);
916 }
Derek Allard39b622d2008-01-16 21:10:09 +0000917
918
919 return $this->_insert($this->prep_tablename($table), $fields, $values);
920 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000921
922 // --------------------------------------------------------------------
923
924 /**
925 * Generate an update string
926 *
927 * @access public
928 * @param string the table upon which the query will be performed
929 * @param array an associative array data of key/values
930 * @param mixed the "where" statement
931 * @return string
932 */
933 function update_string($table, $data, $where)
934 {
935 if ($where == '')
Derek Allard513ce072008-05-18 12:23:11 +0000936 {
Derek Allardd2df9bc2007-04-15 17:41:17 +0000937 return false;
Derek Allard513ce072008-05-18 12:23:11 +0000938 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000939
940 $fields = array();
941 foreach($data as $key => $val)
942 {
943 $fields[$key] = $this->escape($val);
944 }
945
Rick Ellis244b4c72008-05-12 18:21:33 +0000946 if ( ! is_array($where))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000947 {
948 $dest = array($where);
949 }
950 else
951 {
952 $dest = array();
953 foreach ($where as $key => $val)
954 {
955 $prefix = (count($dest) == 0) ? '' : ' AND ';
956
Derek Allard39b622d2008-01-16 21:10:09 +0000957 if ($val !== '')
Derek Allardd2df9bc2007-04-15 17:41:17 +0000958 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000959 if ( ! $this->_has_operator($key))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000960 {
961 $key .= ' =';
962 }
963
964 $val = ' '.$this->escape($val);
965 }
966
967 $dest[] = $prefix.$key.$val;
968 }
969 }
970
Derek Allard39b622d2008-01-16 21:10:09 +0000971 return $this->_update($this->prep_tablename($table), $fields, $dest);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000972 }
973
974 // --------------------------------------------------------------------
975
976 /**
Derek Allard513ce072008-05-18 12:23:11 +0000977 * Tests whether the string has an SQL operator
978 *
979 * @access private
980 * @param string
981 * @return bool
982 */
983 function _has_operator($str)
984 {
985 $str = trim($str);
986 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
987 {
988 return FALSE;
989 }
990
991 return TRUE;
992 }
993
994 // --------------------------------------------------------------------
995
996 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000997 * Prep the table name - simply adds the table prefix if needed
998 *
999 * @access public
1000 * @param string the table name
1001 * @return string
1002 */
1003 function prep_tablename($table = '')
1004 {
1005 // Do we need to add the table prefix?
1006 if ($this->dbprefix != '')
1007 {
1008 if (substr($table, 0, strlen($this->dbprefix)) != $this->dbprefix)
1009 {
1010 $table = $this->dbprefix.$table;
1011 }
1012 }
1013
1014 return $table;
1015 }
1016
1017 // --------------------------------------------------------------------
1018
1019 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +00001020 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1021 *
1022 * @access public
1023 * @param string the function name
1024 * @param mixed any parameters needed by the function
1025 * @return mixed
1026 */
1027 function call_function($function)
1028 {
1029 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
1030
1031 if (FALSE === strpos($driver, $function))
1032 {
1033 $function = $driver.$function;
1034 }
1035
Rick Ellis244b4c72008-05-12 18:21:33 +00001036 if ( ! function_exists($function))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001037 {
1038 if ($this->db_debug)
1039 {
1040 return $this->display_error('db_unsupported_function');
1041 }
Derek Allard993925b2008-08-21 12:43:31 +00001042 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001043 }
1044 else
1045 {
1046 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1047
1048 return call_user_func_array($function, $args);
1049 }
1050 }
1051
1052 // --------------------------------------------------------------------
1053
1054 /**
1055 * Set Cache Directory Path
1056 *
1057 * @access public
1058 * @param string the path to the cache directory
1059 * @return void
1060 */
1061 function cache_set_path($path = '')
1062 {
1063 $this->cachedir = $path;
1064 }
1065
1066 // --------------------------------------------------------------------
1067
1068 /**
1069 * Enable Query Caching
1070 *
1071 * @access public
1072 * @return void
1073 */
1074 function cache_on()
1075 {
1076 $this->cache_on = TRUE;
1077 return TRUE;
1078 }
1079
1080 // --------------------------------------------------------------------
1081
1082 /**
1083 * Disable Query Caching
1084 *
1085 * @access public
1086 * @return void
1087 */
1088 function cache_off()
1089 {
1090 $this->cache_on = FALSE;
1091 return FALSE;
1092 }
1093
1094
1095 // --------------------------------------------------------------------
1096
1097 /**
1098 * Delete the cache files associated with a particular URI
1099 *
1100 * @access public
1101 * @return void
1102 */
1103 function cache_delete($segment_one = '', $segment_two = '')
1104 {
Rick Ellis244b4c72008-05-12 18:21:33 +00001105 if ( ! $this->_cache_init())
Derek Allardd2df9bc2007-04-15 17:41:17 +00001106 {
1107 return FALSE;
1108 }
1109 return $this->CACHE->delete($segment_one, $segment_two);
1110 }
1111
1112 // --------------------------------------------------------------------
1113
1114 /**
1115 * Delete All cache files
1116 *
1117 * @access public
1118 * @return void
1119 */
1120 function cache_delete_all()
1121 {
Rick Ellis244b4c72008-05-12 18:21:33 +00001122 if ( ! $this->_cache_init())
Derek Allardd2df9bc2007-04-15 17:41:17 +00001123 {
1124 return FALSE;
1125 }
1126
1127 return $this->CACHE->delete_all();
1128 }
1129
1130 // --------------------------------------------------------------------
1131
1132 /**
1133 * Initialize the Cache Class
1134 *
1135 * @access private
1136 * @return void
1137 */
1138 function _cache_init()
1139 {
1140 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1141 {
1142 return TRUE;
1143 }
1144
Rick Ellis244b4c72008-05-12 18:21:33 +00001145 if ( ! @include(BASEPATH.'database/DB_cache'.EXT))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001146 {
1147 return $this->cache_off();
1148 }
1149
Derek Jonesd36ade02008-05-12 15:17:41 +00001150 $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
Derek Allardd2df9bc2007-04-15 17:41:17 +00001151 return TRUE;
1152 }
1153
Derek Allardd2df9bc2007-04-15 17:41:17 +00001154 // --------------------------------------------------------------------
1155
1156 /**
1157 * Close DB Connection
1158 *
1159 * @access public
1160 * @return void
1161 */
1162 function close()
1163 {
Derek Jones05097752008-05-07 19:58:23 +00001164 if (is_resource($this->conn_id) OR is_object($this->conn_id))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001165 {
1166 $this->_close($this->conn_id);
1167 }
1168 $this->conn_id = FALSE;
1169 }
1170
1171 // --------------------------------------------------------------------
1172
1173 /**
1174 * Display an error message
1175 *
1176 * @access public
1177 * @param string the error message
1178 * @param string any "swap" values
1179 * @param boolean whether to localize the message
1180 * @return string sends the application/error_db.php template
1181 */
1182 function display_error($error = '', $swap = '', $native = FALSE)
1183 {
Derek Jones7f88aa52008-05-12 00:03:51 +00001184 global $LANG;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001185 $LANG->load('db');
1186
Derek Jones7f88aa52008-05-12 00:03:51 +00001187 $heading = $LANG->line('db_error_heading');
1188
Derek Allardd2df9bc2007-04-15 17:41:17 +00001189 if ($native == TRUE)
1190 {
1191 $message = $error;
1192 }
1193 else
1194 {
Derek Jones0b59f272008-05-13 04:22:33 +00001195 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001196 }
Derek Allardd2df9bc2007-04-15 17:41:17 +00001197
Derek Jones7f88aa52008-05-12 00:03:51 +00001198 $error =& load_class('Exceptions');
1199 echo $error->show_error($heading, $message, 'error_db');
Derek Allardd2df9bc2007-04-15 17:41:17 +00001200 exit;
1201 }
1202
1203}
1204
Derek Jones7f88aa52008-05-12 00:03:51 +00001205
1206/* End of file DB_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +00001207/* Location: ./system/database/DB_driver.php */