source: pro-violet-viettel/sourcecode/system/database/DB_driver.php

Last change on this file was 289, checked in by dungnv, 11 years ago
File size: 31.9 KB
Line 
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 */
31class CI_DB_driver {
32
33        var $username;
34        var $password;
35        var $hostname;
36        var $database;
37        var $dbdriver           = 'mysql';
38        var $dbprefix           = '';
39        var $char_set           = 'utf8';
40        var $dbcollat           = 'utf8_general_ci';
41        var $autoinit           = TRUE; // Whether to automatically initialize the DB
42        var $swap_pre           = '';
43        var $port                       = '';
44        var $pconnect           = FALSE;
45        var $conn_id            = FALSE;
46        var $result_id          = FALSE;
47        var $db_debug           = FALSE;
48        var $benchmark          = 0;
49        var $query_count        = 0;
50        var $bind_marker        = '?';
51        var $save_queries       = TRUE;
52        var $queries            = array();
53        var $query_times        = array();
54        var $data_cache         = array();
55        var $trans_enabled      = TRUE;
56        var $trans_strict       = TRUE;
57        var $_trans_depth       = 0;
58        var $_trans_status      = TRUE; // Used with transactions to determine if a rollback should occur
59        var $cache_on           = FALSE;
60        var $cachedir           = '';
61        var $cache_autodel      = FALSE;
62        var $CACHE; // The cache class object
63
64        // Private variables
65        var $_protect_identifiers       = TRUE;
66        var $_reserved_identifiers      = array('*'); // Identifiers that should NOT be escaped
67
68        // These are use with Oracle
69        var $stmt_id;
70        var $curs_id;
71        var $limit_used;
72
73
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 */
Note: See TracBrowser for help on using the repository browser.