Subversion-Projekte lars-tiefland.ci

Revision

Revision 2242 | Revision 2257 | 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
 *
2254 lars 9
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
68 lars 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/)
2254 lars 32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 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
		{
2107 lars 919
			$this->_trans_status = TRUE;
68 lars 920
			$this->_trans_depth++;
921
			return TRUE;
922
		}
923
 
924
		return FALSE;
925
	}
926
 
927
	// --------------------------------------------------------------------
928
 
929
	/**
930
	 * Commit Transaction
931
	 *
932
	 * @return	bool
933
	 */
934
	public function trans_commit()
935
	{
936
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
937
		{
938
			return FALSE;
939
		}
940
		// When transactions are nested we only begin/commit/rollback the outermost ones
941
		elseif ($this->_trans_depth > 1 OR $this->_trans_commit())
942
		{
943
			$this->_trans_depth--;
944
			return TRUE;
945
		}
946
 
947
		return FALSE;
948
	}
949
 
950
	// --------------------------------------------------------------------
951
 
952
	/**
953
	 * Rollback Transaction
954
	 *
955
	 * @return	bool
956
	 */
957
	public function trans_rollback()
958
	{
959
		if ( ! $this->trans_enabled OR $this->_trans_depth === 0)
960
		{
961
			return FALSE;
962
		}
963
		// When transactions are nested we only begin/commit/rollback the outermost ones
964
		elseif ($this->_trans_depth > 1 OR $this->_trans_rollback())
965
		{
966
			$this->_trans_depth--;
967
			return TRUE;
968
		}
969
 
970
		return FALSE;
971
	}
972
 
973
	// --------------------------------------------------------------------
974
 
975
	/**
976
	 * Compile Bindings
977
	 *
978
	 * @param	string	the sql statement
979
	 * @param	array	an array of bind data
980
	 * @return	string
981
	 */
982
	public function compile_binds($sql, $binds)
983
	{
1257 lars 984
		if (empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
68 lars 985
		{
986
			return $sql;
987
		}
988
		elseif ( ! is_array($binds))
989
		{
990
			$binds = array($binds);
991
			$bind_count = 1;
992
		}
993
		else
994
		{
995
			// Make sure we're using numeric keys
996
			$binds = array_values($binds);
997
			$bind_count = count($binds);
998
		}
999
 
1000
		// We'll need the marker length later
1001
		$ml = strlen($this->bind_marker);
1002
 
1003
		// Make sure not to replace a chunk inside a string that happens to match the bind marker
2049 lars 1004
		if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches))
68 lars 1005
		{
1006
			$c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
1007
				str_replace($matches[0],
1008
					str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
1009
					$sql, $c),
1010
				$matches, PREG_OFFSET_CAPTURE);
1011
 
1012
			// Bind values' count must match the count of markers in the query
1013
			if ($bind_count !== $c)
1014
			{
1015
				return $sql;
1016
			}
1017
		}
1018
		elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
1019
		{
1020
			return $sql;
1021
		}
1022
 
1023
		do
1024
		{
1025
			$c--;
1026
			$escaped_value = $this->escape($binds[$c]);
1027
			if (is_array($escaped_value))
1028
			{
1029
				$escaped_value = '('.implode(',', $escaped_value).')';
1030
			}
1031
			$sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml);
1032
		}
1033
		while ($c !== 0);
1034
 
1035
		return $sql;
1036
	}
1037
 
1038
	// --------------------------------------------------------------------
1039
 
1040
	/**
1041
	 * Determines if a query is a "write" type.
1042
	 *
1043
	 * @param	string	An SQL query string
1044
	 * @return	bool
1045
	 */
1046
	public function is_write_type($sql)
1047
	{
2107 lars 1048
		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i', $sql);
68 lars 1049
	}
1050
 
1051
	// --------------------------------------------------------------------
1052
 
1053
	/**
1054
	 * Calculate the aggregate query elapsed time
1055
	 *
1056
	 * @param	int	The number of decimal places
1057
	 * @return	string
1058
	 */
1059
	public function elapsed_time($decimals = 6)
1060
	{
1061
		return number_format($this->benchmark, $decimals);
1062
	}
1063
 
1064
	// --------------------------------------------------------------------
1065
 
1066
	/**
1067
	 * Returns the total number of queries
1068
	 *
1069
	 * @return	int
1070
	 */
1071
	public function total_queries()
1072
	{
1073
		return $this->query_count;
1074
	}
1075
 
1076
	// --------------------------------------------------------------------
1077
 
1078
	/**
1079
	 * Returns the last query that was executed
1080
	 *
1081
	 * @return	string
1082
	 */
1083
	public function last_query()
1084
	{
1085
		return end($this->queries);
1086
	}
1087
 
1088
	// --------------------------------------------------------------------
1089
 
1090
	/**
1091
	 * "Smart" Escape String
1092
	 *
1093
	 * Escapes data based on type
1094
	 * Sets boolean and null types
1095
	 *
1096
	 * @param	string
1097
	 * @return	mixed
1098
	 */
1099
	public function escape($str)
1100
	{
1101
		if (is_array($str))
1102
		{
1103
			$str = array_map(array(&$this, 'escape'), $str);
1104
			return $str;
1105
		}
1106
		elseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))
1107
		{
1108
			return "'".$this->escape_str($str)."'";
1109
		}
1110
		elseif (is_bool($str))
1111
		{
1112
			return ($str === FALSE) ? 0 : 1;
1113
		}
1114
		elseif ($str === NULL)
1115
		{
1116
			return 'NULL';
1117
		}
1118
 
1119
		return $str;
1120
	}
1121
 
1122
	// --------------------------------------------------------------------
1123
 
1124
	/**
1125
	 * Escape String
1126
	 *
1127
	 * @param	string|string[]	$str	Input string
1128
	 * @param	bool	$like	Whether or not the string will be used in a LIKE condition
1129
	 * @return	string
1130
	 */
1131
	public function escape_str($str, $like = FALSE)
1132
	{
1133
		if (is_array($str))
1134
		{
1135
			foreach ($str as $key => $val)
1136
			{
1137
				$str[$key] = $this->escape_str($val, $like);
1138
			}
1139
 
1140
			return $str;
1141
		}
1142
 
1143
		$str = $this->_escape_str($str);
1144
 
1145
		// escape LIKE condition wildcards
1146
		if ($like === TRUE)
1147
		{
1148
			return str_replace(
1149
				array($this->_like_escape_chr, '%', '_'),
1150
				array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
1151
				$str
1152
			);
1153
		}
1154
 
1155
		return $str;
1156
	}
1157
 
1158
	// --------------------------------------------------------------------
1159
 
1160
	/**
1161
	 * Escape LIKE String
1162
	 *
1163
	 * Calls the individual driver for platform
1164
	 * specific escaping for LIKE conditions
1165
	 *
1166
	 * @param	string|string[]
1167
	 * @return	mixed
1168
	 */
1169
	public function escape_like_str($str)
1170
	{
1171
		return $this->escape_str($str, TRUE);
1172
	}
1173
 
1174
	// --------------------------------------------------------------------
1175
 
1176
	/**
2107 lars 1177
	 * Platform-dependent string escape
68 lars 1178
	 *
1179
	 * @param	string
1180
	 * @return	string
1181
	 */
1182
	protected function _escape_str($str)
1183
	{
2107 lars 1184
		return str_replace("'", "''", remove_invisible_characters($str, FALSE));
68 lars 1185
	}
1186
 
1187
	// --------------------------------------------------------------------
1188
 
1189
	/**
1190
	 * Primary
1191
	 *
1192
	 * Retrieves the primary key. It assumes that the row in the first
1193
	 * position is the primary key
1194
	 *
1195
	 * @param	string	$table	Table name
1196
	 * @return	string
1197
	 */
1198
	public function primary($table)
1199
	{
1200
		$fields = $this->list_fields($table);
1201
		return is_array($fields) ? current($fields) : FALSE;
1202
	}
1203
 
1204
	// --------------------------------------------------------------------
1205
 
1206
	/**
1207
	 * "Count All" query
1208
	 *
1209
	 * Generates a platform-specific query string that counts all records in
1210
	 * the specified database
1211
	 *
1212
	 * @param	string
1213
	 * @return	int
1214
	 */
1215
	public function count_all($table = '')
1216
	{
1217
		if ($table === '')
1218
		{
1219
			return 0;
1220
		}
1221
 
1222
		$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
1223
		if ($query->num_rows() === 0)
1224
		{
1225
			return 0;
1226
		}
1227
 
1228
		$query = $query->row();
1229
		$this->_reset_select();
1230
		return (int) $query->numrows;
1231
	}
1232
 
1233
	// --------------------------------------------------------------------
1234
 
1235
	/**
1236
	 * Returns an array of table names
1237
	 *
1238
	 * @param	string	$constrain_by_prefix = FALSE
1239
	 * @return	array
1240
	 */
1241
	public function list_tables($constrain_by_prefix = FALSE)
1242
	{
1243
		// Is there a cached result?
1244
		if (isset($this->data_cache['table_names']))
1245
		{
1246
			return $this->data_cache['table_names'];
1247
		}
1248
 
1249
		if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix)))
1250
		{
1251
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1252
		}
1253
 
1254
		$this->data_cache['table_names'] = array();
1255
		$query = $this->query($sql);
1256
 
1257
		foreach ($query->result_array() as $row)
1258
		{
1259
			// Do we know from which column to get the table name?
1260
			if ( ! isset($key))
1261
			{
1262
				if (isset($row['table_name']))
1263
				{
1264
					$key = 'table_name';
1265
				}
1266
				elseif (isset($row['TABLE_NAME']))
1267
				{
1268
					$key = 'TABLE_NAME';
1269
				}
1270
				else
1271
				{
1272
					/* We have no other choice but to just get the first element's key.
1273
					 * Due to array_shift() accepting its argument by reference, if
1274
					 * E_STRICT is on, this would trigger a warning. So we'll have to
1275
					 * assign it first.
1276
					 */
1277
					$key = array_keys($row);
1278
					$key = array_shift($key);
1279
				}
1280
			}
1281
 
1282
			$this->data_cache['table_names'][] = $row[$key];
1283
		}
1284
 
1285
		return $this->data_cache['table_names'];
1286
	}
1287
 
1288
	// --------------------------------------------------------------------
1289
 
1290
	/**
1291
	 * Determine if a particular table exists
1292
	 *
1293
	 * @param	string	$table_name
1294
	 * @return	bool
1295
	 */
1296
	public function table_exists($table_name)
1297
	{
1298
		return in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables());
1299
	}
1300
 
1301
	// --------------------------------------------------------------------
1302
 
1303
	/**
1304
	 * Fetch Field Names
1305
	 *
1306
	 * @param	string	$table	Table name
1307
	 * @return	array
1308
	 */
1309
	public function list_fields($table)
1310
	{
1311
		// Is there a cached result?
1312
		if (isset($this->data_cache['field_names'][$table]))
1313
		{
1314
			return $this->data_cache['field_names'][$table];
1315
		}
1316
 
1317
		if (FALSE === ($sql = $this->_list_columns($table)))
1318
		{
1319
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1320
		}
1321
 
1322
		$query = $this->query($sql);
1323
		$this->data_cache['field_names'][$table] = array();
1324
 
1325
		foreach ($query->result_array() as $row)
1326
		{
1327
			// Do we know from where to get the column's name?
1328
			if ( ! isset($key))
1329
			{
1330
				if (isset($row['column_name']))
1331
				{
1332
					$key = 'column_name';
1333
				}
1334
				elseif (isset($row['COLUMN_NAME']))
1335
				{
1336
					$key = 'COLUMN_NAME';
1337
				}
1338
				else
1339
				{
1340
					// We have no other choice but to just get the first element's key.
1341
					$key = key($row);
1342
				}
1343
			}
1344
 
1345
			$this->data_cache['field_names'][$table][] = $row[$key];
1346
		}
1347
 
1348
		return $this->data_cache['field_names'][$table];
1349
	}
1350
 
1351
	// --------------------------------------------------------------------
1352
 
1353
	/**
1354
	 * Determine if a particular field exists
1355
	 *
1356
	 * @param	string
1357
	 * @param	string
1358
	 * @return	bool
1359
	 */
1360
	public function field_exists($field_name, $table_name)
1361
	{
1362
		return in_array($field_name, $this->list_fields($table_name));
1363
	}
1364
 
1365
	// --------------------------------------------------------------------
1366
 
1367
	/**
1368
	 * Returns an object with field data
1369
	 *
1370
	 * @param	string	$table	the table name
1371
	 * @return	array
1372
	 */
1373
	public function field_data($table)
1374
	{
1375
		$query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE)));
1376
		return ($query) ? $query->field_data() : FALSE;
1377
	}
1378
 
1379
	// --------------------------------------------------------------------
1380
 
1381
	/**
1382
	 * Escape the SQL Identifiers
1383
	 *
1384
	 * This function escapes column and table names
1385
	 *
1386
	 * @param	mixed
1387
	 * @return	mixed
1388
	 */
1389
	public function escape_identifiers($item)
1390
	{
1391
		if ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers))
1392
		{
1393
			return $item;
1394
		}
1395
		elseif (is_array($item))
1396
		{
1397
			foreach ($item as $key => $value)
1398
			{
1399
				$item[$key] = $this->escape_identifiers($value);
1400
			}
1401
 
1402
			return $item;
1403
		}
1404
		// Avoid breaking functions and literal values inside queries
1405
		elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->_escape_char !== '"' && $item[0] === '"') OR strpos($item, '(') !== FALSE)
1406
		{
1407
			return $item;
1408
		}
1409
 
1410
		static $preg_ec = array();
1411
 
1412
		if (empty($preg_ec))
1413
		{
1414
			if (is_array($this->_escape_char))
1415
			{
1416
				$preg_ec = array(
1417
					preg_quote($this->_escape_char[0], '/'),
1418
					preg_quote($this->_escape_char[1], '/'),
1419
					$this->_escape_char[0],
1420
					$this->_escape_char[1]
1421
				);
1422
			}
1423
			else
1424
			{
1425
				$preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char, '/');
1426
				$preg_ec[2] = $preg_ec[3] = $this->_escape_char;
1427
			}
1428
		}
1429
 
1430
		foreach ($this->_reserved_identifiers as $id)
1431
		{
1432
			if (strpos($item, '.'.$id) !== FALSE)
1433
			{
1434
				return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);
1435
			}
1436
		}
1437
 
1438
		return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);
1439
	}
1440
 
1441
	// --------------------------------------------------------------------
1442
 
1443
	/**
1444
	 * Generate an insert string
1445
	 *
1446
	 * @param	string	the table upon which the query will be performed
1447
	 * @param	array	an associative array data of key/values
1448
	 * @return	string
1449
	 */
1450
	public function insert_string($table, $data)
1451
	{
1452
		$fields = $values = array();
1453
 
1454
		foreach ($data as $key => $val)
1455
		{
1456
			$fields[] = $this->escape_identifiers($key);
1457
			$values[] = $this->escape($val);
1458
		}
1459
 
1460
		return $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
1461
	}
1462
 
1463
	// --------------------------------------------------------------------
1464
 
1465
	/**
1466
	 * Insert statement
1467
	 *
1468
	 * Generates a platform-specific insert string from the supplied data
1469
	 *
1470
	 * @param	string	the table name
1471
	 * @param	array	the insert keys
1472
	 * @param	array	the insert values
1473
	 * @return	string
1474
	 */
1475
	protected function _insert($table, $keys, $values)
1476
	{
1477
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1478
	}
1479
 
1480
	// --------------------------------------------------------------------
1481
 
1482
	/**
1483
	 * Generate an update string
1484
	 *
1485
	 * @param	string	the table upon which the query will be performed
1486
	 * @param	array	an associative array data of key/values
1487
	 * @param	mixed	the "where" statement
1488
	 * @return	string
1489
	 */
1490
	public function update_string($table, $data, $where)
1491
	{
1492
		if (empty($where))
1493
		{
1494
			return FALSE;
1495
		}
1496
 
1497
		$this->where($where);
1498
 
1499
		$fields = array();
1500
		foreach ($data as $key => $val)
1501
		{
1502
			$fields[$this->protect_identifiers($key)] = $this->escape($val);
1503
		}
1504
 
1505
		$sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields);
1506
		$this->_reset_write();
1507
		return $sql;
1508
	}
1509
 
1510
	// --------------------------------------------------------------------
1511
 
1512
	/**
1513
	 * Update statement
1514
	 *
1515
	 * Generates a platform-specific update string from the supplied data
1516
	 *
1517
	 * @param	string	the table name
1518
	 * @param	array	the update data
1519
	 * @return	string
1520
	 */
1521
	protected function _update($table, $values)
1522
	{
1523
		foreach ($values as $key => $val)
1524
		{
1525
			$valstr[] = $key.' = '.$val;
1526
		}
1527
 
1528
		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
1529
			.$this->_compile_wh('qb_where')
1530
			.$this->_compile_order_by()
2254 lars 1531
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
68 lars 1532
	}
1533
 
1534
	// --------------------------------------------------------------------
1535
 
1536
	/**
1537
	 * Tests whether the string has an SQL operator
1538
	 *
1539
	 * @param	string
1540
	 * @return	bool
1541
	 */
1542
	protected function _has_operator($str)
1543
	{
1544
		return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str));
1545
	}
1546
 
1547
	// --------------------------------------------------------------------
1548
 
1549
	/**
1550
	 * Returns the SQL string operator
1551
	 *
1552
	 * @param	string
1553
	 * @return	string
1554
	 */
1555
	protected function _get_operator($str)
1556
	{
1557
		static $_operators;
1558
 
1559
		if (empty($_operators))
1560
		{
1561
			$_les = ($this->_like_escape_str !== '')
1562
				? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/')
1563
				: '';
1564
			$_operators = array(
1565
				'\s*(?:<|>|!)?=\s*',             // =, <=, >=, !=
1566
				'\s*<>?\s*',                     // <, <>
1567
				'\s*>\s*',                       // >
1568
				'\s+IS NULL',                    // IS NULL
1569
				'\s+IS NOT NULL',                // IS NOT NULL
1570
				'\s+EXISTS\s*\(.*\)',        // EXISTS(sql)
1571
				'\s+NOT EXISTS\s*\(.*\)',    // NOT EXISTS(sql)
1572
				'\s+BETWEEN\s+',                 // BETWEEN value AND value
1573
				'\s+IN\s*\(.*\)',            // IN(list)
1574
				'\s+NOT IN\s*\(.*\)',        // NOT IN (list)
1575
				'\s+LIKE\s+\S.*('.$_les.')?',    // LIKE 'expr'[ ESCAPE '%s']
1576
				'\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s']
1577
			);
1578
 
1579
		}
1580
 
1581
		return preg_match('/'.implode('|', $_operators).'/i', $str, $match)
1582
			? $match[0] : FALSE;
1583
	}
1584
 
1585
	// --------------------------------------------------------------------
1586
 
1587
	/**
1588
	 * Enables a native PHP function to be run, using a platform agnostic wrapper.
1589
	 *
1590
	 * @param	string	$function	Function name
1591
	 * @return	mixed
1592
	 */
1593
	public function call_function($function)
1594
	{
1595
		$driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';
1596
 
1597
		if (FALSE === strpos($driver, $function))
1598
		{
1599
			$function = $driver.$function;
1600
		}
1601
 
1602
		if ( ! function_exists($function))
1603
		{
1604
			return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE;
1605
		}
1606
 
1607
		return (func_num_args() > 1)
1608
			? call_user_func_array($function, array_slice(func_get_args(), 1))
1609
			: call_user_func($function);
1610
	}
1611
 
1612
	// --------------------------------------------------------------------
1613
 
1614
	/**
1615
	 * Set Cache Directory Path
1616
	 *
1617
	 * @param	string	the path to the cache directory
1618
	 * @return	void
1619
	 */
1620
	public function cache_set_path($path = '')
1621
	{
1622
		$this->cachedir = $path;
1623
	}
1624
 
1625
	// --------------------------------------------------------------------
1626
 
1627
	/**
1628
	 * Enable Query Caching
1629
	 *
1630
	 * @return	bool	cache_on value
1631
	 */
1632
	public function cache_on()
1633
	{
1634
		return $this->cache_on = TRUE;
1635
	}
1636
 
1637
	// --------------------------------------------------------------------
1638
 
1639
	/**
1640
	 * Disable Query Caching
1641
	 *
1642
	 * @return	bool	cache_on value
1643
	 */
1644
	public function cache_off()
1645
	{
1646
		return $this->cache_on = FALSE;
1647
	}
1648
 
1649
	// --------------------------------------------------------------------
1650
 
1651
	/**
1652
	 * Delete the cache files associated with a particular URI
1653
	 *
1654
	 * @param	string	$segment_one = ''
1655
	 * @param	string	$segment_two = ''
1656
	 * @return	bool
1657
	 */
1658
	public function cache_delete($segment_one = '', $segment_two = '')
1659
	{
1660
		return $this->_cache_init()
1661
			? $this->CACHE->delete($segment_one, $segment_two)
1662
			: FALSE;
1663
	}
1664
 
1665
	// --------------------------------------------------------------------
1666
 
1667
	/**
1668
	 * Delete All cache files
1669
	 *
1670
	 * @return	bool
1671
	 */
1672
	public function cache_delete_all()
1673
	{
1674
		return $this->_cache_init()
1675
			? $this->CACHE->delete_all()
1676
			: FALSE;
1677
	}
1678
 
1679
	// --------------------------------------------------------------------
1680
 
1681
	/**
1682
	 * Initialize the Cache Class
1683
	 *
1684
	 * @return	bool
1685
	 */
1686
	protected function _cache_init()
1687
	{
1688
		if ( ! class_exists('CI_DB_Cache', FALSE))
1689
		{
1690
			require_once(BASEPATH.'database/DB_cache.php');
1691
		}
1692
		elseif (is_object($this->CACHE))
1693
		{
1694
			return TRUE;
1695
		}
1696
 
1697
		$this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects
1698
		return TRUE;
1699
	}
1700
 
1701
	// --------------------------------------------------------------------
1702
 
1703
	/**
1704
	 * Close DB Connection
1705
	 *
1706
	 * @return	void
1707
	 */
1708
	public function close()
1709
	{
1710
		if ($this->conn_id)
1711
		{
1712
			$this->_close();
1713
			$this->conn_id = FALSE;
1714
		}
1715
	}
1716
 
1717
	// --------------------------------------------------------------------
1718
 
1719
	/**
1720
	 * Close DB Connection
1721
	 *
1722
	 * This method would be overridden by most of the drivers.
1723
	 *
1724
	 * @return	void
1725
	 */
1726
	protected function _close()
1727
	{
1728
		$this->conn_id = FALSE;
1729
	}
1730
 
1731
	// --------------------------------------------------------------------
1732
 
1733
	/**
1734
	 * Display an error message
1735
	 *
1736
	 * @param	string	the error message
1737
	 * @param	string	any "swap" values
1738
	 * @param	bool	whether to localize the message
1739
	 * @return	string	sends the application/views/errors/error_db.php template
1740
	 */
1741
	public function display_error($error = '', $swap = '', $native = FALSE)
1742
	{
1743
		$LANG =& load_class('Lang', 'core');
1744
		$LANG->load('db');
1745
 
1746
		$heading = $LANG->line('db_error_heading');
1747
 
1748
		if ($native === TRUE)
1749
		{
1750
			$message = (array) $error;
1751
		}
1752
		else
1753
		{
1754
			$message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error)));
1755
		}
1756
 
1757
		// Find the most likely culprit of the error by going through
1758
		// the backtrace until the source file is no longer in the
1759
		// database folder.
1760
		$trace = debug_backtrace();
1761
		foreach ($trace as $call)
1762
		{
1763
			if (isset($call['file'], $call['class']))
1764
			{
1765
				// We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes
1766
				if (DIRECTORY_SEPARATOR !== '/')
1767
				{
1768
					$call['file'] = str_replace('\\', '/', $call['file']);
1769
				}
1770
 
1771
				if (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE)
1772
				{
1773
					// Found it - use a relative path for safety
1774
					$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
1775
					$message[] = 'Line Number: '.$call['line'];
1776
					break;
1777
				}
1778
			}
1779
		}
1780
 
1781
		$error =& load_class('Exceptions', 'core');
1782
		echo $error->show_error($heading, $message, 'error_db');
1783
		exit(8); // EXIT_DATABASE
1784
	}
1785
 
1786
	// --------------------------------------------------------------------
1787
 
1788
	/**
1789
	 * Protect Identifiers
1790
	 *
1791
	 * This function is used extensively by the Query Builder class, and by
1792
	 * a couple functions in this class.
1793
	 * It takes a column or table name (optionally with an alias) and inserts
1794
	 * the table prefix onto it. Some logic is necessary in order to deal with
1795
	 * column names that include the path. Consider a query like this:
1796
	 *
1797
	 * SELECT hostname.database.table.column AS c FROM hostname.database.table
1798
	 *
1799
	 * Or a query with aliasing:
1800
	 *
1801
	 * SELECT m.member_id, m.member_name FROM members AS m
1802
	 *
1803
	 * Since the column name can include up to four segments (host, DB, table, column)
1804
	 * or also have an alias prefix, we need to do a bit of work to figure this out and
1805
	 * insert the table prefix (if it exists) in the proper position, and escape only
1806
	 * the correct identifiers.
1807
	 *
1808
	 * @param	string
1809
	 * @param	bool
1810
	 * @param	mixed
1811
	 * @param	bool
1812
	 * @return	string
1813
	 */
1814
	public function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE)
1815
	{
1816
		if ( ! is_bool($protect_identifiers))
1817
		{
1818
			$protect_identifiers = $this->_protect_identifiers;
1819
		}
1820
 
1821
		if (is_array($item))
1822
		{
1823
			$escaped_array = array();
1824
			foreach ($item as $k => $v)
1825
			{
1826
				$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);
1827
			}
1828
 
1829
			return $escaped_array;
1830
		}
1831
 
1832
		// This is basically a bug fix for queries that use MAX, MIN, etc.
1833
		// If a parenthesis is found we know that we do not need to
1834
		// escape the data or add a prefix. There's probably a more graceful
1835
		// way to deal with this, but I'm not thinking of it -- Rick
1836
		//
1837
		// Added exception for single quotes as well, we don't want to alter
1838
		// literal strings. -- Narf
1839
		if (strcspn($item, "()'") !== strlen($item))
1840
		{
1841
			return $item;
1842
		}
1843
 
1844
		// Convert tabs or multiple spaces into single spaces
1845
		$item = preg_replace('/\s+/', ' ', trim($item));
1846
 
1847
		// If the item has an alias declaration we remove it and set it aside.
1848
		// Note: strripos() is used in order to support spaces in table names
1849
		if ($offset = strripos($item, ' AS '))
1850
		{
1851
			$alias = ($protect_identifiers)
1852
				? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4))
1853
				: substr($item, $offset);
1854
			$item = substr($item, 0, $offset);
1855
		}
1856
		elseif ($offset = strrpos($item, ' '))
1857
		{
1858
			$alias = ($protect_identifiers)
1859
				? ' '.$this->escape_identifiers(substr($item, $offset + 1))
1860
				: substr($item, $offset);
1861
			$item = substr($item, 0, $offset);
1862
		}
1863
		else
1864
		{
1865
			$alias = '';
1866
		}
1867
 
1868
		// Break the string apart if it contains periods, then insert the table prefix
1869
		// in the correct location, assuming the period doesn't indicate that we're dealing
1870
		// with an alias. While we're at it, we will escape the components
1871
		if (strpos($item, '.') !== FALSE)
1872
		{
1873
			$parts = explode('.', $item);
1874
 
1875
			// Does the first segment of the exploded item match
1876
			// one of the aliases previously identified? If so,
1877
			// we have nothing more to do other than escape the item
1878
			//
1879
			// NOTE: The ! empty() condition prevents this method
1880
			//       from breaking when QB isn't enabled.
1881
			if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables))
1882
			{
1883
				if ($protect_identifiers === TRUE)
1884
				{
1885
					foreach ($parts as $key => $val)
1886
					{
1887
						if ( ! in_array($val, $this->_reserved_identifiers))
1888
						{
1889
							$parts[$key] = $this->escape_identifiers($val);
1890
						}
1891
					}
1892
 
1893
					$item = implode('.', $parts);
1894
				}
1895
 
1896
				return $item.$alias;
1897
			}
1898
 
1899
			// Is there a table prefix defined in the config file? If not, no need to do anything
1900
			if ($this->dbprefix !== '')
1901
			{
1902
				// We now add the table prefix based on some logic.
1903
				// Do we have 4 segments (hostname.database.table.column)?
1904
				// If so, we add the table prefix to the column name in the 3rd segment.
1905
				if (isset($parts[3]))
1906
				{
1907
					$i = 2;
1908
				}
1909
				// Do we have 3 segments (database.table.column)?
1910
				// If so, we add the table prefix to the column name in 2nd position
1911
				elseif (isset($parts[2]))
1912
				{
1913
					$i = 1;
1914
				}
1915
				// Do we have 2 segments (table.column)?
1916
				// If so, we add the table prefix to the column name in 1st segment
1917
				else
1918
				{
1919
					$i = 0;
1920
				}
1921
 
1922
				// This flag is set when the supplied $item does not contain a field name.
1923
				// This can happen when this function is being called from a JOIN.
1924
				if ($field_exists === FALSE)
1925
				{
1926
					$i++;
1927
				}
1928
 
1929
				// Verify table prefix and replace if necessary
2254 lars 1930
				if ($this->swap_pre !== '' && strpos($parts[$i], $this->swap_pre) === 0)
68 lars 1931
				{
2254 lars 1932
					$parts[$i] = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $parts[$i]);
68 lars 1933
				}
1934
				// We only add the table prefix if it does not already exist
2254 lars 1935
				elseif (strpos($parts[$i], $this->dbprefix) !== 0)
68 lars 1936
				{
2254 lars 1937
					$parts[$i] = $this->dbprefix.$parts[$i];
68 lars 1938
				}
1939
 
1940
				// Put the parts back together
1941
				$item = implode('.', $parts);
1942
			}
1943
 
1944
			if ($protect_identifiers === TRUE)
1945
			{
1946
				$item = $this->escape_identifiers($item);
1947
			}
1948
 
1949
			return $item.$alias;
1950
		}
1951
 
1952
		// Is there a table prefix? If not, no need to insert it
1953
		if ($this->dbprefix !== '')
1954
		{
1955
			// Verify table prefix and replace if necessary
1956
			if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)
1957
			{
1958
				$item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item);
1959
			}
1960
			// Do we prefix an item with no segments?
1961
			elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)
1962
			{
1963
				$item = $this->dbprefix.$item;
1964
			}
1965
		}
1966
 
1967
		if ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers))
1968
		{
1969
			$item = $this->escape_identifiers($item);
1970
		}
1971
 
1972
		return $item.$alias;
1973
	}
1974
 
1975
	// --------------------------------------------------------------------
1976
 
1977
	/**
1978
	 * Dummy method that allows Query Builder class to be disabled
1979
	 * and keep count_all() working.
1980
	 *
1981
	 * @return	void
1982
	 */
1983
	protected function _reset_select()
1984
	{
1985
	}
1986
 
1987
}