/lenasys/trunk

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