PluginProbe
ShiftController Employee Shift Scheduling / 2.2.5
ShiftController Employee Shift Scheduling v2.2.5
4.9.97 4.9.96 4.9.95 4.9.74 4.9.75 4.9.76 4.9.77 4.9.78 4.9.84 4.9.85 4.9.87 4.9.91 4.9.92 trunk 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 3.2.4 All 38 releases
shiftcontroller / happ / system / database / DB_driver.php

DB_driver.php in ShiftController Employee Shift Scheduling 2.2.5, at happ/system/database/DB_driver.php

1,410 lines 31.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2 /**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
10 * @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 */
31 class 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
74
75 /**
76 * Constructor. Accepts one parameter containing the database
77 * connection settings.
78 *
79 * @param array
80 */
81 function __construct($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 }
93
94 // --------------------------------------------------------------------
95
96 /**
97 * Initialize Database Settings
98 *
99 * @access private Called by the constructor
100 * @param mixed
101 * @return void
102 */
103 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 }
111
112 // ----------------------------------------------------------------
113
114 // Connect to the database and set the connection ID
115 $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
116
117 // No connection resource? Throw an error
118 if ( ! $this->conn_id)
119 {
120 log_message('error', 'Unable to connect to the database');
121
122 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);
137
138 if ($this->db_debug)
139 {
140 $this->display_error('db_unable_to_select', $this->database);
141 }
142 return FALSE;
143 }
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 }
151
152 return TRUE;
153 }
154 }
155
156 return TRUE;
157 }
158
159 // --------------------------------------------------------------------
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);
174
175 if ($this->db_debug)
176 {
177 $this->display_error('db_unable_to_set_charset', $this->char_set);
178 }
179
180 return FALSE;
181 }
182
183 return TRUE;
184 }
185
186 // --------------------------------------------------------------------
187
188 /**
189 * The name of the platform in use (mysql, mssql, etc...)
190 *
191 * @access public
192 * @return string
193 */
194 function platform()
195 {
196 return $this->dbdriver;
197 }
198
199 // --------------------------------------------------------------------
200
201 /**
202 * Database Version Number. Returns a string containing the
203 * version of the database being used
204 *
205 * @access public
206 * @return string
207 */
208 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 }
218
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.
221 $driver_version_exceptions = array('oci8', 'sqlite', 'cubrid');
222
223 if (in_array($this->dbdriver, $driver_version_exceptions))
224 {
225 return $sql;
226 }
227 else
228 {
229 $query = $this->query($sql);
230 return $query->row('ver');
231 }
232 }
233
234 // --------------------------------------------------------------------
235
236 /**
237 * Execute the query
238 *
239 * Accepts an SQL string as input and returns a result object upon
240 * successful execution of a "read" type query. Returns boolean TRUE
241 * 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
248 * @return mixed
249 */
250 function query($sql, $binds = FALSE, $return_object = TRUE)
251 {
252 if ($sql == '')
253 {
254 if ($this->db_debug)
255 {
256 log_message('error', 'Invalid query: '.$sql);
257 return $this->display_error('db_invalid_query');
258 }
259 return FALSE;
260 }
261
262 // Verify table prefix and replace if necessary
263 if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) )
264 {
265 $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql);
266 }
267
268 // Compile binds if needed
269 if ($binds !== FALSE)
270 {
271 $sql = $this->compile_binds($sql, $binds);
272 }
273
274 // Is query caching enabled? If the query is a "read type"
275 // we will load the caching class and return the previously
276 // cached query if it exists
277 if ($this->cache_on == TRUE AND stristr($sql, 'SELECT'))
278 {
279 if ($this->_cache_init())
280 {
281 $this->load_rdriver();
282 if (FALSE !== ($cache = $this->CACHE->read($sql)))
283 {
284 return $cache;
285 }
286 }
287 }
288
289 // Save the query for debugging
290 if ($this->save_queries == TRUE)
291 {
292 $this->queries[] = $sql;
293 }
294
295 // Start the Query Timer
296 $time_start = list($sm, $ss) = explode(' ', microtime());
297
298 // Run the Query
299 if (FALSE === ($this->result_id = $this->simple_query($sql)))
300 {
301 if ($this->save_queries == TRUE)
302 {
303 $this->query_times[] = 0;
304 }
305
306 // This will trigger a rollback if transactions are being used
307 $this->_trans_status = FALSE;
308
309 if ($this->db_debug)
310 {
311 // grab the error number and message now, as we might run some
312 // additional queries before displaying the error
313 $error_no = $this->_error_number();
314 $error_msg = $this->_error_message();
315
316 // We call this function in order to roll-back queries
317 // if transactions are enabled. If we don't call this here
318 // the error message will trigger an exit, causing the
319 // transactions to remain in limbo.
320 $this->trans_complete();
321
322 // Log and display errors
323 log_message('error', 'Query error: '.$error_msg);
324 return $this->display_error(
325 array(
326 'Error Number: '.$error_no,
327 $error_msg,
328 $sql
329 )
330 );
331 }
332
333 return FALSE;
334 }
335
336 // Stop and aggregate the query time results
337 $time_end = list($em, $es) = explode(' ', microtime());
338 $this->benchmark += ($em + $es) - ($sm + $ss);
339
340 if ($this->save_queries == TRUE)
341 {
342 $this->query_times[] = ($em + $es) - ($sm + $ss);
343 }
344
345 // Increment the query counter
346 $this->query_count++;
347
348 // Was the query a "write" type?
349 // If so we'll simply return true
350 if ($this->is_write_type($sql) === TRUE)
351 {
352 // If caching is enabled we'll auto-cleanup any
353 // existing files related to this particular URI
354 if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init())
355 {
356 $this->CACHE->delete();
357 }
358
359 return TRUE;
360 }
361
362 // Return TRUE if we don't need to create a result object
363 // Currently only the Oracle driver uses this when stored
364 // procedures are used
365 if ($return_object !== TRUE)
366 {
367 return TRUE;
368 }
369
370 // Load and instantiate the result driver
371
372 $driver = $this->load_rdriver();
373 $RES = new $driver();
374 $RES->conn_id = $this->conn_id;
375 $RES->result_id = $this->result_id;
376
377 if ($this->dbdriver == 'oci8')
378 {
379 $RES->stmt_id = $this->stmt_id;
380 $RES->curs_id = NULL;
381 $RES->limit_used = $this->limit_used;
382 $this->stmt_id = FALSE;
383 }
384
385 // oci8 vars must be set before calling this
386 $RES->num_rows = $RES->num_rows();
387
388 // Is query caching enabled? If so, we'll serialize the
389 // result object and save it to a cache file.
390 if ($this->cache_on == TRUE AND $this->_cache_init())
391 {
392 // We'll create a new instance of the result object
393 // only without the platform specific driver since
394 // we can't use it with cached data (the query result
395 // resource ID won't be any good once we've cached the
396 // result object, so we'll have to compile the data
397 // and save it)
398 $CR = new CI_DB_result();
399 $CR->num_rows = $RES->num_rows();
400 $CR->result_object = $RES->result_object();
401 $CR->result_array = $RES->result_array();
402
403 // Reset these since cached objects can not utilize resource IDs.
404 $CR->conn_id = NULL;
405 $CR->result_id = NULL;
406
407 $this->CACHE->write($sql, $CR);
408 }
409
410 return $RES;
411 }
412
413 // --------------------------------------------------------------------
414
415 /**
416 * Load the result drivers
417 *
418 * @access public
419 * @return string the name of the result class
420 */
421 function load_rdriver()
422 {
423 $driver = 'CI_DB_'.$this->dbdriver.'_result';
424
425 if ( ! class_exists($driver))
426 {
427 include_once(BASEPATH.'database/DB_result.php');
428 include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
429 }
430
431 return $driver;
432 }
433
434 // --------------------------------------------------------------------
435
436 /**
437 * Simple Query
438 * This is a simplified version of the query() function. Internally
439 * we only use it when running transaction commands since they do
440 * not require all the features of the main query() function.
441 *
442 * @access public
443 * @param string the sql query
444 * @return mixed
445 */
446 function simple_query($sql)
447 {
448 if ( ! $this->conn_id)
449 {
450 $this->initialize();
451 }
452
453 return $this->_execute($sql);
454 }
455
456 // --------------------------------------------------------------------
457
458 /**
459 * Disable Transactions
460 * This permits transactions to be disabled at run-time.
461 *
462 * @access public
463 * @return void
464 */
465 function trans_off()
466 {
467 $this->trans_enabled = FALSE;
468 }
469
470 // --------------------------------------------------------------------
471
472 /**
473 * Enable/disable Transaction Strict Mode
474 * When strict mode is enabled, if you are running multiple groups of
475 * transactions, if one group fails all groups will be rolled back.
476 * If strict mode is disabled, each group is treated autonomously, meaning
477 * a failure of one group will not affect any others
478 *
479 * @access public
480 * @return void
481 */
482 function trans_strict($mode = TRUE)
483 {
484 $this->trans_strict = is_bool($mode) ? $mode : TRUE;
485 }
486
487 // --------------------------------------------------------------------
488
489 /**
490 * Start Transaction
491 *
492 * @access public
493 * @return void
494 */
495 function trans_start($test_mode = FALSE)
496 {
497 if ( ! $this->trans_enabled)
498 {
499 return FALSE;
500 }
501
502 // When transactions are nested we only begin/commit/rollback the outermost ones
503 if ($this->_trans_depth > 0)
504 {
505 $this->_trans_depth += 1;
506 return;
507 }
508
509 $this->trans_begin($test_mode);
510 }
511
512 // --------------------------------------------------------------------
513
514 /**
515 * Complete Transaction
516 *
517 * @access public
518 * @return bool
519 */
520 function trans_complete()
521 {
522 if ( ! $this->trans_enabled)
523 {
524 return FALSE;
525 }
526
527 // When transactions are nested we only begin/commit/rollback the outermost ones
528 if ($this->_trans_depth > 1)
529 {
530 $this->_trans_depth -= 1;
531 return TRUE;
532 }
533
534 // The query() function will set this flag to FALSE in the event that a query failed
535 if ($this->_trans_status === FALSE)
536 {
537 $this->trans_rollback();
538
539 // If we are NOT running in strict mode, we will reset
540 // the _trans_status flag so that subsequent groups of transactions
541 // will be permitted.
542 if ($this->trans_strict === FALSE)
543 {
544 $this->_trans_status = TRUE;
545 }
546
547 log_message('debug', 'DB Transaction Failure');
548 return FALSE;
549 }
550
551 $this->trans_commit();
552 return TRUE;
553 }
554
555 // --------------------------------------------------------------------
556
557 /**
558 * Lets you retrieve the transaction flag to determine if it has failed
559 *
560 * @access public
561 * @return bool
562 */
563 function trans_status()
564 {
565 return $this->_trans_status;
566 }
567
568 // --------------------------------------------------------------------
569
570 /**
571 * Compile Bindings
572 *
573 * @access public
574 * @param string the sql statement
575 * @param array an array of bind data
576 * @return string
577 */
578 function compile_binds($sql, $binds)
579 {
580 if (strpos($sql, $this->bind_marker) === FALSE)
581 {
582 return $sql;
583 }
584
585 if ( ! is_array($binds))
586 {
587 $binds = array($binds);
588 }
589
590 // Get the sql segments around the bind markers
591 $segments = explode($this->bind_marker, $sql);
592
593 // The count of bind should be 1 less then the count of segments
594 // If there are more bind arguments trim it down
595 if (count($binds) >= count($segments)) {
596 $binds = array_slice($binds, 0, count($segments)-1);
597 }
598
599 // Construct the binded query
600 $result = $segments[0];
601 $i = 0;
602 foreach ($binds as $bind)
603 {
604 $result .= $this->escape($bind);
605 $result .= $segments[++$i];
606 }
607
608 return $result;
609 }
610
611 // --------------------------------------------------------------------
612
613 /**
614 * Determines if a query is a "write" type.
615 *
616 * @access public
617 * @param string An SQL query string
618 * @return boolean
619 */
620 function is_write_type($sql)
621 {
622 if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
623 {
624 return FALSE;
625 }
626 return TRUE;
627 }
628
629 // --------------------------------------------------------------------
630
631 /**
632 * Calculate the aggregate query elapsed time
633 *
634 * @access public
635 * @param integer The number of decimal places
636 * @return integer
637 */
638 function elapsed_time($decimals = 6)
639 {
640 return number_format($this->benchmark, $decimals);
641 }
642
643 // --------------------------------------------------------------------
644
645 /**
646 * Returns the total number of queries
647 *
648 * @access public
649 * @return integer
650 */
651 function total_queries()
652 {
653 return $this->query_count;
654 }
655
656 // --------------------------------------------------------------------
657
658 /**
659 * Returns the last query that was executed
660 *
661 * @access public
662 * @return void
663 */
664 function last_query()
665 {
666 return end($this->queries);
667 }
668
669 // --------------------------------------------------------------------
670
671 /**
672 * "Smart" Escape String
673 *
674 * Escapes data based on type
675 * Sets boolean and null types
676 *
677 * @access public
678 * @param string
679 * @return mixed
680 */
681 function escape($str)
682 {
683 if (is_string($str))
684 {
685 $str = "'".$this->escape_str($str)."'";
686 }
687 elseif (is_bool($str))
688 {
689 $str = ($str === FALSE) ? 0 : 1;
690 }
691 elseif (is_null($str))
692 {
693 $str = 'NULL';
694 }
695
696 return $str;
697 }
698
699 // --------------------------------------------------------------------
700
701 /**
702 * Escape LIKE String
703 *
704 * Calls the individual driver for platform
705 * specific escaping for LIKE conditions
706 *
707 * @access public
708 * @param string
709 * @return mixed
710 */
711 function escape_like_str($str)
712 {
713 return $this->escape_str($str, TRUE);
714 }
715
716 // --------------------------------------------------------------------
717
718 /**
719 * Primary
720 *
721 * Retrieves the primary key. It assumes that the row in the first
722 * position is the primary key
723 *
724 * @access public
725 * @param string the table name
726 * @return string
727 */
728 function primary($table = '')
729 {
730 $fields = $this->list_fields($table);
731
732 if ( ! is_array($fields))
733 {
734 return FALSE;
735 }
736
737 return current($fields);
738 }
739
740 // --------------------------------------------------------------------
741
742 /**
743 * Returns an array of table names
744 *
745 * @access public
746 * @return array
747 */
748 function list_tables($constrain_by_prefix = FALSE)
749 {
750 // Is there a cached result?
751 if (isset($this->data_cache['table_names']))
752 {
753 return $this->data_cache['table_names'];
754 }
755
756 if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
757 {
758 if ($this->db_debug)
759 {
760 return $this->display_error('db_unsupported_function');
761 }
762 return FALSE;
763 }
764
765 $retval = array();
766 $query = $this->query($sql);
767
768 if ($query->num_rows() > 0)
769 {
770 foreach ($query->result_array() as $row)
771 {
772 if (isset($row['TABLE_NAME']))
773 {
774 $retval[] = $row['TABLE_NAME'];
775 }
776 else
777 {
778 $retval[] = array_shift($row);
779 }
780 }
781 }
782
783 $this->data_cache['table_names'] = $retval;
784 return $this->data_cache['table_names'];
785 }
786
787 // --------------------------------------------------------------------
788
789 /**
790 * Determine if a particular table exists
791 * @access public
792 * @return boolean
793 */
794 function table_exists($table_name)
795 {
796 return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE;
797 }
798
799 // --------------------------------------------------------------------
800
801 /**
802 * Fetch MySQL Field Names
803 *
804 * @access public
805 * @param string the table name
806 * @return array
807 */
808 function list_fields($table = '')
809 {
810 // Is there a cached result?
811 if (isset($this->data_cache['field_names'][$table]))
812 {
813 return $this->data_cache['field_names'][$table];
814 }
815
816 if ($table == '')
817 {
818 if ($this->db_debug)
819 {
820 return $this->display_error('db_field_param_missing');
821 }
822 return FALSE;
823 }
824
825 if (FALSE === ($sql = $this->_list_columns($table)))
826 {
827 if ($this->db_debug)
828 {
829 return $this->display_error('db_unsupported_function');
830 }
831 return FALSE;
832 }
833
834 $query = $this->query($sql);
835
836 $retval = array();
837 foreach ($query->result_array() as $row)
838 {
839 if (isset($row['COLUMN_NAME']))
840 {
841 $retval[] = $row['COLUMN_NAME'];
842 }
843 else
844 {
845 $retval[] = current($row);
846 }
847 }
848
849 $this->data_cache['field_names'][$table] = $retval;
850 return $this->data_cache['field_names'][$table];
851 }
852
853 // --------------------------------------------------------------------
854
855 /**
856 * Determine if a particular field exists
857 * @access public
858 * @param string
859 * @param string
860 * @return boolean
861 */
862 function field_exists($field_name, $table_name)
863 {
864 return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE;
865 }
866
867 // --------------------------------------------------------------------
868
869 /**
870 * Returns an object with field data
871 *
872 * @access public
873 * @param string the table name
874 * @return object
875 */
876 function field_data($table = '')
877 {
878 if ($table == '')
879 {
880 if ($this->db_debug)
881 {
882 return $this->display_error('db_field_param_missing');
883 }
884 return FALSE;
885 }
886
887 $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE)));
888
889 return $query->field_data();
890 }
891
892 // --------------------------------------------------------------------
893
894 /**
895 * Generate an insert string
896 *
897 * @access public
898 * @param string the table upon which the query will be performed
899 * @param array an associative array data of key/values
900 * @return string
901 */
902 function insert_string($table, $data)
903 {
904 $fields = array();
905 $values = array();
906
907 foreach ($data as $key => $val)
908 {
909 $fields[] = $this->_escape_identifiers($key);
910 $values[] = $this->escape($val);
911 }
912
913 return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
914 }
915
916 // --------------------------------------------------------------------
917
918 /**
919 * Generate an update string
920 *
921 * @access public
922 * @param string the table upon which the query will be performed
923 * @param array an associative array data of key/values
924 * @param mixed the "where" statement
925 * @return string
926 */
927 function update_string($table, $data, $where)
928 {
929 if ($where == '')
930 {
931 return false;
932 }
933
934 $fields = array();
935 foreach ($data as $key => $val)
936 {
937 $fields[$this->_protect_identifiers($key)] = $this->escape($val);
938 }
939
940 if ( ! is_array($where))
941 {
942 $dest = array($where);
943 }
944 else
945 {
946 $dest = array();
947 foreach ($where as $key => $val)
948 {
949 $prefix = (count($dest) == 0) ? '' : ' AND ';
950
951 if ($val !== '')
952 {
953 if ( ! $this->_has_operator($key))
954 {
955 $key .= ' =';
956 }
957
958 $val = ' '.$this->escape($val);
959 }
960
961 $dest[] = $prefix.$key.$val;
962 }
963 }
964
965 return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest);
966 }
967
968 // --------------------------------------------------------------------
969
970 /**
971 * Tests whether the string has an SQL operator
972 *
973 * @access private
974 * @param string
975 * @return bool
976 */
977 function _has_operator($str)
978 {
979 $str = trim($str);
980 if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str))
981 {
982 return FALSE;
983 }
984
985 return TRUE;
986 }
987
988 // --------------------------------------------------------------------
989
990 /**
991 * Enables a native PHP function to be run, using a platform agnostic wrapper.
992 *
993 * @access public
994 * @param string the function name
995 * @param mixed any parameters needed by the function
996 * @return mixed
997 */
998 function call_function($function)
999 {
1000 $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
1001
1002 if (FALSE === strpos($driver, $function))
1003 {
1004 $function = $driver.$function;
1005 }
1006
1007 if ( ! function_exists($function))
1008 {
1009 if ($this->db_debug)
1010 {
1011 return $this->display_error('db_unsupported_function');
1012 }
1013 return FALSE;
1014 }
1015 else
1016 {
1017 $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null;
1018 if (is_null($args))
1019 {
1020 return call_user_func($function);
1021 }
1022 else
1023 {
1024 return call_user_func_array($function, $args);
1025 }
1026 }
1027 }
1028
1029 // --------------------------------------------------------------------
1030
1031 /**
1032 * Set Cache Directory Path
1033 *
1034 * @access public
1035 * @param string the path to the cache directory
1036 * @return void
1037 */
1038 function cache_set_path($path = '')
1039 {
1040 $this->cachedir = $path;
1041 }
1042
1043 // --------------------------------------------------------------------
1044
1045 /**
1046 * Enable Query Caching
1047 *
1048 * @access public
1049 * @return void
1050 */
1051 function cache_on()
1052 {
1053 $this->cache_on = TRUE;
1054 return TRUE;
1055 }
1056
1057 // --------------------------------------------------------------------
1058
1059 /**
1060 * Disable Query Caching
1061 *
1062 * @access public
1063 * @return void
1064 */
1065 function cache_off()
1066 {
1067 $this->cache_on = FALSE;
1068 return FALSE;
1069 }
1070
1071
1072 // --------------------------------------------------------------------
1073
1074 /**
1075 * Delete the cache files associated with a particular URI
1076 *
1077 * @access public
1078 * @return void
1079 */
1080 function cache_delete($segment_one = '', $segment_two = '')
1081 {
1082 if ( ! $this->_cache_init())
1083 {
1084 return FALSE;
1085 }
1086 return $this->CACHE->delete($segment_one, $segment_two);
1087 }
1088
1089 // --------------------------------------------------------------------
1090
1091 /**
1092 * Delete All cache files
1093 *
1094 * @access public
1095 * @return void
1096 */
1097 function cache_delete_all()
1098 {
1099 if ( ! $this->_cache_init())
1100 {
1101 return FALSE;
1102 }
1103
1104 return $this->CACHE->delete_all();
1105 }
1106
1107 // --------------------------------------------------------------------
1108
1109 /**
1110 * Initialize the Cache Class
1111 *
1112 * @access private
1113 * @return void
1114 */
1115 function _cache_init()
1116 {
1117 if (is_object($this->CACHE) AND class_exists('CI_DB_Cache'))
1118 {
1119 return TRUE;
1120 }
1121
1122 if ( ! class_exists('CI_DB_Cache'))
1123 {
1124 if ( ! @include(BASEPATH.'database/DB_cache.php'))
1125 {
1126 return $this->cache_off();
1127 }
1128 }
1129
1130 $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1131 return TRUE;
1132 }
1133
1134 // --------------------------------------------------------------------
1135
1136 /**
1137 * Close DB Connection
1138 *
1139 * @access public
1140 * @return void
1141 */
1142 function close()
1143 {
1144 if (is_resource($this->conn_id) OR is_object($this->conn_id))
1145 {
1146 $this->_close($this->conn_id);
1147 }
1148 $this->conn_id = FALSE;
1149 }
1150
1151 // --------------------------------------------------------------------
1152
1153 /**
1154 * Display an error message
1155 *
1156 * @access public
1157 * @param string the error message
1158 * @param string any "swap" values
1159 * @param boolean whether to localize the message
1160 * @return string sends the application/error_db.php template
1161 */
1162 function display_error($error = '', $swap = '', $native = FALSE)
1163 {
1164 $LANG =& load_class('Lang', 'core');
1165 $LANG->load('db');
1166
1167 $heading = $LANG->line('db_error_heading');
1168
1169 if ($native == TRUE)
1170 {
1171 $message = $error;
1172 }
1173 else
1174 {
1175 $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error;
1176 }
1177
1178 // Find the most likely culprit of the error by going through
1179 // the backtrace until the source file is no longer in the
1180 // database folder.
1181
1182 $trace = debug_backtrace();
1183
1184 foreach ($trace as $call)
1185 {
1186 if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
1187 {
1188 // Found it - use a relative path for safety
1189 $message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
1190 $message[] = 'Line Number: '.$call['line'];
1191
1192 break;
1193 }
1194 }
1195
1196 $error =& load_class('Exceptions', 'core');
1197 echo $error->show_error($heading, $message, 'error_db');
1198 exit;
1199 }
1200
1201 // --------------------------------------------------------------------
1202
1203 /**
1204 * Protect Identifiers
1205 *
1206 * This function adds backticks if appropriate based on db type
1207 *
1208 * @access private
1209 * @param mixed the item to escape
1210 * @return mixed the item with backticks
1211 */
1212 function protect_identifiers($item, $prefix_single = FALSE)
1213 {
1214 return $this->_protect_identifiers($item, $prefix_single);
1215 }
1216
1217 // --------------------------------------------------------------------
1218
1219 /**
1220 * Protect Identifiers
1221 *
1222 * This function is used extensively by the Active Record class, and by
1223 * a couple functions in this class.
1224 * It takes a column or table name (optionally with an alias) and inserts
1225 * the table prefix onto it. Some logic is necessary in order to deal with
1226 * column names that include the path. Consider a query like this:
1227 *
1228 * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table
1229 *
1230 * Or a query with aliasing:
1231 *
1232 * SELECT m.member_id, m.member_name FROM members AS m
1233 *
1234 * Since the column name can include up to four segments (host, DB, table, column)
1235 * or also have an alias prefix, we need to do a bit of work to figure this out and
1236 * insert the table prefix (if it exists) in the proper position, and escape only
1237 * the correct identifiers.
1238 *
1239 * @access private
1240 * @param string
1241 * @param bool
1242 * @param mixed
1243 * @param bool
1244 * @return string
1245 */
1246 function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1247 {
1248 if ( ! is_bool($protect_identifiers))
1249 {
1250 $protect_identifiers = $this->_protect_identifiers;
1251 }
1252
1253 if (is_array($item))
1254 {
1255 $escaped_array = array();
1256
1257 foreach ($item as $k => $v)
1258 {
1259 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v);
1260 }
1261
1262 return $escaped_array;
1263 }
1264
1265 // Convert tabs or multiple spaces into single spaces
1266 $item = preg_replace('/[\t ]+/', ' ', $item);
1267
1268 // If the item has an alias declaration we remove it and set it aside.
1269 // Basically we remove everything to the right of the first space
1270 if (strpos($item, ' ') !== FALSE)
1271 {
1272 $alias = strstr($item, ' ');
1273 $item = substr($item, 0, - strlen($alias));
1274 }
1275 else
1276 {
1277 $alias = '';
1278 }
1279
1280 // This is basically a bug fix for queries that use MAX, MIN, etc.
1281 // If a parenthesis is found we know that we do not need to
1282 // escape the data or add a prefix. There's probably a more graceful
1283 // way to deal with this, but I'm not thinking of it -- Rick
1284 if (strpos($item, '(') !== FALSE)
1285 {
1286 return $item.$alias;
1287 }
1288
1289 // Break the string apart if it contains periods, then insert the table prefix
1290 // in the correct location, assuming the period doesn't indicate that we're dealing
1291 // with an alias. While we're at it, we will escape the components
1292 if (strpos($item, '.') !== FALSE)
1293 {
1294 $parts = explode('.', $item);
1295
1296 // Does the first segment of the exploded item match
1297 // one of the aliases previously identified? If so,
1298 // we have nothing more to do other than escape the item
1299 if (in_array($parts[0], $this->ar_aliased_tables))
1300 {
1301 if ($protect_identifiers === TRUE)
1302 {
1303 foreach ($parts as $key => $val)
1304 {
1305 if ( ! in_array($val, $this->_reserved_identifiers))
1306 {
1307 $parts[$key] = $this->_escape_identifiers($val);
1308 }
1309 }
1310
1311 $item = implode('.', $parts);
1312 }
1313 return $item.$alias;
1314 }
1315
1316 // Is there a table prefix defined in the config file? If not, no need to do anything
1317 if ($this->dbprefix != '')
1318 {
1319 // We now add the table prefix based on some logic.
1320 // Do we have 4 segments (hostname.database.table.column)?
1321 // If so, we add the table prefix to the column name in the 3rd segment.
1322 if (isset($parts[3]))
1323 {
1324 $i = 2;
1325 }
1326 // Do we have 3 segments (database.table.column)?
1327 // If so, we add the table prefix to the column name in 2nd position
1328 elseif (isset($parts[2]))
1329 {
1330 $i = 1;
1331 }
1332 // Do we have 2 segments (table.column)?
1333 // If so, we add the table prefix to the column name in 1st segment
1334 else
1335 {
1336 $i = 0;
1337 }
1338
1339 // This flag is set when the supplied $item does not contain a field name.
1340 // This can happen when this function is being called from a JOIN.
1341 if ($field_exists == FALSE)
1342 {
1343 $i++;
1344 }
1345
1346 // Verify table prefix and replace if necessary
1347 if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0)
1348 {
1349 $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]);
1350 }
1351
1352 // We only add the table prefix if it does not already exist
1353 if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix)
1354 {
1355 $parts[$i] = $this->dbprefix.$parts[$i];
1356 }
1357
1358 // Put the parts back together
1359 $item = implode('.', $parts);
1360 }
1361
1362 if ($protect_identifiers === TRUE)
1363 {
1364 $item = $this->_escape_identifiers($item);
1365 }
1366
1367 return $item.$alias;
1368 }
1369
1370 // Is there a table prefix? If not, no need to insert it
1371 if ($this->dbprefix != '')
1372 {
1373 // Verify table prefix and replace if necessary
1374 if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0)
1375 {
1376 $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item);
1377 }
1378
1379 // Do we prefix an item with no segments?
1380 if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix)
1381 {
1382 $item = $this->dbprefix.$item;
1383 }
1384 }
1385
1386 if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers))
1387 {
1388 $item = $this->_escape_identifiers($item);
1389 }
1390
1391 return $item.$alias;
1392 }
1393
1394 // --------------------------------------------------------------------
1395
1396 /**
1397 * Dummy method that allows Active Record class to be disabled
1398 *
1399 * This function is used extensively by every db driver.
1400 *
1401 * @return void
1402 */
1403 protected function _reset_select()
1404 {
1405 }
1406
1407 }
1408
1409 /* End of file DB_driver.php */
1410 /* Location: ./system/database/DB_driver.php */