Subversion-Projekte lars-tiefland.ci

Revision

Revision 68 | Revision 2049 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Database Driver Class
42
 *
43
 * This is the platform-independent base DB implementation class.
44
 * This class will not be called directly. Rather, the adapter
45
 * class for the specific database will extend and instantiate it.
46
 *
47
 * @package		CodeIgniter
48
 * @subpackage	Drivers
49
 * @category	Database
50
 * @author		EllisLab Dev Team
51
 * @link		https://codeigniter.com/user_guide/database/
52
 */
53
abstract class CI_DB_driver {
54
 
55
	/**
56
	 * Data Source Name / Connect string
57
	 *
58
	 * @var	string
59
	 */
60
	public $dsn;
61
 
62
	/**
63
	 * Username
64
	 *
65
	 * @var	string
66
	 */
67
	public $username;
68
 
69
	/**
70
	 * Password
71
	 *
72
	 * @var	string
73
	 */
74
	public $password;
75
 
76
	/**
77
	 * Hostname
78
	 *
79
	 * @var	string
80
	 */
81
	public $hostname;
82
 
83
	/**
84
	 * Database name
85
	 *
86
	 * @var	string
87
	 */
88
	public $database;
89
 
90
	/**
91
	 * Database driver
92
	 *
93
	 * @var	string
94
	 */
95
	public $dbdriver		= 'mysqli';
96
 
97
	/**
98
	 * Sub-driver
99
	 *
100
	 * @used-by	CI_DB_pdo_driver
101
	 * @var	string
102
	 */
103
	public $subdriver;
104
 
105
	/**
106
	 * Table prefix
107
	 *
108
	 * @var	string
109
	 */
110
	public $dbprefix		= '';
111
 
112
	/**
113
	 * Character set
114
	 *
115
	 * @var	string
116
	 */
117
	public $char_set		= 'utf8';
118
 
119
	/**
120
	 * Collation
121
	 *
122
	 * @var	string
123
	 */
124
	public $dbcollat		= 'utf8_general_ci';
125
 
126
	/**
127
	 * Encryption flag/data
128
	 *
129
	 * @var	mixed
130
	 */
131
	public $encrypt			= FALSE;
132
 
133
	/**
134
	 * Swap Prefix
135
	 *
136
	 * @var	string
137
	 */
138
	public $swap_pre		= '';
139
 
140
	/**
141
	 * Database port
142
	 *
143
	 * @var	int
144
	 */
145
	public $port			= '';
146
 
147
	/**
148
	 * Persistent connection flag
149
	 *
150
	 * @var	bool
151
	 */
152
	public $pconnect		= FALSE;
153
 
154
	/**
155
	 * Connection ID
156
	 *
157
	 * @var	object|resource
158
	 */
159
	public $conn_id			= FALSE;
160
 
161
	/**
162
	 * Result ID
163
	 *
164
	 * @var	object|resource
165
	 */
166
	public $result_id		= FALSE;
167
 
168
	/**
169
	 * Debug flag
170
	 *
171
	 * Whether to display error messages.
172
	 *
173
	 * @var	bool
174
	 */
175
	public $db_debug		= FALSE;
176
 
177
	/**
178
	 * Benchmark time
179
	 *
180
	 * @var	int
181
	 */
182
	public $benchmark		= 0;
183
 
184
	/**
185
	 * Executed queries count
186
	 *
187
	 * @var	int
188
	 */
189
	public $query_count		= 0;
190
 
191
	/**
192
	 * Bind marker
193
	 *
194
	 * Character used to identify values in a prepared statement.
195
	 *
196
	 * @var	string
197
	 */
198
	public $bind_marker		= '?';
199
 
200
	/**
201
	 * Save queries flag
202
	 *
203
	 * Whether to keep an in-memory history of queries for debugging purposes.
204
	 *
205
	 * @var	bool
206
	 */
207
	public $save_queries		= TRUE;
208
 
209
	/**
210
	 * Queries list
211
	 *
212
	 * @see	CI_DB_driver::$save_queries
213
	 * @var	string[]
214
	 */
215
	public $queries			= array();
216
 
217
	/**
218
	 * Query times
219
	 *
220
	 * A list of times that queries took to execute.
221
	 *
222
	 * @var	array
223
	 */
224
	public $query_times		= array();
225
 
226
	/**
227
	 * Data cache
228
	 *
229
	 * An internal generic value cache.
230
	 *
231
	 * @var	array
232
	 */
233
	public $data_cache		= array();
234
 
235
	/**
236
	 * Transaction enabled flag
237
	 *
238
	 * @var	bool
239
	 */
240
	public $trans_enabled		= TRUE;
241
 
242
	/**
243
	 * Strict transaction mode flag
244
	 *
245
	 * @var	bool
246
	 */
247
	public $trans_strict		= TRUE;
248
 
249
	/**
250
	 * Transaction depth level
251
	 *
252
	 * @var	int
253
	 */
254
	protected $_trans_depth		= 0;
255
 
256
	/**
257
	 * Transaction status flag
258
	 *
259
	 * Used with transactions to determine if a rollback should occur.
260
	 *
261
	 * @var	bool
262
	 */
263
	protected $_trans_status	= TRUE;
264
 
265
	/**
266
	 * Transaction failure flag
267
	 *
268
	 * Used with transactions to determine if a transaction has failed.
269
	 *
270
	 * @var	bool
271
	 */
272
	protected $_trans_failure	= FALSE;
273
 
274
	/**
275
	 * Cache On flag
276
	 *
277
	 * @var	bool
278
	 */
279
	public $cache_on		= FALSE;
280
 
281
	/**
282
	 * Cache directory path
283
	 *
284
	 * @var	bool
285
	 */
286
	public $cachedir		= '';
287
 
288
	/**
289
	 * Cache auto-delete flag
290
	 *
291
	 * @var	bool
292
	 */
293
	public $cache_autodel		= FALSE;
294
 
295
	/**
296
	 * DB Cache object
297
	 *
298
	 * @see	CI_DB_cache
299
	 * @var	object
300
	 */
301
	public $CACHE;
302
 
303
	/**
304
	 * Protect identifiers flag
305
	 *
306
	 * @var	bool
307
	 */
308
	protected $_protect_identifiers		= TRUE;
309
 
310
	/**
311
	 * List of reserved identifiers
312
	 *
313
	 * Identifiers that must NOT be escaped.
314
	 *
315
	 * @var	string[]
316
	 */
317
	protected $_reserved_identifiers	= array('*');
318
 
319
	/**
320
	 * Identifier escape character
321
	 *
322
	 * @var	string
323
	 */
324
	protected $_escape_char = '"';
325
 
326
	/**
327
	 * ESCAPE statement string
328
	 *
329
	 * @var	string
330
	 */
331
	protected $_like_escape_str = " ESCAPE '%s' ";
332
 
333
	/**
334
	 * ESCAPE character
335
	 *
336
	 * @var	string
337
	 */
338
	protected $_like_escape_chr = '!';
339
 
340
	/**
341
	 * ORDER BY random keyword
342
	 *
343
	 * @var	array
344
	 */
345
	protected $_random_keyword = array('RAND()', 'RAND(%d)');
346
 
347
	/**
348
	 * COUNT string
349
	 *
350
	 * @used-by	CI_DB_driver::count_all()
351
	 * @used-by	CI_DB_query_builder::count_all_results()
352
	 *
353
	 * @var	string
354
	 */
355
	protected $_count_string = 'SELECT COUNT(*) AS ';
356
 
357
	// --------------------------------------------------------------------
358
 
359
	/**
360
	 * Class constructor
361
	 *
362
	 * @param	array	$params
363
	 * @return	void
364
	 */
365
	public function __construct($params)
366
	{
367
		if (is_array($params))
368
		{
369
			foreach ($params as $key => $val)
370
			{
371
				$this->$key = $val;
372
			}
373
		}
374
 
375
		log_message('info', 'Database Driver Class Initialized');
376
	}
377
 
378
	// --------------------------------------------------------------------
379
 
380
	/**
381
	 * Initialize Database Settings
382
	 *
383
	 * @return	bool
384
	 */
385
	public function initialize()
386
	{
387
		/* If an established connection is available, then there's
388
		 * no need to connect and select the database.
389
		 *
390
		 * Depending on the database driver, conn_id can be either
391
		 * boolean TRUE, a resource or an object.
392
		 */
393
		if ($this->conn_id)
394
		{
395
			return TRUE;
396
		}
397
 
398
		// ----------------------------------------------------------------
399
 
400
		// Connect to the database and set the connection ID
401
		$this->conn_id = $this->db_connect($this->pconnect);
402
 
403
		// No connection resource? Check if there is a failover else throw an error
404
		if ( ! $this->conn_id)
405
		{
406
			// Check if there is a failover set
407
			if ( ! empty($this->failover) && is_array($this->failover))
408
			{
409
				// Go over all the failovers
410
				foreach ($this->failover as $failover)
411
				{
412
					// Replace the current settings with those of the failover
413
					foreach ($failover as $key => $val)
414
					{
415
						$this->$key = $val;
416
					}
417
 
418
					// Try to connect
419
					$this->conn_id = $this->db_connect($this->pconnect);
420
 
421
					// If a connection is made break the foreach loop
422
					if ($this->conn_id)
423
					{
424
						break;
425
					}
426
				}
427
			}
428
 
429
			// We still don't have a connection?
430
			if ( ! $this->conn_id)
431
			{
432
				log_message('error', 'Unable to connect to the database');
433
 
434
				if ($this->db_debug)
435
				{
436
					$this->display_error('db_unable_to_connect');
437
				}
438
 
439
				return FALSE;
440
			}
441
		}
442
 
443
		// Now we set the character set and that's all
444
		return $this->db_set_charset($this->char_set);
445
	}
446
 
447
	// --------------------------------------------------------------------
448
 
449
	/**
450
	 * DB connect
451
	 *
452
	 * This is just a dummy method that all drivers will override.
453
	 *
454
	 * @return	mixed
455
	 */
456
	public function db_connect()
457
	{
458
		return TRUE;
459
	}
460
 
461
	// --------------------------------------------------------------------
462
 
463
	/**
464
	 * Persistent database connection
465
	 *
466
	 * @return	mixed
467
	 */
468
	public function db_pconnect()
469
	{
470
		return $this->db_connect(TRUE);
471
	}
472
 
473
	// --------------------------------------------------------------------
474
 
475
	/**
476
	 * Reconnect
477
	 *
478
	 * Keep / reestablish the db connection if no queries have been
479
	 * sent for a length of time exceeding the server's idle timeout.
480
	 *
481
	 * This is just a dummy method to allow drivers without such
482
	 * functionality to not declare it, while others will override it.
483
	 *
484
	 * @return	void
485
	 */
486
	public function reconnect()
487
	{
488
	}
489
 
490
	// --------------------------------------------------------------------
491
 
492
	/**
493
	 * Select database
494
	 *
495
	 * This is just a dummy method to allow drivers without such
496
	 * functionality to not declare it, while others will override it.
497
	 *
498
	 * @return	bool
499
	 */
500
	public function db_select()
501
	{
502
		return TRUE;
503
	}
504
 
505
	// --------------------------------------------------------------------
506
 
507
	/**
508
	 * Last error
509
	 *
510
	 * @return	array
511
	 */
512
	public function error()
513
	{
514
		return array('code' => NULL, 'message' => NULL);
515
	}
516
 
517
	// --------------------------------------------------------------------
518
 
519
	/**
520
	 * Set client character set
521
	 *
522
	 * @param	string
523
	 * @return	bool
524
	 */
525
	public function db_set_charset($charset)
526
	{
527
		if (method_exists($this, '_db_set_charset') && ! $this->_db_set_charset($charset))
528
		{
529
			log_message('error', 'Unable to set database connection charset: '.$charset);
530
 
531
			if ($this->db_debug)
532
			{
533
				$this->display_error('db_unable_to_set_charset', $charset);
534
			}
535
 
536
			return FALSE;
537
		}
538
 
539
		return TRUE;
540
	}
541
 
542
	// --------------------------------------------------------------------
543
 
544
	/**
545
	 * The name of the platform in use (mysql, mssql, etc...)
546
	 *
547
	 * @return	string
548
	 */
549
	public function platform()
550
	{
551
		return $this->dbdriver;
552
	}
553
 
554
	// --------------------------------------------------------------------
555
 
556
	/**
557
	 * Database version number
558
	 *
559
	 * Returns a string containing the version of the database being used.
560
	 * Most drivers will override this method.
561
	 *
562
	 * @return	string
563
	 */
564
	public function version()
565
	{
566
		if (isset($this->data_cache['version']))
567
		{
568
			return $this->data_cache['version'];
569
		}
570
 
571
		if (FALSE === ($sql = $this->_version()))
572
		{
573
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
574
		}
575
 
576
		$query = $this->query($sql)->row();
577
		return $this->data_cache['version'] = $query->ver;
578
	}
579
 
580
	// --------------------------------------------------------------------
581
 
582
	/**
583
	 * Version number query string
584
	 *
585
	 * @return	string
586
	 */
587
	protected function _version()
588
	{
589
		return 'SELECT VERSION() AS ver';
590
	}
591
 
592
	// --------------------------------------------------------------------
593
 
594
	/**
595
	 * Execute the query
596
	 *
597
	 * Accepts an SQL string as input and returns a result object upon
598
	 * successful execution of a "read" type query. Returns boolean TRUE
599
	 * upon successful execution of a "write" type query. Returns boolean
600
	 * FALSE upon failure, and if the $db_debug variable is set to TRUE
601
	 * will raise an error.
602
	 *
603
	 * @param	string	$sql
604
	 * @param	array	$binds = FALSE		An array of binding data
605
	 * @param	bool	$return_object = NULL
606
	 * @return	mixed
607
	 */
608
	public function query($sql, $binds = FALSE, $return_object = NULL)
609
	{
610
		if ($sql === '')
611
		{
612
			log_message('error', 'Invalid query: '.$sql);
613
			return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE;
614
		}
615
		elseif ( ! is_bool($return_object))
616
		{
617
			$return_object = ! $this->is_write_type($sql);
618
		}
619
 
620
		// Verify table prefix and replace if necessary
621
		if ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre)
622
		{
623
			$sql = preg_replace('/(\W)'.$this->swap_pre.'(\S+?)/', '\\1'.$this->dbprefix.'\\2', $sql);
624
		}
625
 
626
		// Compile binds if needed
627
		if ($binds !== FALSE)
628
		{
629
			$sql = $this->compile_binds($sql, $binds);
630
		}
631
 
632
		// Is query caching enabled? If the query is a "read type"
633
		// we will load the caching class and return the previously
634
		// cached query if it exists
635
		if ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init())
636
		{
637
			$this->load_rdriver();
638
			if (FALSE !== ($cache = $this->CACHE->read($sql)))
639
			{
640
				return $cache;
641
			}
642
		}
643
 
644
		// Save the query for debugging
645
		if ($this->save_queries === TRUE)
646
		{
647
			$this->queries[] = $sql;
648
		}
649
 
650
		// Start the Query Timer
651
		$time_start = microtime(TRUE);
652
 
653
		// Run the Query
654
		if (FALSE === ($this->result_id = $this->simple_query($sql)))
655
		{
656
			if ($this->save_queries === TRUE)
657
			{
658
				$this->query_times[] = 0;
659
			}
660
 
661
			// This will trigger a rollback if transactions are being used
662
			if ($this->_trans_depth !== 0)
663
			{
664
				$this->_trans_status = FALSE;
665
			}
666
 
667
			// Grab the error now, as we might run some additional queries before displaying the error
668
			$error = $this->error();
669
 
670
			// Log errors
671
			log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql);
672
 
673
			if ($this->db_debug)
674
			{
675
				// We call this function in order to roll-back queries
676
				// if transactions are enabled. If we don't call this here
677
				// the error message will trigger an exit, causing the
678
				// transactions to remain in limbo.
679
				while ($this->_trans_depth !== 0)
680
				{
681
					$trans_depth = $this->_trans_depth;
682
					$this->trans_complete();
683
					if ($trans_depth === $this->_trans_depth)
684
					{
685
						log_message('error', 'Database: Failure during an automated transaction commit/rollback!');
686
						break;
687
					}
688
				}
689
 
690
				// Display errors
691
				return $this->display_error(array('Error Number: '.$error['code'], $error['message'], $sql));
692
			}
693
 
694
			return FALSE;
695
		}
696
 
697
		// Stop and aggregate the query time results
698
		$time_end = microtime(TRUE);
699
		$this->benchmark += $time_end - $time_start;
700
 
701
		if ($this->save_queries === TRUE)
702
		{
703
			$this->query_times[] = $time_end - $time_start;
704
		}
705
 
706
		// Increment the query counter
707
		$this->query_count++;
708
 
709
		// Will we have a result object instantiated? If not - we'll simply return TRUE
710
		if ($return_object !== TRUE)
711
		{
712
			// If caching is enabled we'll auto-cleanup any existing files related to this particular URI
713
			if ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init())
714
			{
715
				$this->CACHE->delete();
716
			}
717
 
718
			return TRUE;
719
		}
720
 
721
		// Load and instantiate the result driver
722
		$driver		= $this->load_rdriver();
723
		$RES		= new $driver($this);
724
 
725
		// Is query caching enabled? If so, we'll serialize the
726
		// result object and save it to a cache file.
727
		if ($this->cache_on === TRUE && $this->_cache_init())
728
		{
729
			// We'll create a new instance of the result object
730
			// only without the platform specific driver since
731
			// we can't use it with cached data (the query result
732
			// resource ID won't be any good once we've cached the
733
			// result object, so we'll have to compile the data
734
			// and save it)
735
			$CR = new CI_DB_result($this);
736
			$CR->result_object	= $RES->result_object();
737
			$CR->result_array	= $RES->result_array();
738
			$CR->num_rows		= $RES->num_rows();
739
 
740
			// Reset these since cached objects can not utilize resource IDs.
741
			$CR->conn_id		= NULL;
742
			$CR->result_id		= NULL;
743
 
744
			$this->CACHE->write($sql, $CR);
745
		}
746
 
747
		return $RES;
748
	}
749
 
750
	// --------------------------------------------------------------------
751
 
752
	/**
753
	 * Load the result drivers
754
	 *
755
	 * @return	string	the name of the result class
756
	 */
757
	public function load_rdriver()
758
	{
759
		$driver = 'CI_DB_'.$this->dbdriver.'_result';
760
 
761
		if ( ! class_exists($driver, FALSE))
762
		{
763
			require_once(BASEPATH.'database/DB_result.php');
764
			require_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php');
765
		}
766
 
767
		return $driver;
768
	}
769
 
770
	// --------------------------------------------------------------------
771
 
772
	/**
773
	 * Simple Query
774
	 * This is a simplified version of the query() function. Internally
775
	 * we only use it when running transaction commands since they do
776
	 * not require all the features of the main query() function.
777
	 *
778
	 * @param	string	the sql query
779
	 * @return	mixed
780
	 */
781
	public function simple_query($sql)
782
	{
783
		if ( ! $this->conn_id)
784
		{
785
			if ( ! $this->initialize())
786
			{
787
				return FALSE;
788
			}
789
		}
790
 
791
		return $this->_execute($sql);
792
	}
793
 
794
	// --------------------------------------------------------------------
795
 
796
	/**
797
	 * Disable Transactions
798
	 * This permits transactions to be disabled at run-time.
799
	 *
800
	 * @return	void
801
	 */
802
	public function trans_off()
803
	{
804
		$this->trans_enabled = FALSE;
805
	}
806
 
807
	// --------------------------------------------------------------------
808
 
809
	/**
810
	 * Enable/disable Transaction Strict Mode
811
	 *
812
	 * When strict mode is enabled, if you are running multiple groups of
813
	 * transactions, if one group fails all subsequent groups will be
814
	 * rolled back.
815
	 *
816
	 * If strict mode is disabled, each group is treated autonomously,
817
	 * meaning a failure of one group will not affect any others
818
	 *
819
	 * @param	bool	$mode = TRUE
820
	 * @return	void
821
	 */
822
	public function trans_strict($mode = TRUE)
823
	{
824
		$this->trans_strict = is_bool($mode) ? $mode : TRUE;
825
	}
826
 
827
	// --------------------------------------------------------------------
828
 
829
	/**
830
	 * Start Transaction
831
	 *
832
	 * @param	bool	$test_mode = FALSE
833
	 * @return	bool
834
	 */
835
	public function trans_start($test_mode = FALSE)
836
	{
837
		if ( ! $this->trans_enabled)
838
		{
839
			return FALSE;
840
		}
841
 
842
		return $this->trans_begin($test_mode);
843
	}
844
 
845
	// --------------------------------------------------------------------
846
 
847
	/**
848
	 * Complete Transaction
849
	 *
850
	 * @return	bool
851
	 */
852
	public function trans_complete()
853
	{
854
		if ( ! $this->trans_enabled)
855
		{
856
			return FALSE;
857
		}
858
 
859
		// The query() function will set this flag to FALSE in the event that a query failed
860
		if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)
861
		{
862
			$this->trans_rollback();
863
 
864
			// If we are NOT running in strict mode, we will reset
865
			// the _trans_status flag so that subsequent groups of
866
			// transactions will be permitted.
867
			if ($this->trans_strict === FALSE)
868
			{
869
				$this->_trans_status = TRUE;
870
			}
871
 
872
			log_message('debug', 'DB Transaction Failure');
873
			return FALSE;
874
		}
875
 
876
		return $this->trans_commit();
877
	}
878
 
879
	// --------------------------------------------------------------------
880
 
881
	/**
882
	 * Lets you retrieve the transaction flag to determine if it has failed
883
	 *
884
	 * @return	bool
885
	 */
886
	public function trans_status()
887
	{
888
		return $this->_trans_status;
889
	}
890
 
891
	// --------------------------------------------------------------------
892
 
893
	/**
894
	 * Begin Transaction
895
	 *
896
	 * @param	bool	$test_mode
897
	 * @return	bool
898
	 */
899
	public function trans_begin($test_mode = FALSE)
900
	{
901
		if ( ! $this->trans_enabled)
902
		{
903
			return FALSE;
904
		}
905
		// When transactions are nested we only begin/commit/rollback the outermost ones
906
		elseif ($this->_trans_depth > 0)
907
		{
908
			$this->_trans_depth++;
909
			return TRUE;
910
		}
911
 
912
		// Reset the transaction failure flag.
913
		// If the $test_mode flag is set to TRUE transactions will be rolled back
914
		// even if the queries produce a successful result.
915
		$this->_trans_failure = ($test_mode === TRUE);
916
 
917
		if ($this->_trans_begin())
918
		{
919
			$this->_trans_depth++;
920
			return TRUE;
921
		}
922
 
923
		return FALSE;
924
	}
925
 
926
	// --------------------------------------------------------------------
927
 
928
	/**
929
	 * Commit Transaction
930
	 *
931
	 * @return	bool
932
	 */
933
	public function trans_commit()
934
	{
935
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
936
		{
937
			return FALSE;
938
		}
939
		// When transactions are nested we only begin/commit/rollback the outermost ones
940
		elseif ($this->_trans_depth > 1 OR $this->_trans_commit())
941
		{
942
			$this->_trans_depth--;
943
			return TRUE;
944
		}
945
 
946
		return FALSE;
947
	}
948
 
949
	// --------------------------------------------------------------------
950
 
951
	/**
952
	 * Rollback Transaction
953
	 *
954
	 * @return	bool
955
	 */
956
	public function trans_rollback()
957
	{
958
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
959
		{
960
			return FALSE;
961
		}
962
		// When transactions are nested we only begin/commit/rollback the outermost ones
963
		elseif ($this->_trans_depth > 1 OR $this->_trans_rollback())
964
		{
965
			$this->_trans_depth--;
966
			return TRUE;
967
		}
968
 
969
		return FALSE;
970
	}
971
 
972
	// --------------------------------------------------------------------
973
 
974
	/**
975
	 * Compile Bindings
976
	 *
977
	 * @param	string	the sql statement
978
	 * @param	array	an array of bind data
979
	 * @return	string
980
	 */
981
	public function compile_binds($sql, $binds)
982
	{
1257 lars 983
		if (empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
68 lars 984
		{
985
			return $sql;
986
		}
987
		elseif ( ! is_array($binds))
988
		{
989
			$binds = array($binds);
990
			$bind_count = 1;
991
		}
992
		else
993
		{
994
			// Make sure we're using numeric keys
995
			$binds = array_values($binds);
996
			$bind_count = count($binds);
997
		}
998
 
999
		// We'll need the marker length later
1000
		$ml = strlen($this->bind_marker);
1001
 
1002
		// Make sure not to replace a chunk inside a string that happens to match the bind marker
1003
		if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
1004
		{
1005
			$c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
1006
				str_replace($matches[0],
1007
					str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
1008
					$sql, $c),
1009
				$matches, PREG_OFFSET_CAPTURE);
1010
 
1011
			// Bind values' count must match the count of markers in the query
1012
			if ($bind_count !== $c)
1013
			{
1014
				return $sql;
1015
			}
1016
		}
1017
		elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
1018
		{
1019
			return $sql;
1020
		}
1021
 
1022
		do
1023
		{
1024
			$c--;
1025
			$escaped_value = $this->escape($binds[$c]);
1026
			if (is_array($escaped_value))
1027
			{
1028
				$escaped_value = '('.implode(',', $escaped_value).')';
1029
			}
1030
			$sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml);
1031
		}
1032
		while ($c !== 0);
1033
 
1034
		return $sql;
1035
	}
1036
 
1037
	// --------------------------------------------------------------------
1038
 
1039
	/**
1040
	 * Determines if a query is a "write" type.
1041
	 *
1042
	 * @param	string	An SQL query string
1043
	 * @return	bool
1044
	 */
1045
	public function is_write_type($sql)
1046
	{
1047
		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', $sql);
1048
	}
1049
 
1050
	// --------------------------------------------------------------------
1051
 
1052
	/**
1053
	 * Calculate the aggregate query elapsed time
1054
	 *
1055
	 * @param	int	The number of decimal places
1056
	 * @return	string
1057
	 */
1058
	public function elapsed_time($decimals = 6)
1059
	{
1060
		return number_format($this->benchmark, $decimals);
1061
	}
1062
 
1063
	// --------------------------------------------------------------------
1064
 
1065
	/**
1066
	 * Returns the total number of queries
1067
	 *
1068
	 * @return	int
1069
	 */
1070
	public function total_queries()
1071
	{
1072
		return $this->query_count;
1073
	}
1074
 
1075
	// --------------------------------------------------------------------
1076
 
1077
	/**
1078
	 * Returns the last query that was executed
1079
	 *
1080
	 * @return	string
1081
	 */
1082
	public function last_query()
1083
	{
1084
		return end($this->queries);
1085
	}
1086
 
1087
	// --------------------------------------------------------------------
1088
 
1089
	/**
1090
	 * "Smart" Escape String
1091
	 *
1092
	 * Escapes data based on type
1093
	 * Sets boolean and null types
1094
	 *
1095
	 * @param	string
1096
	 * @return	mixed
1097
	 */
1098
	public function escape($str)
1099
	{
1100
		if (is_array($str))
1101
		{
1102
			$str = array_map(array(&$this, 'escape'), $str);
1103
			return $str;
1104
		}
1105
		elseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))
1106
		{
1107
			return "'".$this->escape_str($str)."'";
1108
		}
1109
		elseif (is_bool($str))
1110
		{
1111
			return ($str === FALSE) ? 0 : 1;
1112
		}
1113
		elseif ($str === NULL)
1114
		{
1115
			return 'NULL';
1116
		}
1117
 
1118
		return $str;
1119
	}
1120
 
1121
	// --------------------------------------------------------------------
1122
 
1123
	/**
1124
	 * Escape String
1125
	 *
1126
	 * @param	string|string[]	$str	Input string
1127
	 * @param	bool	$like	Whether or not the string will be used in a LIKE condition
1128
	 * @return	string
1129
	 */
1130
	public function escape_str($str, $like = FALSE)
1131
	{
1132
		if (is_array($str))
1133
		{
1134
			foreach ($str as $key => $val)
1135
			{
1136
				$str[$key] = $this->escape_str($val, $like);
1137
			}
1138
 
1139
			return $str;
1140
		}
1141
 
1142
		$str = $this->_escape_str($str);
1143
 
1144
		// escape LIKE condition wildcards
1145
		if ($like === TRUE)
1146
		{
1147
			return str_replace(
1148
				array($this->_like_escape_chr, '%', '_'),
1149
				array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
1150
				$str
1151
			);
1152
		}
1153
 
1154
		return $str;
1155
	}
1156
 
1157
	// --------------------------------------------------------------------
1158
 
1159
	/**
1160
	 * Escape LIKE String
1161
	 *
1162
	 * Calls the individual driver for platform
1163
	 * specific escaping for LIKE conditions
1164
	 *
1165
	 * @param	string|string[]
1166
	 * @return	mixed
1167
	 */
1168
	public function escape_like_str($str)
1169
	{
1170
		return $this->escape_str($str, TRUE);
1171
	}
1172
 
1173
	// --------------------------------------------------------------------
1174
 
1175
	/**
1176
	 * Platform-dependant string escape
1177
	 *
1178
	 * @param	string
1179
	 * @return	string
1180
	 */
1181
	protected function _escape_str($str)
1182
	{
1183
		return str_replace("'", "''", remove_invisible_characters($str));
1184
	}
1185
 
1186
	// --------------------------------------------------------------------
1187
 
1188
	/**
1189
	 * Primary
1190
	 *
1191
	 * Retrieves the primary key. It assumes that the row in the first
1192
	 * position is the primary key
1193
	 *
1194
	 * @param	string	$table	Table name
1195
	 * @return	string
1196
	 */
1197
	public function primary($table)
1198
	{
1199
		$fields = $this->list_fields($table);
1200
		return is_array($fields) ? current($fields) : FALSE;
1201
	}
1202
 
1203
	// --------------------------------------------------------------------
1204
 
1205
	/**
1206
	 * "Count All" query
1207
	 *
1208
	 * Generates a platform-specific query string that counts all records in
1209
	 * the specified database
1210
	 *
1211
	 * @param	string
1212
	 * @return	int
1213
	 */
1214
	public function count_all($table = '')
1215
	{
1216
		if ($table === '')
1217
		{
1218
			return 0;
1219
		}
1220
 
1221
		$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
1222
		if ($query->num_rows() === 0)
1223
		{
1224
			return 0;
1225
		}
1226
 
1227
		$query = $query->row();
1228
		$this->_reset_select();
1229
		return (int) $query->numrows;
1230
	}
1231
 
1232
	// --------------------------------------------------------------------
1233
 
1234
	/**
1235
	 * Returns an array of table names
1236
	 *
1237
	 * @param	string	$constrain_by_prefix = FALSE
1238
	 * @return	array
1239
	 */
1240
	public function list_tables($constrain_by_prefix = FALSE)
1241
	{
1242
		// Is there a cached result?
1243
		if (isset($this->data_cache['table_names']))
1244
		{
1245
			return $this->data_cache['table_names'];
1246
		}
1247
 
1248
		if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
1249
		{
1250
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1251
		}
1252
 
1253
		$this->data_cache['table_names'] = array();
1254
		$query = $this->query($sql);
1255
 
1256
		foreach ($query->result_array() as $row)
1257
		{
1258
			// Do we know from which column to get the table name?
1259
			if ( ! isset($key))
1260
			{
1261
				if (isset($row['table_name']))
1262
				{
1263
					$key = 'table_name';
1264
				}
1265
				elseif (isset($row['TABLE_NAME']))
1266
				{
1267
					$key = 'TABLE_NAME';
1268
				}
1269
				else
1270
				{
1271
					/* We have no other choice but to just get the first element's key.
1272
					 * Due to array_shift() accepting its argument by reference, if
1273
					 * E_STRICT is on, this would trigger a warning. So we'll have to
1274
					 * assign it first.
1275
					 */
1276
					$key = array_keys($row);
1277
					$key = array_shift($key);
1278
				}
1279
			}
1280
 
1281
			$this->data_cache['table_names'][] = $row[$key];
1282
		}
1283
 
1284
		return $this->data_cache['table_names'];
1285
	}
1286
 
1287
	// --------------------------------------------------------------------
1288
 
1289
	/**
1290
	 * Determine if a particular table exists
1291
	 *
1292
	 * @param	string	$table_name
1293
	 * @return	bool
1294
	 */
1295
	public function table_exists($table_name)
1296
	{
1297
		return in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables());
1298
	}
1299
 
1300
	// --------------------------------------------------------------------
1301
 
1302
	/**
1303
	 * Fetch Field Names
1304
	 *
1305
	 * @param	string	$table	Table name
1306
	 * @return	array
1307
	 */
1308
	public function list_fields($table)
1309
	{
1310
		// Is there a cached result?
1311
		if (isset($this->data_cache['field_names'][$table]))
1312
		{
1313
			return $this->data_cache['field_names'][$table];
1314
		}
1315
 
1316
		if (FALSE === ($sql = $this->_list_columns($table)))
1317
		{
1318
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1319
		}
1320
 
1321
		$query = $this->query($sql);
1322
		$this->data_cache['field_names'][$table] = array();
1323
 
1324
		foreach ($query->result_array() as $row)
1325
		{
1326
			// Do we know from where to get the column's name?
1327
			if ( ! isset($key))
1328
			{
1329
				if (isset($row['column_name']))
1330
				{
1331
					$key = 'column_name';
1332
				}
1333
				elseif (isset($row['COLUMN_NAME']))
1334
				{
1335
					$key = 'COLUMN_NAME';
1336
				}
1337
				else
1338
				{
1339
					// We have no other choice but to just get the first element's key.
1340
					$key = key($row);
1341
				}
1342
			}
1343
 
1344
			$this->data_cache['field_names'][$table][] = $row[$key];
1345
		}
1346
 
1347
		return $this->data_cache['field_names'][$table];
1348
	}
1349
 
1350
	// --------------------------------------------------------------------
1351
 
1352
	/**
1353
	 * Determine if a particular field exists
1354
	 *
1355
	 * @param	string
1356
	 * @param	string
1357
	 * @return	bool
1358
	 */
1359
	public function field_exists($field_name, $table_name)
1360
	{
1361
		return in_array($field_name, $this->list_fields($table_name));
1362
	}
1363
 
1364
	// --------------------------------------------------------------------
1365
 
1366
	/**
1367
	 * Returns an object with field data
1368
	 *
1369
	 * @param	string	$table	the table name
1370
	 * @return	array
1371
	 */
1372
	public function field_data($table)
1373
	{
1374
		$query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE)));
1375
		return ($query) ? $query->field_data() : FALSE;
1376
	}
1377
 
1378
	// --------------------------------------------------------------------
1379
 
1380
	/**
1381
	 * Escape the SQL Identifiers
1382
	 *
1383
	 * This function escapes column and table names
1384
	 *
1385
	 * @param	mixed
1386
	 * @return	mixed
1387
	 */
1388
	public function escape_identifiers($item)
1389
	{
1390
		if ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers))
1391
		{
1392
			return $item;
1393
		}
1394
		elseif (is_array($item))
1395
		{
1396
			foreach ($item as $key => $value)
1397
			{
1398
				$item[$key] = $this->escape_identifiers($value);
1399
			}
1400
 
1401
			return $item;
1402
		}
1403
		// Avoid breaking functions and literal values inside queries
1404
		elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->_escape_char !== '"' && $item[0] === '"') OR strpos($item, '(') !== FALSE)
1405
		{
1406
			return $item;
1407
		}
1408
 
1409
		static $preg_ec = array();
1410
 
1411
		if (empty($preg_ec))
1412
		{
1413
			if (is_array($this->_escape_char))
1414
			{
1415
				$preg_ec = array(
1416
					preg_quote($this->_escape_char[0], '/'),
1417
					preg_quote($this->_escape_char[1], '/'),
1418
					$this->_escape_char[0],
1419
					$this->_escape_char[1]
1420
				);
1421
			}
1422
			else
1423
			{
1424
				$preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char, '/');
1425
				$preg_ec[2] = $preg_ec[3] = $this->_escape_char;
1426
			}
1427
		}
1428
 
1429
		foreach ($this->_reserved_identifiers as $id)
1430
		{
1431
			if (strpos($item, '.'.$id) !== FALSE)
1432
			{
1433
				return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);
1434
			}
1435
		}
1436
 
1437
		return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);
1438
	}
1439
 
1440
	// --------------------------------------------------------------------
1441
 
1442
	/**
1443
	 * Generate an insert string
1444
	 *
1445
	 * @param	string	the table upon which the query will be performed
1446
	 * @param	array	an associative array data of key/values
1447
	 * @return	string
1448
	 */
1449
	public function insert_string($table, $data)
1450
	{
1451
		$fields = $values = array();
1452
 
1453
		foreach ($data as $key => $val)
1454
		{
1455
			$fields[] = $this->escape_identifiers($key);
1456
			$values[] = $this->escape($val);
1457
		}
1458
 
1459
		return $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
1460
	}
1461
 
1462
	// --------------------------------------------------------------------
1463
 
1464
	/**
1465
	 * Insert statement
1466
	 *
1467
	 * Generates a platform-specific insert string from the supplied data
1468
	 *
1469
	 * @param	string	the table name
1470
	 * @param	array	the insert keys
1471
	 * @param	array	the insert values
1472
	 * @return	string
1473
	 */
1474
	protected function _insert($table, $keys, $values)
1475
	{
1476
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1477
	}
1478
 
1479
	// --------------------------------------------------------------------
1480
 
1481
	/**
1482
	 * Generate an update string
1483
	 *
1484
	 * @param	string	the table upon which the query will be performed
1485
	 * @param	array	an associative array data of key/values
1486
	 * @param	mixed	the "where" statement
1487
	 * @return	string
1488
	 */
1489
	public function update_string($table, $data, $where)
1490
	{
1491
		if (empty($where))
1492
		{
1493
			return FALSE;
1494
		}
1495
 
1496
		$this->where($where);
1497
 
1498
		$fields = array();
1499
		foreach ($data as $key => $val)
1500
		{
1501
			$fields[$this->protect_identifiers($key)] = $this->escape($val);
1502
		}
1503
 
1504
		$sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields);
1505
		$this->_reset_write();
1506
		return $sql;
1507
	}
1508
 
1509
	// --------------------------------------------------------------------
1510
 
1511
	/**
1512
	 * Update statement
1513
	 *
1514
	 * Generates a platform-specific update string from the supplied data
1515
	 *
1516
	 * @param	string	the table name
1517
	 * @param	array	the update data
1518
	 * @return	string
1519
	 */
1520
	protected function _update($table, $values)
1521
	{
1522
		foreach ($values as $key => $val)
1523
		{
1524
			$valstr[] = $key.' = '.$val;
1525
		}
1526
 
1527
		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
1528
			.$this->_compile_wh('qb_where')
1529
			.$this->_compile_order_by()
1530
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
1531
	}
1532
 
1533
	// --------------------------------------------------------------------
1534
 
1535
	/**
1536
	 * Tests whether the string has an SQL operator
1537
	 *
1538
	 * @param	string
1539
	 * @return	bool
1540
	 */
1541
	protected function _has_operator($str)
1542
	{
1543
		return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str));
1544
	}
1545
 
1546
	// --------------------------------------------------------------------
1547
 
1548
	/**
1549
	 * Returns the SQL string operator
1550
	 *
1551
	 * @param	string
1552
	 * @return	string
1553
	 */
1554
	protected function _get_operator($str)
1555
	{
1556
		static $_operators;
1557
 
1558
		if (empty($_operators))
1559
		{
1560
			$_les = ($this->_like_escape_str !== '')
1561
				? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/')
1562
				: '';
1563
			$_operators = array(
1564
				'\s*(?:<|>|!)?=\s*',             // =, <=, >=, !=
1565
				'\s*<>?\s*',                     // <, <>
1566
				'\s*>\s*',                       // >
1567
				'\s+IS NULL',                    // IS NULL
1568
				'\s+IS NOT NULL',                // IS NOT NULL
1569
				'\s+EXISTS\s*\(.*\)',        // EXISTS(sql)
1570
				'\s+NOT EXISTS\s*\(.*\)',    // NOT EXISTS(sql)
1571
				'\s+BETWEEN\s+',                 // BETWEEN value AND value
1572
				'\s+IN\s*\(.*\)',            // IN(list)
1573
				'\s+NOT IN\s*\(.*\)',        // NOT IN (list)
1574
				'\s+LIKE\s+\S.*('.$_les.')?',    // LIKE 'expr'[ ESCAPE '%s']
1575
				'\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s']
1576
			);
1577
 
1578
		}
1579
 
1580
		return preg_match('/'.implode('|', $_operators).'/i', $str, $match)
1581
			? $match[0] : FALSE;
1582
	}
1583
 
1584
	// --------------------------------------------------------------------
1585
 
1586
	/**
1587
	 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1588
	 *
1589
	 * @param	string	$function	Function name
1590
	 * @return	mixed
1591
	 */
1592
	public function call_function($function)
1593
	{
1594
		$driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';
1595
 
1596
		if (FALSE === strpos($driver, $function))
1597
		{
1598
			$function = $driver.$function;
1599
		}
1600
 
1601
		if ( ! function_exists($function))
1602
		{
1603
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1604
		}
1605
 
1606
		return (func_num_args() > 1)
1607
			? call_user_func_array($function, array_slice(func_get_args(), 1))
1608
			: call_user_func($function);
1609
	}
1610
 
1611
	// --------------------------------------------------------------------
1612
 
1613
	/**
1614
	 * Set Cache Directory Path
1615
	 *
1616
	 * @param	string	the path to the cache directory
1617
	 * @return	void
1618
	 */
1619
	public function cache_set_path($path = '')
1620
	{
1621
		$this->cachedir = $path;
1622
	}
1623
 
1624
	// --------------------------------------------------------------------
1625
 
1626
	/**
1627
	 * Enable Query Caching
1628
	 *
1629
	 * @return	bool	cache_on value
1630
	 */
1631
	public function cache_on()
1632
	{
1633
		return $this->cache_on = TRUE;
1634
	}
1635
 
1636
	// --------------------------------------------------------------------
1637
 
1638
	/**
1639
	 * Disable Query Caching
1640
	 *
1641
	 * @return	bool	cache_on value
1642
	 */
1643
	public function cache_off()
1644
	{
1645
		return $this->cache_on = FALSE;
1646
	}
1647
 
1648
	// --------------------------------------------------------------------
1649
 
1650
	/**
1651
	 * Delete the cache files associated with a particular URI
1652
	 *
1653
	 * @param	string	$segment_one = ''
1654
	 * @param	string	$segment_two = ''
1655
	 * @return	bool
1656
	 */
1657
	public function cache_delete($segment_one = '', $segment_two = '')
1658
	{
1659
		return $this->_cache_init()
1660
			? $this->CACHE->delete($segment_one, $segment_two)
1661
			: FALSE;
1662
	}
1663
 
1664
	// --------------------------------------------------------------------
1665
 
1666
	/**
1667
	 * Delete All cache files
1668
	 *
1669
	 * @return	bool
1670
	 */
1671
	public function cache_delete_all()
1672
	{
1673
		return $this->_cache_init()
1674
			? $this->CACHE->delete_all()
1675
			: FALSE;
1676
	}
1677
 
1678
	// --------------------------------------------------------------------
1679
 
1680
	/**
1681
	 * Initialize the Cache Class
1682
	 *
1683
	 * @return	bool
1684
	 */
1685
	protected function _cache_init()
1686
	{
1687
		if ( ! class_exists('CI_DB_Cache', FALSE))
1688
		{
1689
			require_once(BASEPATH.'database/DB_cache.php');
1690
		}
1691
		elseif (is_object($this->CACHE))
1692
		{
1693
			return TRUE;
1694
		}
1695
 
1696
		$this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1697
		return TRUE;
1698
	}
1699
 
1700
	// --------------------------------------------------------------------
1701
 
1702
	/**
1703
	 * Close DB Connection
1704
	 *
1705
	 * @return	void
1706
	 */
1707
	public function close()
1708
	{
1709
		if ($this->conn_id)
1710
		{
1711
			$this->_close();
1712
			$this->conn_id = FALSE;
1713
		}
1714
	}
1715
 
1716
	// --------------------------------------------------------------------
1717
 
1718
	/**
1719
	 * Close DB Connection
1720
	 *
1721
	 * This method would be overridden by most of the drivers.
1722
	 *
1723
	 * @return	void
1724
	 */
1725
	protected function _close()
1726
	{
1727
		$this->conn_id = FALSE;
1728
	}
1729
 
1730
	// --------------------------------------------------------------------
1731
 
1732
	/**
1733
	 * Display an error message
1734
	 *
1735
	 * @param	string	the error message
1736
	 * @param	string	any "swap" values
1737
	 * @param	bool	whether to localize the message
1738
	 * @return	string	sends the application/views/errors/error_db.php template
1739
	 */
1740
	public function display_error($error = '', $swap = '', $native = FALSE)
1741
	{
1742
		$LANG =& load_class('Lang', 'core');
1743
		$LANG->load('db');
1744
 
1745
		$heading = $LANG->line('db_error_heading');
1746
 
1747
		if ($native === TRUE)
1748
		{
1749
			$message = (array) $error;
1750
		}
1751
		else
1752
		{
1753
			$message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));
1754
		}
1755
 
1756
		// Find the most likely culprit of the error by going through
1757
		// the backtrace until the source file is no longer in the
1758
		// database folder.
1759
		$trace = debug_backtrace();
1760
		foreach ($trace as $call)
1761
		{
1762
			if (isset($call['file'], $call['class']))
1763
			{
1764
				// We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes
1765
				if (DIRECTORY_SEPARATOR !== '/')
1766
				{
1767
					$call['file'] = str_replace('\\', '/', $call['file']);
1768
				}
1769
 
1770
				if (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE)
1771
				{
1772
					// Found it - use a relative path for safety
1773
					$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
1774
					$message[] = 'Line Number: '.$call['line'];
1775
					break;
1776
				}
1777
			}
1778
		}
1779
 
1780
		$error =& load_class('Exceptions', 'core');
1781
		echo $error->show_error($heading, $message, 'error_db');
1782
		exit(8); // EXIT_DATABASE
1783
	}
1784
 
1785
	// --------------------------------------------------------------------
1786
 
1787
	/**
1788
	 * Protect Identifiers
1789
	 *
1790
	 * This function is used extensively by the Query Builder class, and by
1791
	 * a couple functions in this class.
1792
	 * It takes a column or table name (optionally with an alias) and inserts
1793
	 * the table prefix onto it. Some logic is necessary in order to deal with
1794
	 * column names that include the path. Consider a query like this:
1795
	 *
1796
	 * SELECT hostname.database.table.column AS c FROM hostname.database.table
1797
	 *
1798
	 * Or a query with aliasing:
1799
	 *
1800
	 * SELECT m.member_id, m.member_name FROM members AS m
1801
	 *
1802
	 * Since the column name can include up to four segments (host, DB, table, column)
1803
	 * or also have an alias prefix, we need to do a bit of work to figure this out and
1804
	 * insert the table prefix (if it exists) in the proper position, and escape only
1805
	 * the correct identifiers.
1806
	 *
1807
	 * @param	string
1808
	 * @param	bool
1809
	 * @param	mixed
1810
	 * @param	bool
1811
	 * @return	string
1812
	 */
1813
	public function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1814
	{
1815
		if ( ! is_bool($protect_identifiers))
1816
		{
1817
			$protect_identifiers = $this->_protect_identifiers;
1818
		}
1819
 
1820
		if (is_array($item))
1821
		{
1822
			$escaped_array = array();
1823
			foreach ($item as $k => $v)
1824
			{
1825
				$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);
1826
			}
1827
 
1828
			return $escaped_array;
1829
		}
1830
 
1831
		// This is basically a bug fix for queries that use MAX, MIN, etc.
1832
		// If a parenthesis is found we know that we do not need to
1833
		// escape the data or add a prefix. There's probably a more graceful
1834
		// way to deal with this, but I'm not thinking of it -- Rick
1835
		//
1836
		// Added exception for single quotes as well, we don't want to alter
1837
		// literal strings. -- Narf
1838
		if (strcspn($item, "()'") !== strlen($item))
1839
		{
1840
			return $item;
1841
		}
1842
 
1843
		// Convert tabs or multiple spaces into single spaces
1844
		$item = preg_replace('/\s+/', ' ', trim($item));
1845
 
1846
		// If the item has an alias declaration we remove it and set it aside.
1847
		// Note: strripos() is used in order to support spaces in table names
1848
		if ($offset = strripos($item, ' AS '))
1849
		{
1850
			$alias = ($protect_identifiers)
1851
				? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4))
1852
				: substr($item, $offset);
1853
			$item = substr($item, 0, $offset);
1854
		}
1855
		elseif ($offset = strrpos($item, ' '))
1856
		{
1857
			$alias = ($protect_identifiers)
1858
				? ' '.$this->escape_identifiers(substr($item, $offset + 1))
1859
				: substr($item, $offset);
1860
			$item = substr($item, 0, $offset);
1861
		}
1862
		else
1863
		{
1864
			$alias = '';
1865
		}
1866
 
1867
		// Break the string apart if it contains periods, then insert the table prefix
1868
		// in the correct location, assuming the period doesn't indicate that we're dealing
1869
		// with an alias. While we're at it, we will escape the components
1870
		if (strpos($item, '.') !== FALSE)
1871
		{
1872
			$parts = explode('.', $item);
1873
 
1874
			// Does the first segment of the exploded item match
1875
			// one of the aliases previously identified? If so,
1876
			// we have nothing more to do other than escape the item
1877
			//
1878
			// NOTE: The ! empty() condition prevents this method
1879
			//       from breaking when QB isn't enabled.
1880
			if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables))
1881
			{
1882
				if ($protect_identifiers === TRUE)
1883
				{
1884
					foreach ($parts as $key => $val)
1885
					{
1886
						if ( ! in_array($val, $this->_reserved_identifiers))
1887
						{
1888
							$parts[$key] = $this->escape_identifiers($val);
1889
						}
1890
					}
1891
 
1892
					$item = implode('.', $parts);
1893
				}
1894
 
1895
				return $item.$alias;
1896
			}
1897
 
1898
			// Is there a table prefix defined in the config file? If not, no need to do anything
1899
			if ($this->dbprefix !== '')
1900
			{
1901
				// We now add the table prefix based on some logic.
1902
				// Do we have 4 segments (hostname.database.table.column)?
1903
				// If so, we add the table prefix to the column name in the 3rd segment.
1904
				if (isset($parts[3]))
1905
				{
1906
					$i = 2;
1907
				}
1908
				// Do we have 3 segments (database.table.column)?
1909
				// If so, we add the table prefix to the column name in 2nd position
1910
				elseif (isset($parts[2]))
1911
				{
1912
					$i = 1;
1913
				}
1914
				// Do we have 2 segments (table.column)?
1915
				// If so, we add the table prefix to the column name in 1st segment
1916
				else
1917
				{
1918
					$i = 0;
1919
				}
1920
 
1921
				// This flag is set when the supplied $item does not contain a field name.
1922
				// This can happen when this function is being called from a JOIN.
1923
				if ($field_exists === FALSE)
1924
				{
1925
					$i++;
1926
				}
1927
 
1928
				// Verify table prefix and replace if necessary
1929
				if ($this->swap_pre !== '' && strpos($parts[$i], $this->swap_pre) === 0)
1930
				{
1931
					$parts[$i] = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $parts[$i]);
1932
				}
1933
				// We only add the table prefix if it does not already exist
1934
				elseif (strpos($parts[$i], $this->dbprefix) !== 0)
1935
				{
1936
					$parts[$i] = $this->dbprefix.$parts[$i];
1937
				}
1938
 
1939
				// Put the parts back together
1940
				$item = implode('.', $parts);
1941
			}
1942
 
1943
			if ($protect_identifiers === TRUE)
1944
			{
1945
				$item = $this->escape_identifiers($item);
1946
			}
1947
 
1948
			return $item.$alias;
1949
		}
1950
 
1951
		// Is there a table prefix? If not, no need to insert it
1952
		if ($this->dbprefix !== '')
1953
		{
1954
			// Verify table prefix and replace if necessary
1955
			if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)
1956
			{
1957
				$item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item);
1958
			}
1959
			// Do we prefix an item with no segments?
1960
			elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)
1961
			{
1962
				$item = $this->dbprefix.$item;
1963
			}
1964
		}
1965
 
1966
		if ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers))
1967
		{
1968
			$item = $this->escape_identifiers($item);
1969
		}
1970
 
1971
		return $item.$alias;
1972
	}
1973
 
1974
	// --------------------------------------------------------------------
1975
 
1976
	/**
1977
	 * Dummy method that allows Query Builder class to be disabled
1978
	 * and keep count_all() working.
1979
	 *
1980
	 * @return	void
1981
	 */
1982
	protected function _reset_select()
1983
	{
1984
	}
1985
 
1986
}