blob: 572595f42665f052674f9b8fc489629e48d98582 [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 Allard060052d2007-07-14 14:26:13 +0000378
Derek Allardd2df9bc2007-04-15 17:41:17 +0000379 if ($this->dbdriver == 'oci8')
380 {
381 $RES->stmt_id = $this->stmt_id;
382 $RES->curs_id = NULL;
383 $RES->limit_used = $this->limit_used;
Rick Ellisa2a240a2008-10-07 00:55:41 +0000384 $this->stmt_id = FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000385 }
Derek Allard39b622d2008-01-16 21:10:09 +0000386
Derek Jonesa8664292008-10-08 22:38:31 +0000387 // oci8 vars must be set before calling this
388 $RES->num_rows = $RES->num_rows();
389
Derek Allardd2df9bc2007-04-15 17:41:17 +0000390 // Is query caching enabled? If so, we'll serialize the
391 // result object and save it to a cache file.
392 if ($this->cache_on == TRUE AND $this->_cache_init())
393 {
394 // We'll create a new instance of the result object
395 // only without the platform specific driver since
396 // we can't use it with cached data (the query result
397 // resource ID won't be any good once we've cached the
398 // result object, so we'll have to compile the data
399 // and save it)
400 $CR = new CI_DB_result();
401 $CR->num_rows = $RES->num_rows();
402 $CR->result_object = $RES->result_object();
403 $CR->result_array = $RES->result_array();
404
405 // Reset these since cached objects can not utilize resource IDs.
406 $CR->conn_id = NULL;
407 $CR->result_id = NULL;
408
409 $this->CACHE->write($sql, $CR);
410 }
411
412 return $RES;
413 }
414
415 // --------------------------------------------------------------------
416
417 /**
418 * Load the result drivers
419 *
420 * @access public
421 * @return string the name of the result class
422 */
423 function load_rdriver()
424 {
425 $driver = 'CI_DB_'.$this->dbdriver.'_result';
426
Rick Ellis244b4c72008-05-12 18:21:33 +0000427 if ( ! class_exists($driver))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000428 {
429 include_once(BASEPATH.'database/DB_result'.EXT);
430 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result'.EXT);
431 }
432
433 return $driver;
434 }
435
436 // --------------------------------------------------------------------
437
438 /**
439 * Simple Query
440 * This is a simplified version of the query() function. Internally
441 * we only use it when running transaction commands since they do
442 * not require all the features of the main query() function.
443 *
444 * @access public
445 * @param string the sql query
446 * @return mixed
447 */
448 function simple_query($sql)
449 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000450 if ( ! $this->conn_id)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000451 {
452 $this->initialize();
453 }
454
455 return $this->_execute($sql);
456 }
457
458 // --------------------------------------------------------------------
459
460 /**
461 * Disable Transactions
462 * This permits transactions to be disabled at run-time.
463 *
464 * @access public
465 * @return void
466 */
467 function trans_off()
468 {
469 $this->trans_enabled = FALSE;
470 }
471
472 // --------------------------------------------------------------------
473
474 /**
Rick Ellis244b4c72008-05-12 18:21:33 +0000475 * Enable/disable Transaction Strict Mode
476 * When strict mode is enabled, if you are running multiple groups of
477 * transactions, if one group fails all groups will be rolled back.
478 * If strict mode is disabled, each group is treated autonomously, meaning
479 * a failure of one group will not affect any others
480 *
481 * @access public
482 * @return void
483 */
484 function trans_strict($mode = TRUE)
485 {
486 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
487 }
488
489 // --------------------------------------------------------------------
490
491 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000492 * Start Transaction
493 *
494 * @access public
495 * @return void
496 */
497 function trans_start($test_mode = FALSE)
498 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000499 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000500 {
501 return FALSE;
502 }
503
504 // When transactions are nested we only begin/commit/rollback the outermost ones
505 if ($this->_trans_depth > 0)
506 {
507 $this->_trans_depth += 1;
508 return;
509 }
510
511 $this->trans_begin($test_mode);
512 }
513
514 // --------------------------------------------------------------------
515
516 /**
517 * Complete Transaction
518 *
519 * @access public
520 * @return bool
521 */
522 function trans_complete()
523 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000524 if ( ! $this->trans_enabled)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000525 {
526 return FALSE;
527 }
528
529 // When transactions are nested we only begin/commit/rollback the outermost ones
530 if ($this->_trans_depth > 1)
531 {
532 $this->_trans_depth -= 1;
533 return TRUE;
534 }
535
Rick Ellis244b4c72008-05-12 18:21:33 +0000536 // The query() function will set this flag to FALSE in the event that a query failed
Rick Ellis28239ad2007-06-11 04:26:39 +0000537 if ($this->_trans_status === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000538 {
539 $this->trans_rollback();
540
Rick Ellis244b4c72008-05-12 18:21:33 +0000541 // If we are NOT running in strict mode, we will reset
542 // the _trans_status flag so that subsequent groups of transactions
543 // will be permitted.
544 if ($this->trans_strict === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000545 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000546 $this->_trans_status = TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000547 }
Rick Ellis244b4c72008-05-12 18:21:33 +0000548
Derek Allard993925b2008-08-21 12:43:31 +0000549 log_message('debug', 'DB Transaction Failure');
550 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000551 }
552
553 $this->trans_commit();
Derek Allard993925b2008-08-21 12:43:31 +0000554 return TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000555 }
556
557 // --------------------------------------------------------------------
558
559 /**
560 * Lets you retrieve the transaction flag to determine if it has failed
561 *
562 * @access public
563 * @return bool
564 */
565 function trans_status()
566 {
Rick Ellis28239ad2007-06-11 04:26:39 +0000567 return $this->_trans_status;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000568 }
569
570 // --------------------------------------------------------------------
571
572 /**
573 * Compile Bindings
574 *
575 * @access public
576 * @param string the sql statement
577 * @param array an array of bind data
578 * @return string
579 */
580 function compile_binds($sql, $binds)
Derek Allardc0743382008-02-11 05:54:44 +0000581 {
582 if (strpos($sql, $this->bind_marker) === FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000583 {
584 return $sql;
585 }
586
Rick Ellis244b4c72008-05-12 18:21:33 +0000587 if ( ! is_array($binds))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000588 {
589 $binds = array($binds);
590 }
591
Derek Allardc0743382008-02-11 05:54:44 +0000592 // Get the sql segments around the bind markers
593 $segments = explode($this->bind_marker, $sql);
594
595 // The count of bind should be 1 less then the count of segments
596 // If there are more bind arguments trim it down
597 if (count($binds) >= count($segments)) {
598 $binds = array_slice($binds, 0, count($segments)-1);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000599 }
600
Derek Allardc0743382008-02-11 05:54:44 +0000601 // Construct the binded query
602 $result = $segments[0];
603 $i = 0;
604 foreach ($binds as $bind)
605 {
606 $result .= $this->escape($bind);
607 $result .= $segments[++$i];
608 }
609
610 return $result;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000611 }
612
613 // --------------------------------------------------------------------
614
615 /**
616 * Determines if a query is a "write" type.
617 *
618 * @access public
619 * @param string An SQL query string
620 * @return boolean
621 */
622 function is_write_type($sql)
623 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000624 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 +0000625 {
626 return FALSE;
627 }
628 return TRUE;
629 }
630
631 // --------------------------------------------------------------------
632
633 /**
634 * Calculate the aggregate query elapsed time
635 *
636 * @access public
637 * @param integer The number of decimal places
638 * @return integer
639 */
640 function elapsed_time($decimals = 6)
641 {
642 return number_format($this->benchmark, $decimals);
643 }
644
645 // --------------------------------------------------------------------
646
647 /**
648 * Returns the total number of queries
649 *
650 * @access public
651 * @return integer
652 */
653 function total_queries()
654 {
655 return $this->query_count;
656 }
657
658 // --------------------------------------------------------------------
659
660 /**
661 * Returns the last query that was executed
662 *
663 * @access public
664 * @return void
665 */
666 function last_query()
667 {
668 return end($this->queries);
669 }
670
671 // --------------------------------------------------------------------
672
673 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000674 * Protect Identifiers
675 *
676 * This function adds backticks if appropriate based on db type
677 *
678 * @access private
679 * @param mixed the item to escape
680 * @param boolean only affect the first word
681 * @return mixed the item with backticks
682 */
683 function protect_identifiers($item, $first_word_only = FALSE)
684 {
Derek Allard1a704ce2008-01-27 15:03:06 +0000685 return $this->_protect_identifiers($item, $first_word_only);
Derek Allard39b622d2008-01-16 21:10:09 +0000686 }
687
688 // --------------------------------------------------------------------
689
690 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000691 * "Smart" Escape String
692 *
693 * Escapes data based on type
694 * Sets boolean and null types
695 *
696 * @access public
697 * @param string
698 * @return integer
699 */
700 function escape($str)
701 {
702 switch (gettype($str))
703 {
704 case 'string' : $str = "'".$this->escape_str($str)."'";
705 break;
706 case 'boolean' : $str = ($str === FALSE) ? 0 : 1;
707 break;
708 default : $str = ($str === NULL) ? 'NULL' : $str;
709 break;
710 }
711
712 return $str;
713 }
714
715 // --------------------------------------------------------------------
716
717 /**
718 * Primary
719 *
720 * Retrieves the primary key. It assumes that the row in the first
721 * position is the primary key
722 *
723 * @access public
724 * @param string the table name
725 * @return string
726 */
727 function primary($table = '')
728 {
729 $fields = $this->list_fields($table);
730
Rick Ellis244b4c72008-05-12 18:21:33 +0000731 if ( ! is_array($fields))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000732 {
733 return FALSE;
734 }
735
736 return current($fields);
737 }
738
739 // --------------------------------------------------------------------
740
741 /**
742 * Returns an array of table names
743 *
744 * @access public
745 * @return array
746 */
Derek Allard39b622d2008-01-16 21:10:09 +0000747 function list_tables($constrain_by_prefix = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000748 {
749 // Is there a cached result?
750 if (isset($this->data_cache['table_names']))
751 {
752 return $this->data_cache['table_names'];
753 }
754
Derek Allard39b622d2008-01-16 21:10:09 +0000755 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000756 {
757 if ($this->db_debug)
758 {
759 return $this->display_error('db_unsupported_function');
760 }
Derek Allard993925b2008-08-21 12:43:31 +0000761 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000762 }
763
764 $retval = array();
765 $query = $this->query($sql);
766
767 if ($query->num_rows() > 0)
768 {
769 foreach($query->result_array() as $row)
770 {
771 if (isset($row['TABLE_NAME']))
772 {
773 $retval[] = $row['TABLE_NAME'];
774 }
775 else
776 {
777 $retval[] = array_shift($row);
778 }
779 }
780 }
781
782 $this->data_cache['table_names'] = $retval;
783 return $this->data_cache['table_names'];
784 }
785
786 // --------------------------------------------------------------------
787
788 /**
789 * Determine if a particular table exists
790 * @access public
791 * @return boolean
792 */
793 function table_exists($table_name)
794 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000795 return ( ! in_array($this->prep_tablename($table_name), $this->list_tables())) ? FALSE : TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000796 }
797
798 // --------------------------------------------------------------------
799
800 /**
801 * Fetch MySQL Field Names
802 *
803 * @access public
804 * @param string the table name
805 * @return array
806 */
807 function list_fields($table = '')
808 {
809 // Is there a cached result?
810 if (isset($this->data_cache['field_names'][$table]))
811 {
812 return $this->data_cache['field_names'][$table];
813 }
814
815 if ($table == '')
816 {
817 if ($this->db_debug)
818 {
819 return $this->display_error('db_field_param_missing');
820 }
Derek Allard993925b2008-08-21 12:43:31 +0000821 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000822 }
823
Derek Allard39b622d2008-01-16 21:10:09 +0000824 if (FALSE === ($sql = $this->_list_columns($this->prep_tablename($table))))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000825 {
826 if ($this->db_debug)
827 {
828 return $this->display_error('db_unsupported_function');
829 }
Derek Allard993925b2008-08-21 12:43:31 +0000830 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000831 }
832
833 $query = $this->query($sql);
834
835 $retval = array();
836 foreach($query->result_array() as $row)
837 {
838 if (isset($row['COLUMN_NAME']))
839 {
840 $retval[] = $row['COLUMN_NAME'];
841 }
842 else
843 {
844 $retval[] = current($row);
845 }
846 }
847
848 $this->data_cache['field_names'][$table] = $retval;
849 return $this->data_cache['field_names'][$table];
850 }
851
852 // --------------------------------------------------------------------
853
854 /**
855 * Determine if a particular field exists
856 * @access public
857 * @param string
858 * @param string
859 * @return boolean
860 */
861 function field_exists($field_name, $table_name)
862 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000863 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000864 }
865
866 // --------------------------------------------------------------------
867
868 /**
869 * DEPRECATED - use list_fields()
870 */
871 function field_names($table = '')
872 {
873 return $this->list_fields($table);
874 }
875
876 // --------------------------------------------------------------------
877
878 /**
879 * Returns an object with field data
880 *
881 * @access public
882 * @param string the table name
883 * @return object
884 */
885 function field_data($table = '')
886 {
887 if ($table == '')
888 {
889 if ($this->db_debug)
890 {
891 return $this->display_error('db_field_param_missing');
892 }
Derek Allard993925b2008-08-21 12:43:31 +0000893 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000894 }
895
Derek Allard39b622d2008-01-16 21:10:09 +0000896 $query = $this->query($this->_field_data($this->prep_tablename($table)));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000897 return $query->field_data();
898 }
899
900 // --------------------------------------------------------------------
901
902 /**
903 * Generate an insert string
904 *
905 * @access public
906 * @param string the table upon which the query will be performed
907 * @param array an associative array data of key/values
908 * @return string
909 */
910 function insert_string($table, $data)
911 {
Derek Allard993925b2008-08-21 12:43:31 +0000912 $fields = array();
Derek Allardd2df9bc2007-04-15 17:41:17 +0000913 $values = array();
914
915 foreach($data as $key => $val)
916 {
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000917 $fields[] = $this->_escape_column($key);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000918 $values[] = $this->escape($val);
919 }
Derek Allard39b622d2008-01-16 21:10:09 +0000920
Derek Allard39b622d2008-01-16 21:10:09 +0000921 return $this->_insert($this->prep_tablename($table), $fields, $values);
922 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000923
924 // --------------------------------------------------------------------
925
926 /**
927 * Generate an update string
928 *
929 * @access public
930 * @param string the table upon which the query will be performed
931 * @param array an associative array data of key/values
932 * @param mixed the "where" statement
933 * @return string
934 */
935 function update_string($table, $data, $where)
936 {
937 if ($where == '')
Derek Allard513ce072008-05-18 12:23:11 +0000938 {
Derek Allardd2df9bc2007-04-15 17:41:17 +0000939 return false;
Derek Allard513ce072008-05-18 12:23:11 +0000940 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000941
942 $fields = array();
943 foreach($data as $key => $val)
944 {
Rick Ellis52dc8ca2008-09-30 19:53:52 +0000945 $fields[$this->_escape_column($key)] = $this->escape($val);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000946 }
947
Rick Ellis244b4c72008-05-12 18:21:33 +0000948 if ( ! is_array($where))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000949 {
950 $dest = array($where);
951 }
952 else
953 {
954 $dest = array();
955 foreach ($where as $key => $val)
956 {
957 $prefix = (count($dest) == 0) ? '' : ' AND ';
958
Derek Allard39b622d2008-01-16 21:10:09 +0000959 if ($val !== '')
Derek Allardd2df9bc2007-04-15 17:41:17 +0000960 {
Rick Ellis244b4c72008-05-12 18:21:33 +0000961 if ( ! $this->_has_operator($key))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000962 {
963 $key .= ' =';
964 }
965
966 $val = ' '.$this->escape($val);
967 }
968
969 $dest[] = $prefix.$key.$val;
970 }
971 }
972
Derek Allard39b622d2008-01-16 21:10:09 +0000973 return $this->_update($this->prep_tablename($table), $fields, $dest);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000974 }
975
976 // --------------------------------------------------------------------
977
978 /**
Derek Allard513ce072008-05-18 12:23:11 +0000979 * Tests whether the string has an SQL operator
980 *
981 * @access private
982 * @param string
983 * @return bool
984 */
985 function _has_operator($str)
986 {
987 $str = trim($str);
988 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
989 {
990 return FALSE;
991 }
992
993 return TRUE;
994 }
995
996 // --------------------------------------------------------------------
997
998 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000999 * Prep the table name - simply adds the table prefix if needed
1000 *
1001 * @access public
1002 * @param string the table name
1003 * @return string
1004 */
1005 function prep_tablename($table = '')
1006 {
1007 // Do we need to add the table prefix?
1008 if ($this->dbprefix != '')
1009 {
1010 if (substr($table, 0, strlen($this->dbprefix)) != $this->dbprefix)
1011 {
1012 $table = $this->dbprefix.$table;
1013 }
1014 }
1015
1016 return $table;
1017 }
1018
1019 // --------------------------------------------------------------------
1020
1021 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +00001022 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1023 *
1024 * @access public
1025 * @param string the function name
1026 * @param mixed any parameters needed by the function
1027 * @return mixed
1028 */
1029 function call_function($function)
1030 {
1031 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
1032
1033 if (FALSE === strpos($driver, $function))
1034 {
1035 $function = $driver.$function;
1036 }
1037
Rick Ellis244b4c72008-05-12 18:21:33 +00001038 if ( ! function_exists($function))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001039 {
1040 if ($this->db_debug)
1041 {
1042 return $this->display_error('db_unsupported_function');
1043 }
Derek Allard993925b2008-08-21 12:43:31 +00001044 return FALSE;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001045 }
1046 else
1047 {
1048 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1049
1050 return call_user_func_array($function, $args);
1051 }
1052 }
1053
1054 // --------------------------------------------------------------------
1055
1056 /**
1057 * Set Cache Directory Path
1058 *
1059 * @access public
1060 * @param string the path to the cache directory
1061 * @return void
1062 */
1063 function cache_set_path($path = '')
1064 {
1065 $this->cachedir = $path;
1066 }
1067
1068 // --------------------------------------------------------------------
1069
1070 /**
1071 * Enable Query Caching
1072 *
1073 * @access public
1074 * @return void
1075 */
1076 function cache_on()
1077 {
1078 $this->cache_on = TRUE;
1079 return TRUE;
1080 }
1081
1082 // --------------------------------------------------------------------
1083
1084 /**
1085 * Disable Query Caching
1086 *
1087 * @access public
1088 * @return void
1089 */
1090 function cache_off()
1091 {
1092 $this->cache_on = FALSE;
1093 return FALSE;
1094 }
1095
1096
1097 // --------------------------------------------------------------------
1098
1099 /**
1100 * Delete the cache files associated with a particular URI
1101 *
1102 * @access public
1103 * @return void
1104 */
1105 function cache_delete($segment_one = '', $segment_two = '')
1106 {
Rick Ellis244b4c72008-05-12 18:21:33 +00001107 if ( ! $this->_cache_init())
Derek Allardd2df9bc2007-04-15 17:41:17 +00001108 {
1109 return FALSE;
1110 }
1111 return $this->CACHE->delete($segment_one, $segment_two);
1112 }
1113
1114 // --------------------------------------------------------------------
1115
1116 /**
1117 * Delete All cache files
1118 *
1119 * @access public
1120 * @return void
1121 */
1122 function cache_delete_all()
1123 {
Rick Ellis244b4c72008-05-12 18:21:33 +00001124 if ( ! $this->_cache_init())
Derek Allardd2df9bc2007-04-15 17:41:17 +00001125 {
1126 return FALSE;
1127 }
1128
1129 return $this->CACHE->delete_all();
1130 }
1131
1132 // --------------------------------------------------------------------
1133
1134 /**
1135 * Initialize the Cache Class
1136 *
1137 * @access private
1138 * @return void
1139 */
1140 function _cache_init()
1141 {
1142 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1143 {
1144 return TRUE;
1145 }
1146
Rick Ellis244b4c72008-05-12 18:21:33 +00001147 if ( ! @include(BASEPATH.'database/DB_cache'.EXT))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001148 {
1149 return $this->cache_off();
1150 }
1151
Derek Jonesd36ade02008-05-12 15:17:41 +00001152 $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 +00001153 return TRUE;
1154 }
1155
Derek Allardd2df9bc2007-04-15 17:41:17 +00001156 // --------------------------------------------------------------------
1157
1158 /**
1159 * Close DB Connection
1160 *
1161 * @access public
1162 * @return void
1163 */
1164 function close()
1165 {
Derek Jones05097752008-05-07 19:58:23 +00001166 if (is_resource($this->conn_id) OR is_object($this->conn_id))
Derek Allardd2df9bc2007-04-15 17:41:17 +00001167 {
1168 $this->_close($this->conn_id);
1169 }
1170 $this->conn_id = FALSE;
1171 }
1172
1173 // --------------------------------------------------------------------
1174
1175 /**
1176 * Display an error message
1177 *
1178 * @access public
1179 * @param string the error message
1180 * @param string any "swap" values
1181 * @param boolean whether to localize the message
1182 * @return string sends the application/error_db.php template
1183 */
1184 function display_error($error = '', $swap = '', $native = FALSE)
1185 {
Derek Jones7f88aa52008-05-12 00:03:51 +00001186 global $LANG;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001187 $LANG->load('db');
1188
Derek Jones7f88aa52008-05-12 00:03:51 +00001189 $heading = $LANG->line('db_error_heading');
1190
Derek Allardd2df9bc2007-04-15 17:41:17 +00001191 if ($native == TRUE)
1192 {
1193 $message = $error;
1194 }
1195 else
1196 {
Derek Jones0b59f272008-05-13 04:22:33 +00001197 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
Derek Allardd2df9bc2007-04-15 17:41:17 +00001198 }
Derek Allardd2df9bc2007-04-15 17:41:17 +00001199
Derek Jones7f88aa52008-05-12 00:03:51 +00001200 $error =& load_class('Exceptions');
1201 echo $error->show_error($heading, $message, 'error_db');
Derek Allardd2df9bc2007-04-15 17:41:17 +00001202 exit;
1203 }
1204
1205}
1206
Derek Jones7f88aa52008-05-12 00:03:51 +00001207
1208/* End of file DB_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +00001209/* Location: ./system/database/DB_driver.php */