Subversion-Projekte lars-tiefland.ci

Revision

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

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Query Builder Class
42
 *
43
 * This is the platform-independent base Query Builder implementation class.
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Drivers
47
 * @category	Database
48
 * @author		EllisLab Dev Team
49
 * @link		https://codeigniter.com/user_guide/database/
50
 */
51
 
52
abstract class CI_DB_query_builder extends CI_DB_driver {
53
 
54
	/**
55
	 * Return DELETE SQL flag
56
	 *
57
	 * @var	bool
58
	 */
59
	protected $return_delete_sql		= FALSE;
60
 
61
	/**
62
	 * Reset DELETE data flag
63
	 *
64
	 * @var	bool
65
	 */
66
	protected $reset_delete_data		= FALSE;
67
 
68
	/**
69
	 * QB SELECT data
70
	 *
71
	 * @var	array
72
	 */
73
	protected $qb_select			= array();
74
 
75
	/**
76
	 * QB DISTINCT flag
77
	 *
78
	 * @var	bool
79
	 */
80
	protected $qb_distinct			= FALSE;
81
 
82
	/**
83
	 * QB FROM data
84
	 *
85
	 * @var	array
86
	 */
87
	protected $qb_from			= array();
88
 
89
	/**
90
	 * QB JOIN data
91
	 *
92
	 * @var	array
93
	 */
94
	protected $qb_join			= array();
95
 
96
	/**
97
	 * QB WHERE data
98
	 *
99
	 * @var	array
100
	 */
101
	protected $qb_where			= array();
102
 
103
	/**
104
	 * QB GROUP BY data
105
	 *
106
	 * @var	array
107
	 */
108
	protected $qb_groupby			= array();
109
 
110
	/**
111
	 * QB HAVING data
112
	 *
113
	 * @var	array
114
	 */
115
	protected $qb_having			= array();
116
 
117
	/**
118
	 * QB keys
119
	 *
120
	 * @var	array
121
	 */
122
	protected $qb_keys			= array();
123
 
124
	/**
125
	 * QB LIMIT data
126
	 *
127
	 * @var	int
128
	 */
129
	protected $qb_limit			= FALSE;
130
 
131
	/**
132
	 * QB OFFSET data
133
	 *
134
	 * @var	int
135
	 */
136
	protected $qb_offset			= FALSE;
137
 
138
	/**
139
	 * QB ORDER BY data
140
	 *
141
	 * @var	array
142
	 */
143
	protected $qb_orderby			= array();
144
 
145
	/**
146
	 * QB data sets
147
	 *
148
	 * @var	array
149
	 */
150
	protected $qb_set			= array();
151
 
152
	/**
153
	 * QB aliased tables list
154
	 *
155
	 * @var	array
156
	 */
157
	protected $qb_aliased_tables		= array();
158
 
159
	/**
160
	 * QB WHERE group started flag
161
	 *
162
	 * @var	bool
163
	 */
164
	protected $qb_where_group_started	= FALSE;
165
 
166
	/**
167
	 * QB WHERE group count
168
	 *
169
	 * @var	int
170
	 */
171
	protected $qb_where_group_count		= 0;
172
 
173
	// Query Builder Caching variables
174
 
175
	/**
176
	 * QB Caching flag
177
	 *
178
	 * @var	bool
179
	 */
180
	protected $qb_caching				= FALSE;
181
 
182
	/**
183
	 * QB Cache exists list
184
	 *
185
	 * @var	array
186
	 */
187
	protected $qb_cache_exists			= array();
188
 
189
	/**
190
	 * QB Cache SELECT data
191
	 *
192
	 * @var	array
193
	 */
194
	protected $qb_cache_select			= array();
195
 
196
	/**
197
	 * QB Cache FROM data
198
	 *
199
	 * @var	array
200
	 */
201
	protected $qb_cache_from			= array();
202
 
203
	/**
204
	 * QB Cache JOIN data
205
	 *
206
	 * @var	array
207
	 */
208
	protected $qb_cache_join			= array();
209
 
210
	/**
211
	 * QB Cache WHERE data
212
	 *
213
	 * @var	array
214
	 */
215
	protected $qb_cache_where			= array();
216
 
217
	/**
218
	 * QB Cache GROUP BY data
219
	 *
220
	 * @var	array
221
	 */
222
	protected $qb_cache_groupby			= array();
223
 
224
	/**
225
	 * QB Cache HAVING data
226
	 *
227
	 * @var	array
228
	 */
229
	protected $qb_cache_having			= array();
230
 
231
	/**
232
	 * QB Cache ORDER BY data
233
	 *
234
	 * @var	array
235
	 */
236
	protected $qb_cache_orderby			= array();
237
 
238
	/**
239
	 * QB Cache data sets
240
	 *
241
	 * @var	array
242
	 */
243
	protected $qb_cache_set				= array();
244
 
245
	/**
246
	 * QB No Escape data
247
	 *
248
	 * @var	array
249
	 */
250
	protected $qb_no_escape 			= array();
251
 
252
	/**
253
	 * QB Cache No Escape data
254
	 *
255
	 * @var	array
256
	 */
257
	protected $qb_cache_no_escape			= array();
258
 
259
	// --------------------------------------------------------------------
260
 
261
	/**
262
	 * Select
263
	 *
264
	 * Generates the SELECT portion of the query
265
	 *
266
	 * @param	string
267
	 * @param	mixed
268
	 * @return	CI_DB_query_builder
269
	 */
270
	public function select($select = '*', $escape = NULL)
271
	{
272
		if (is_string($select))
273
		{
274
			$select = explode(',', $select);
275
		}
276
 
277
		// If the escape value was not set, we will base it on the global setting
278
		is_bool($escape) OR $escape = $this->_protect_identifiers;
279
 
280
		foreach ($select as $val)
281
		{
282
			$val = trim($val);
283
 
284
			if ($val !== '')
285
			{
286
				$this->qb_select[] = $val;
287
				$this->qb_no_escape[] = $escape;
288
 
289
				if ($this->qb_caching === TRUE)
290
				{
291
					$this->qb_cache_select[] = $val;
292
					$this->qb_cache_exists[] = 'select';
293
					$this->qb_cache_no_escape[] = $escape;
294
				}
295
			}
296
		}
297
 
298
		return $this;
299
	}
300
 
301
	// --------------------------------------------------------------------
302
 
303
	/**
304
	 * Select Max
305
	 *
306
	 * Generates a SELECT MAX(field) portion of a query
307
	 *
308
	 * @param	string	the field
309
	 * @param	string	an alias
310
	 * @return	CI_DB_query_builder
311
	 */
312
	public function select_max($select = '', $alias = '')
313
	{
314
		return $this->_max_min_avg_sum($select, $alias, 'MAX');
315
	}
316
 
317
	// --------------------------------------------------------------------
318
 
319
	/**
320
	 * Select Min
321
	 *
322
	 * Generates a SELECT MIN(field) portion of a query
323
	 *
324
	 * @param	string	the field
325
	 * @param	string	an alias
326
	 * @return	CI_DB_query_builder
327
	 */
328
	public function select_min($select = '', $alias = '')
329
	{
330
		return $this->_max_min_avg_sum($select, $alias, 'MIN');
331
	}
332
 
333
	// --------------------------------------------------------------------
334
 
335
	/**
336
	 * Select Average
337
	 *
338
	 * Generates a SELECT AVG(field) portion of a query
339
	 *
340
	 * @param	string	the field
341
	 * @param	string	an alias
342
	 * @return	CI_DB_query_builder
343
	 */
344
	public function select_avg($select = '', $alias = '')
345
	{
346
		return $this->_max_min_avg_sum($select, $alias, 'AVG');
347
	}
348
 
349
	// --------------------------------------------------------------------
350
 
351
	/**
352
	 * Select Sum
353
	 *
354
	 * Generates a SELECT SUM(field) portion of a query
355
	 *
356
	 * @param	string	the field
357
	 * @param	string	an alias
358
	 * @return	CI_DB_query_builder
359
	 */
360
	public function select_sum($select = '', $alias = '')
361
	{
362
		return $this->_max_min_avg_sum($select, $alias, 'SUM');
363
	}
364
 
365
	// --------------------------------------------------------------------
366
 
367
	/**
368
	 * SELECT [MAX|MIN|AVG|SUM]()
369
	 *
370
	 * @used-by	select_max()
371
	 * @used-by	select_min()
372
	 * @used-by	select_avg()
373
	 * @used-by	select_sum()
374
	 *
375
	 * @param	string	$select	Field name
376
	 * @param	string	$alias
377
	 * @param	string	$type
378
	 * @return	CI_DB_query_builder
379
	 */
380
	protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
381
	{
382
		if ( ! is_string($select) OR $select === '')
383
		{
384
			$this->display_error('db_invalid_query');
385
		}
386
 
387
		$type = strtoupper($type);
388
 
389
		if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))
390
		{
391
			show_error('Invalid function type: '.$type);
392
		}
393
 
394
		if ($alias === '')
395
		{
396
			$alias = $this->_create_alias_from_table(trim($select));
397
		}
398
 
399
		$sql = $type.'('.$this->protect_identifiers(trim($select)).') AS '.$this->escape_identifiers(trim($alias));
400
 
401
		$this->qb_select[] = $sql;
402
		$this->qb_no_escape[] = NULL;
403
 
404
		if ($this->qb_caching === TRUE)
405
		{
406
			$this->qb_cache_select[] = $sql;
407
			$this->qb_cache_exists[] = 'select';
408
		}
409
 
410
		return $this;
411
	}
412
 
413
	// --------------------------------------------------------------------
414
 
415
	/**
416
	 * Determines the alias name based on the table
417
	 *
418
	 * @param	string	$item
419
	 * @return	string
420
	 */
421
	protected function _create_alias_from_table($item)
422
	{
423
		if (strpos($item, '.') !== FALSE)
424
		{
425
			$item = explode('.', $item);
426
			return end($item);
427
		}
428
 
429
		return $item;
430
	}
431
 
432
	// --------------------------------------------------------------------
433
 
434
	/**
435
	 * DISTINCT
436
	 *
437
	 * Sets a flag which tells the query string compiler to add DISTINCT
438
	 *
439
	 * @param	bool	$val
440
	 * @return	CI_DB_query_builder
441
	 */
442
	public function distinct($val = TRUE)
443
	{
444
		$this->qb_distinct = is_bool($val) ? $val : TRUE;
445
		return $this;
446
	}
447
 
448
	// --------------------------------------------------------------------
449
 
450
	/**
451
	 * From
452
	 *
453
	 * Generates the FROM portion of the query
454
	 *
455
	 * @param	mixed	$from	can be a string or array
456
	 * @return	CI_DB_query_builder
457
	 */
458
	public function from($from)
459
	{
460
		foreach ((array) $from as $val)
461
		{
462
			if (strpos($val, ',') !== FALSE)
463
			{
464
				foreach (explode(',', $val) as $v)
465
				{
466
					$v = trim($v);
467
					$this->_track_aliases($v);
468
 
469
					$this->qb_from[] = $v = $this->protect_identifiers($v, TRUE, NULL, FALSE);
470
 
471
					if ($this->qb_caching === TRUE)
472
					{
473
						$this->qb_cache_from[] = $v;
474
						$this->qb_cache_exists[] = 'from';
475
					}
476
				}
477
			}
478
			else
479
			{
480
				$val = trim($val);
481
 
482
				// Extract any aliases that might exist. We use this information
483
				// in the protect_identifiers to know whether to add a table prefix
484
				$this->_track_aliases($val);
485
 
486
				$this->qb_from[] = $val = $this->protect_identifiers($val, TRUE, NULL, FALSE);
487
 
488
				if ($this->qb_caching === TRUE)
489
				{
490
					$this->qb_cache_from[] = $val;
491
					$this->qb_cache_exists[] = 'from';
492
				}
493
			}
494
		}
495
 
496
		return $this;
497
	}
498
 
499
	// --------------------------------------------------------------------
500
 
501
	/**
502
	 * JOIN
503
	 *
504
	 * Generates the JOIN portion of the query
505
	 *
506
	 * @param	string
507
	 * @param	string	the join condition
508
	 * @param	string	the type of join
509
	 * @param	string	whether not to try to escape identifiers
510
	 * @return	CI_DB_query_builder
511
	 */
512
	public function join($table, $cond, $type = '', $escape = NULL)
513
	{
514
		if ($type !== '')
515
		{
516
			$type = strtoupper(trim($type));
517
 
518
			if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE))
519
			{
520
				$type = '';
521
			}
522
			else
523
			{
524
				$type .= ' ';
525
			}
526
		}
527
 
528
		// Extract any aliases that might exist. We use this information
529
		// in the protect_identifiers to know whether to add a table prefix
530
		$this->_track_aliases($table);
531
 
532
		is_bool($escape) OR $escape = $this->_protect_identifiers;
533
 
534
		if ( ! $this->_has_operator($cond))
535
		{
536
			$cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';
537
		}
538
		elseif ($escape === FALSE)
539
		{
540
			$cond = ' ON '.$cond;
541
		}
542
		else
543
		{
544
			// Split multiple conditions
545
			if (preg_match_all('/\sAND\s|\sOR\s/i', $cond, $joints, PREG_OFFSET_CAPTURE))
546
			{
547
				$conditions = array();
548
				$joints = $joints[0];
549
				array_unshift($joints, array('', 0));
550
 
551
				for ($i = count($joints) - 1, $pos = strlen($cond); $i >= 0; $i--)
552
				{
553
					$joints[$i][1] += strlen($joints[$i][0]); // offset
554
					$conditions[$i] = substr($cond, $joints[$i][1], $pos - $joints[$i][1]);
555
					$pos = $joints[$i][1] - strlen($joints[$i][0]);
556
					$joints[$i] = $joints[$i][0];
557
				}
558
			}
559
			else
560
			{
561
				$conditions = array($cond);
562
				$joints = array('');
563
			}
564
 
565
			$cond = ' ON ';
566
			for ($i = 0, $c = count($conditions); $i < $c; $i++)
567
			{
568
				$operator = $this->_get_operator($conditions[$i]);
569
				$cond .= $joints[$i];
570
				$cond .= preg_match("/(\(*)?([\[\]\w\.'-]+)".preg_quote($operator)."(.*)/i", $conditions[$i], $match)
571
					? $match[1].$this->protect_identifiers($match[2]).$operator.$this->protect_identifiers($match[3])
572
					: $conditions[$i];
573
			}
574
		}
575
 
576
		// Do we want to escape the table name?
577
		if ($escape === TRUE)
578
		{
579
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
580
		}
581
 
582
		// Assemble the JOIN statement
583
		$this->qb_join[] = $join = $type.'JOIN '.$table.$cond;
584
 
585
		if ($this->qb_caching === TRUE)
586
		{
587
			$this->qb_cache_join[] = $join;
588
			$this->qb_cache_exists[] = 'join';
589
		}
590
 
591
		return $this;
592
	}
593
 
594
	// --------------------------------------------------------------------
595
 
596
	/**
597
	 * WHERE
598
	 *
599
	 * Generates the WHERE portion of the query.
600
	 * Separates multiple calls with 'AND'.
601
	 *
602
	 * @param	mixed
603
	 * @param	mixed
604
	 * @param	bool
605
	 * @return	CI_DB_query_builder
606
	 */
607
	public function where($key, $value = NULL, $escape = NULL)
608
	{
609
		return $this->_wh('qb_where', $key, $value, 'AND ', $escape);
610
	}
611
 
612
	// --------------------------------------------------------------------
613
 
614
	/**
615
	 * OR WHERE
616
	 *
617
	 * Generates the WHERE portion of the query.
618
	 * Separates multiple calls with 'OR'.
619
	 *
620
	 * @param	mixed
621
	 * @param	mixed
622
	 * @param	bool
623
	 * @return	CI_DB_query_builder
624
	 */
625
	public function or_where($key, $value = NULL, $escape = NULL)
626
	{
627
		return $this->_wh('qb_where', $key, $value, 'OR ', $escape);
628
	}
629
 
630
	// --------------------------------------------------------------------
631
 
632
	/**
633
	 * WHERE, HAVING
634
	 *
635
	 * @used-by	where()
636
	 * @used-by	or_where()
637
	 * @used-by	having()
638
	 * @used-by	or_having()
639
	 *
640
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
641
	 * @param	mixed	$key
642
	 * @param	mixed	$value
643
	 * @param	string	$type
644
	 * @param	bool	$escape
645
	 * @return	CI_DB_query_builder
646
	 */
647
	protected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL)
648
	{
649
		$qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';
650
 
651
		if ( ! is_array($key))
652
		{
653
			$key = array($key => $value);
654
		}
655
 
656
		// If the escape value was not set will base it on the global setting
657
		is_bool($escape) OR $escape = $this->_protect_identifiers;
658
 
659
		foreach ($key as $k => $v)
660
		{
661
			$prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)
662
				? $this->_group_get_type('')
663
				: $this->_group_get_type($type);
664
 
665
			if ($v !== NULL)
666
			{
667
				if ($escape === TRUE)
668
				{
669
					$v = ' '.$this->escape($v);
670
				}
671
 
672
				if ( ! $this->_has_operator($k))
673
				{
674
					$k .= ' = ';
675
				}
676
			}
677
			elseif ( ! $this->_has_operator($k))
678
			{
679
				// value appears not to have been set, assign the test to IS NULL
680
				$k .= ' IS NULL';
681
			}
1257 lars 682
			elseif (preg_match('/\s*(!?=|<>|\sIS(?:\s+NOT)?\s)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE))
68 lars 683
			{
684
				$k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL');
685
			}
686
 
687
			$this->{$qb_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
688
			if ($this->qb_caching === TRUE)
689
			{
690
				$this->{$qb_cache_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
691
				$this->qb_cache_exists[] = substr($qb_key, 3);
692
			}
693
 
694
		}
695
 
696
		return $this;
697
	}
698
 
699
	// --------------------------------------------------------------------
700
 
701
	/**
702
	 * WHERE IN
703
	 *
704
	 * Generates a WHERE field IN('item', 'item') SQL query,
705
	 * joined with 'AND' if appropriate.
706
	 *
707
	 * @param	string	$key	The field to search
708
	 * @param	array	$values	The values searched on
709
	 * @param	bool	$escape
710
	 * @return	CI_DB_query_builder
711
	 */
712
	public function where_in($key = NULL, $values = NULL, $escape = NULL)
713
	{
714
		return $this->_where_in($key, $values, FALSE, 'AND ', $escape);
715
	}
716
 
717
	// --------------------------------------------------------------------
718
 
719
	/**
720
	 * OR WHERE IN
721
	 *
722
	 * Generates a WHERE field IN('item', 'item') SQL query,
723
	 * joined with 'OR' if appropriate.
724
	 *
725
	 * @param	string	$key	The field to search
726
	 * @param	array	$values	The values searched on
727
	 * @param	bool	$escape
728
	 * @return	CI_DB_query_builder
729
	 */
730
	public function or_where_in($key = NULL, $values = NULL, $escape = NULL)
731
	{
732
		return $this->_where_in($key, $values, FALSE, 'OR ', $escape);
733
	}
734
 
735
	// --------------------------------------------------------------------
736
 
737
	/**
738
	 * WHERE NOT IN
739
	 *
740
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
741
	 * joined with 'AND' if appropriate.
742
	 *
743
	 * @param	string	$key	The field to search
744
	 * @param	array	$values	The values searched on
745
	 * @param	bool	$escape
746
	 * @return	CI_DB_query_builder
747
	 */
748
	public function where_not_in($key = NULL, $values = NULL, $escape = NULL)
749
	{
750
		return $this->_where_in($key, $values, TRUE, 'AND ', $escape);
751
	}
752
 
753
	// --------------------------------------------------------------------
754
 
755
	/**
756
	 * OR WHERE NOT IN
757
	 *
758
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
759
	 * joined with 'OR' if appropriate.
760
	 *
761
	 * @param	string	$key	The field to search
762
	 * @param	array	$values	The values searched on
763
	 * @param	bool	$escape
764
	 * @return	CI_DB_query_builder
765
	 */
766
	public function or_where_not_in($key = NULL, $values = NULL, $escape = NULL)
767
	{
768
		return $this->_where_in($key, $values, TRUE, 'OR ', $escape);
769
	}
770
 
771
	// --------------------------------------------------------------------
772
 
773
	/**
774
	 * Internal WHERE IN
775
	 *
776
	 * @used-by	where_in()
777
	 * @used-by	or_where_in()
778
	 * @used-by	where_not_in()
779
	 * @used-by	or_where_not_in()
780
	 *
781
	 * @param	string	$key	The field to search
782
	 * @param	array	$values	The values searched on
783
	 * @param	bool	$not	If the statement would be IN or NOT IN
784
	 * @param	string	$type
785
	 * @param	bool	$escape
786
	 * @return	CI_DB_query_builder
787
	 */
788
	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ', $escape = NULL)
789
	{
790
		if ($key === NULL OR $values === NULL)
791
		{
792
			return $this;
793
		}
794
 
795
		if ( ! is_array($values))
796
		{
797
			$values = array($values);
798
		}
799
 
800
		is_bool($escape) OR $escape = $this->_protect_identifiers;
801
 
802
		$not = ($not) ? ' NOT' : '';
803
 
804
		if ($escape === TRUE)
805
		{
806
			$where_in = array();
807
			foreach ($values as $value)
808
			{
809
				$where_in[] = $this->escape($value);
810
			}
811
		}
812
		else
813
		{
814
			$where_in = array_values($values);
815
		}
816
 
817
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
818
			? $this->_group_get_type('')
819
			: $this->_group_get_type($type);
820
 
821
		$where_in = array(
822
			'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')',
823
			'escape' => $escape
824
		);
825
 
826
		$this->qb_where[] = $where_in;
827
		if ($this->qb_caching === TRUE)
828
		{
829
			$this->qb_cache_where[] = $where_in;
830
			$this->qb_cache_exists[] = 'where';
831
		}
832
 
833
		return $this;
834
	}
835
 
836
	// --------------------------------------------------------------------
837
 
838
	/**
839
	 * LIKE
840
	 *
841
	 * Generates a %LIKE% portion of the query.
842
	 * Separates multiple calls with 'AND'.
843
	 *
844
	 * @param	mixed	$field
845
	 * @param	string	$match
846
	 * @param	string	$side
847
	 * @param	bool	$escape
848
	 * @return	CI_DB_query_builder
849
	 */
850
	public function like($field, $match = '', $side = 'both', $escape = NULL)
851
	{
852
		return $this->_like($field, $match, 'AND ', $side, '', $escape);
853
	}
854
 
855
	// --------------------------------------------------------------------
856
 
857
	/**
858
	 * NOT LIKE
859
	 *
860
	 * Generates a NOT LIKE portion of the query.
861
	 * Separates multiple calls with 'AND'.
862
	 *
863
	 * @param	mixed	$field
864
	 * @param	string	$match
865
	 * @param	string	$side
866
	 * @param	bool	$escape
867
	 * @return	CI_DB_query_builder
868
	 */
869
	public function not_like($field, $match = '', $side = 'both', $escape = NULL)
870
	{
871
		return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape);
872
	}
873
 
874
	// --------------------------------------------------------------------
875
 
876
	/**
877
	 * OR LIKE
878
	 *
879
	 * Generates a %LIKE% portion of the query.
880
	 * Separates multiple calls with 'OR'.
881
	 *
882
	 * @param	mixed	$field
883
	 * @param	string	$match
884
	 * @param	string	$side
885
	 * @param	bool	$escape
886
	 * @return	CI_DB_query_builder
887
	 */
888
	public function or_like($field, $match = '', $side = 'both', $escape = NULL)
889
	{
890
		return $this->_like($field, $match, 'OR ', $side, '', $escape);
891
	}
892
 
893
	// --------------------------------------------------------------------
894
 
895
	/**
896
	 * OR NOT LIKE
897
	 *
898
	 * Generates a NOT LIKE portion of the query.
899
	 * Separates multiple calls with 'OR'.
900
	 *
901
	 * @param	mixed	$field
902
	 * @param	string	$match
903
	 * @param	string	$side
904
	 * @param	bool	$escape
905
	 * @return	CI_DB_query_builder
906
	 */
907
	public function or_not_like($field, $match = '', $side = 'both', $escape = NULL)
908
	{
909
		return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape);
910
	}
911
 
912
	// --------------------------------------------------------------------
913
 
914
	/**
915
	 * Internal LIKE
916
	 *
917
	 * @used-by	like()
918
	 * @used-by	or_like()
919
	 * @used-by	not_like()
920
	 * @used-by	or_not_like()
921
	 *
922
	 * @param	mixed	$field
923
	 * @param	string	$match
924
	 * @param	string	$type
925
	 * @param	string	$side
926
	 * @param	string	$not
927
	 * @param	bool	$escape
928
	 * @return	CI_DB_query_builder
929
	 */
930
	protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)
931
	{
932
		if ( ! is_array($field))
933
		{
934
			$field = array($field => $match);
935
		}
936
 
937
		is_bool($escape) OR $escape = $this->_protect_identifiers;
938
		// lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)
939
		$side = strtolower($side);
940
 
941
		foreach ($field as $k => $v)
942
		{
943
			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
944
				? $this->_group_get_type('') : $this->_group_get_type($type);
945
 
946
			if ($escape === TRUE)
947
			{
948
				$v = $this->escape_like_str($v);
949
			}
950
 
951
			if ($side === 'none')
952
			{
953
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}'";
954
			}
955
			elseif ($side === 'before')
956
			{
957
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}'";
958
			}
959
			elseif ($side === 'after')
960
			{
961
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}%'";
962
			}
963
			else
964
			{
965
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}%'";
966
			}
967
 
968
			// some platforms require an escape sequence definition for LIKE wildcards
969
			if ($escape === TRUE && $this->_like_escape_str !== '')
970
			{
971
				$like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr);
972
			}
973
 
974
			$this->qb_where[] = array('condition' => $like_statement, 'escape' => $escape);
975
			if ($this->qb_caching === TRUE)
976
			{
977
				$this->qb_cache_where[] = array('condition' => $like_statement, 'escape' => $escape);
978
				$this->qb_cache_exists[] = 'where';
979
			}
980
		}
981
 
982
		return $this;
983
	}
984
 
985
	// --------------------------------------------------------------------
986
 
987
	/**
988
	 * Starts a query group.
989
	 *
990
	 * @param	string	$not	(Internal use only)
991
	 * @param	string	$type	(Internal use only)
992
	 * @return	CI_DB_query_builder
993
	 */
994
	public function group_start($not = '', $type = 'AND ')
995
	{
996
		$type = $this->_group_get_type($type);
997
 
998
		$this->qb_where_group_started = TRUE;
999
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
1000
		$where = array(
1001
			'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (',
1002
			'escape' => FALSE
1003
		);
1004
 
1005
		$this->qb_where[] = $where;
1006
		if ($this->qb_caching)
1007
		{
1008
			$this->qb_cache_where[] = $where;
1009
		}
1010
 
1011
		return $this;
1012
	}
1013
 
1014
	// --------------------------------------------------------------------
1015
 
1016
	/**
1017
	 * Starts a query group, but ORs the group
1018
	 *
1019
	 * @return	CI_DB_query_builder
1020
	 */
1021
	public function or_group_start()
1022
	{
1023
		return $this->group_start('', 'OR ');
1024
	}
1025
 
1026
	// --------------------------------------------------------------------
1027
 
1028
	/**
1029
	 * Starts a query group, but NOTs the group
1030
	 *
1031
	 * @return	CI_DB_query_builder
1032
	 */
1033
	public function not_group_start()
1034
	{
1035
		return $this->group_start('NOT ', 'AND ');
1036
	}
1037
 
1038
	// --------------------------------------------------------------------
1039
 
1040
	/**
1041
	 * Starts a query group, but OR NOTs the group
1042
	 *
1043
	 * @return	CI_DB_query_builder
1044
	 */
1045
	public function or_not_group_start()
1046
	{
1047
		return $this->group_start('NOT ', 'OR ');
1048
	}
1049
 
1050
	// --------------------------------------------------------------------
1051
 
1052
	/**
1053
	 * Ends a query group
1054
	 *
1055
	 * @return	CI_DB_query_builder
1056
	 */
1057
	public function group_end()
1058
	{
1059
		$this->qb_where_group_started = FALSE;
1060
		$where = array(
1061
			'condition' => str_repeat(' ', $this->qb_where_group_count--).')',
1062
			'escape' => FALSE
1063
		);
1064
 
1065
		$this->qb_where[] = $where;
1066
		if ($this->qb_caching)
1067
		{
1068
			$this->qb_cache_where[] = $where;
1069
		}
1070
 
1071
		return $this;
1072
	}
1073
 
1074
	// --------------------------------------------------------------------
1075
 
1076
	/**
1077
	 * Group_get_type
1078
	 *
1079
	 * @used-by	group_start()
1080
	 * @used-by	_like()
1081
	 * @used-by	_wh()
1082
	 * @used-by	_where_in()
1083
	 *
1084
	 * @param	string	$type
1085
	 * @return	string
1086
	 */
1087
	protected function _group_get_type($type)
1088
	{
1089
		if ($this->qb_where_group_started)
1090
		{
1091
			$type = '';
1092
			$this->qb_where_group_started = FALSE;
1093
		}
1094
 
1095
		return $type;
1096
	}
1097
 
1098
	// --------------------------------------------------------------------
1099
 
1100
	/**
1101
	 * GROUP BY
1102
	 *
1103
	 * @param	string	$by
1104
	 * @param	bool	$escape
1105
	 * @return	CI_DB_query_builder
1106
	 */
1107
	public function group_by($by, $escape = NULL)
1108
	{
1109
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1110
 
1111
		if (is_string($by))
1112
		{
1113
			$by = ($escape === TRUE)
1114
				? explode(',', $by)
1115
				: array($by);
1116
		}
1117
 
1118
		foreach ($by as $val)
1119
		{
1120
			$val = trim($val);
1121
 
1122
			if ($val !== '')
1123
			{
1124
				$val = array('field' => $val, 'escape' => $escape);
1125
 
1126
				$this->qb_groupby[] = $val;
1127
				if ($this->qb_caching === TRUE)
1128
				{
1129
					$this->qb_cache_groupby[] = $val;
1130
					$this->qb_cache_exists[] = 'groupby';
1131
				}
1132
			}
1133
		}
1134
 
1135
		return $this;
1136
	}
1137
 
1138
	// --------------------------------------------------------------------
1139
 
1140
	/**
1141
	 * HAVING
1142
	 *
1143
	 * Separates multiple calls with 'AND'.
1144
	 *
1145
	 * @param	string	$key
1146
	 * @param	string	$value
1147
	 * @param	bool	$escape
1148
	 * @return	CI_DB_query_builder
1149
	 */
1150
	public function having($key, $value = NULL, $escape = NULL)
1151
	{
1152
		return $this->_wh('qb_having', $key, $value, 'AND ', $escape);
1153
	}
1154
 
1155
	// --------------------------------------------------------------------
1156
 
1157
	/**
1158
	 * OR HAVING
1159
	 *
1160
	 * Separates multiple calls with 'OR'.
1161
	 *
1162
	 * @param	string	$key
1163
	 * @param	string	$value
1164
	 * @param	bool	$escape
1165
	 * @return	CI_DB_query_builder
1166
	 */
1167
	public function or_having($key, $value = NULL, $escape = NULL)
1168
	{
1169
		return $this->_wh('qb_having', $key, $value, 'OR ', $escape);
1170
	}
1171
 
1172
	// --------------------------------------------------------------------
1173
 
1174
	/**
1175
	 * ORDER BY
1176
	 *
1177
	 * @param	string	$orderby
1178
	 * @param	string	$direction	ASC, DESC or RANDOM
1179
	 * @param	bool	$escape
1180
	 * @return	CI_DB_query_builder
1181
	 */
1182
	public function order_by($orderby, $direction = '', $escape = NULL)
1183
	{
1184
		$direction = strtoupper(trim($direction));
1185
 
1186
		if ($direction === 'RANDOM')
1187
		{
1188
			$direction = '';
1189
 
1190
			// Do we have a seed value?
1191
			$orderby = ctype_digit((string) $orderby)
1192
				? sprintf($this->_random_keyword[1], $orderby)
1193
				: $this->_random_keyword[0];
1194
		}
1195
		elseif (empty($orderby))
1196
		{
1197
			return $this;
1198
		}
1199
		elseif ($direction !== '')
1200
		{
1201
			$direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
1202
		}
1203
 
1204
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1205
 
1206
		if ($escape === FALSE)
1207
		{
1208
			$qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE);
1209
		}
1210
		else
1211
		{
1212
			$qb_orderby = array();
1213
			foreach (explode(',', $orderby) as $field)
1214
			{
1215
				$qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE))
1216
					? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE)
1217
					: array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE);
1218
			}
1219
		}
1220
 
1221
		$this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby);
1222
		if ($this->qb_caching === TRUE)
1223
		{
1224
			$this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby);
1225
			$this->qb_cache_exists[] = 'orderby';
1226
		}
1227
 
1228
		return $this;
1229
	}
1230
 
1231
	// --------------------------------------------------------------------
1232
 
1233
	/**
1234
	 * LIMIT
1235
	 *
1236
	 * @param	int	$value	LIMIT value
1237
	 * @param	int	$offset	OFFSET value
1238
	 * @return	CI_DB_query_builder
1239
	 */
1240
	public function limit($value, $offset = 0)
1241
	{
1242
		is_null($value) OR $this->qb_limit = (int) $value;
1243
		empty($offset) OR $this->qb_offset = (int) $offset;
1244
 
1245
		return $this;
1246
	}
1247
 
1248
	// --------------------------------------------------------------------
1249
 
1250
	/**
1251
	 * Sets the OFFSET value
1252
	 *
1253
	 * @param	int	$offset	OFFSET value
1254
	 * @return	CI_DB_query_builder
1255
	 */
1256
	public function offset($offset)
1257
	{
1258
		empty($offset) OR $this->qb_offset = (int) $offset;
1259
		return $this;
1260
	}
1261
 
1262
	// --------------------------------------------------------------------
1263
 
1264
	/**
1265
	 * LIMIT string
1266
	 *
1267
	 * Generates a platform-specific LIMIT clause.
1268
	 *
1269
	 * @param	string	$sql	SQL Query
1270
	 * @return	string
1271
	 */
1272
	protected function _limit($sql)
1273
	{
1257 lars 1274
		return $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').(int) $this->qb_limit;
68 lars 1275
	}
1276
 
1277
	// --------------------------------------------------------------------
1278
 
1279
	/**
1280
	 * The "set" function.
1281
	 *
1282
	 * Allows key/value pairs to be set for inserting or updating
1283
	 *
1284
	 * @param	mixed
1285
	 * @param	string
1286
	 * @param	bool
1287
	 * @return	CI_DB_query_builder
1288
	 */
1289
	public function set($key, $value = '', $escape = NULL)
1290
	{
1291
		$key = $this->_object_to_array($key);
1292
 
1293
		if ( ! is_array($key))
1294
		{
1295
			$key = array($key => $value);
1296
		}
1297
 
1298
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1299
 
1300
		foreach ($key as $k => $v)
1301
		{
1302
			$this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
1303
				? $this->escape($v) : $v;
1304
		}
1305
 
1306
		return $this;
1307
	}
1308
 
1309
	// --------------------------------------------------------------------
1310
 
1311
	/**
1312
	 * Get SELECT query string
1313
	 *
1314
	 * Compiles a SELECT query string and returns the sql.
1315
	 *
1316
	 * @param	string	the table name to select from (optional)
1317
	 * @param	bool	TRUE: resets QB values; FALSE: leave QB values alone
1318
	 * @return	string
1319
	 */
1320
	public function get_compiled_select($table = '', $reset = TRUE)
1321
	{
1322
		if ($table !== '')
1323
		{
1324
			$this->_track_aliases($table);
1325
			$this->from($table);
1326
		}
1327
 
1328
		$select = $this->_compile_select();
1329
 
1330
		if ($reset === TRUE)
1331
		{
1332
			$this->_reset_select();
1333
		}
1334
 
1335
		return $select;
1336
	}
1337
 
1338
	// --------------------------------------------------------------------
1339
 
1340
	/**
1341
	 * Get
1342
	 *
1343
	 * Compiles the select statement based on the other functions called
1344
	 * and runs the query
1345
	 *
1346
	 * @param	string	the table
1347
	 * @param	string	the limit clause
1348
	 * @param	string	the offset clause
1349
	 * @return	CI_DB_result
1350
	 */
1351
	public function get($table = '', $limit = NULL, $offset = NULL)
1352
	{
1353
		if ($table !== '')
1354
		{
1355
			$this->_track_aliases($table);
1356
			$this->from($table);
1357
		}
1358
 
1359
		if ( ! empty($limit))
1360
		{
1361
			$this->limit($limit, $offset);
1362
		}
1363
 
1364
		$result = $this->query($this->_compile_select());
1365
		$this->_reset_select();
1366
		return $result;
1367
	}
1368
 
1369
	// --------------------------------------------------------------------
1370
 
1371
	/**
1372
	 * "Count All Results" query
1373
	 *
1374
	 * Generates a platform-specific query string that counts all records
1375
	 * returned by an Query Builder query.
1376
	 *
1377
	 * @param	string
1378
	 * @param	bool	the reset clause
1379
	 * @return	int
1380
	 */
1381
	public function count_all_results($table = '', $reset = TRUE)
1382
	{
1383
		if ($table !== '')
1384
		{
1385
			$this->_track_aliases($table);
1386
			$this->from($table);
1387
		}
1388
 
1389
		// ORDER BY usage is often problematic here (most notably
1390
		// on Microsoft SQL Server) and ultimately unnecessary
1391
		// for selecting COUNT(*) ...
1392
		if ( ! empty($this->qb_orderby))
1393
		{
1394
			$orderby = $this->qb_orderby;
1395
			$this->qb_orderby = NULL;
1396
		}
1397
 
1398
		$result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby))
1399
			? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results")
1400
			: $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows')));
1401
 
1402
		if ($reset === TRUE)
1403
		{
1404
			$this->_reset_select();
1405
		}
1406
		// If we've previously reset the qb_orderby values, get them back
1407
		elseif ( ! isset($this->qb_orderby))
1408
		{
1409
			$this->qb_orderby = $orderby;
1410
		}
1411
 
1412
		if ($result->num_rows() === 0)
1413
		{
1414
			return 0;
1415
		}
1416
 
1417
		$row = $result->row();
1418
		return (int) $row->numrows;
1419
	}
1420
 
1421
	// --------------------------------------------------------------------
1422
 
1423
	/**
1424
	 * Get_Where
1425
	 *
1426
	 * Allows the where clause, limit and offset to be added directly
1427
	 *
1428
	 * @param	string	$table
1429
	 * @param	string	$where
1430
	 * @param	int	$limit
1431
	 * @param	int	$offset
1432
	 * @return	CI_DB_result
1433
	 */
1434
	public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)
1435
	{
1436
		if ($table !== '')
1437
		{
1438
			$this->from($table);
1439
		}
1440
 
1441
		if ($where !== NULL)
1442
		{
1443
			$this->where($where);
1444
		}
1445
 
1446
		if ( ! empty($limit))
1447
		{
1448
			$this->limit($limit, $offset);
1449
		}
1450
 
1451
		$result = $this->query($this->_compile_select());
1452
		$this->_reset_select();
1453
		return $result;
1454
	}
1455
 
1456
	// --------------------------------------------------------------------
1457
 
1458
	/**
1459
	 * Insert_Batch
1460
	 *
1461
	 * Compiles batch insert strings and runs the queries
1462
	 *
1463
	 * @param	string	$table	Table to insert into
1464
	 * @param	array	$set 	An associative array of insert values
1465
	 * @param	bool	$escape	Whether to escape values and identifiers
1466
	 * @return	int	Number of rows inserted or FALSE on failure
1467
	 */
1468
	public function insert_batch($table, $set = NULL, $escape = NULL, $batch_size = 100)
1469
	{
1470
		if ($set === NULL)
1471
		{
1472
			if (empty($this->qb_set))
1473
			{
1474
				return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1475
			}
1476
		}
1477
		else
1478
		{
1479
			if (empty($set))
1480
			{
1481
				return ($this->db_debug) ? $this->display_error('insert_batch() called with no data') : FALSE;
1482
			}
1483
 
1484
			$this->set_insert_batch($set, '', $escape);
1485
		}
1486
 
1487
		if (strlen($table) === 0)
1488
		{
1489
			if ( ! isset($this->qb_from[0]))
1490
			{
1491
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1492
			}
1493
 
1494
			$table = $this->qb_from[0];
1495
		}
1496
 
1497
		// Batch this baby
1498
		$affected_rows = 0;
1499
		for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size)
1500
		{
1501
			if ($this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size))))
1502
			{
1503
				$affected_rows += $this->affected_rows();
1504
			}
1505
		}
1506
 
1507
		$this->_reset_write();
1508
		return $affected_rows;
1509
	}
1510
 
1511
	// --------------------------------------------------------------------
1512
 
1513
	/**
1514
	 * Insert batch statement
1515
	 *
1516
	 * Generates a platform-specific insert string from the supplied data.
1517
	 *
1518
	 * @param	string	$table	Table name
1519
	 * @param	array	$keys	INSERT keys
1520
	 * @param	array	$values	INSERT values
1521
	 * @return	string
1522
	 */
1523
	protected function _insert_batch($table, $keys, $values)
1524
	{
1525
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
1526
	}
1527
 
1528
	// --------------------------------------------------------------------
1529
 
1530
	/**
1531
	 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
1532
	 *
1533
	 * @param	mixed
1534
	 * @param	string
1535
	 * @param	bool
1536
	 * @return	CI_DB_query_builder
1537
	 */
1538
	public function set_insert_batch($key, $value = '', $escape = NULL)
1539
	{
1540
		$key = $this->_object_to_array_batch($key);
1541
 
1542
		if ( ! is_array($key))
1543
		{
1544
			$key = array($key => $value);
1545
		}
1546
 
1547
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1548
 
1549
		$keys = array_keys($this->_object_to_array(current($key)));
1550
		sort($keys);
1551
 
1552
		foreach ($key as $row)
1553
		{
1554
			$row = $this->_object_to_array($row);
1555
			if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
1556
			{
1557
				// batch function above returns an error on an empty array
1558
				$this->qb_set[] = array();
1559
				return;
1560
			}
1561
 
1562
			ksort($row); // puts $row in the same order as our keys
1563
 
1564
			if ($escape !== FALSE)
1565
			{
1566
				$clean = array();
1567
				foreach ($row as $value)
1568
				{
1569
					$clean[] = $this->escape($value);
1570
				}
1571
 
1572
				$row = $clean;
1573
			}
1574
 
1575
			$this->qb_set[] = '('.implode(',', $row).')';
1576
		}
1577
 
1578
		foreach ($keys as $k)
1579
		{
1580
			$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
1581
		}
1582
 
1583
		return $this;
1584
	}
1585
 
1586
	// --------------------------------------------------------------------
1587
 
1588
	/**
1589
	 * Get INSERT query string
1590
	 *
1591
	 * Compiles an insert query and returns the sql
1592
	 *
1593
	 * @param	string	the table to insert into
1594
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1595
	 * @return	string
1596
	 */
1597
	public function get_compiled_insert($table = '', $reset = TRUE)
1598
	{
1599
		if ($this->_validate_insert($table) === FALSE)
1600
		{
1601
			return FALSE;
1602
		}
1603
 
1604
		$sql = $this->_insert(
1605
			$this->protect_identifiers(
1606
				$this->qb_from[0], TRUE, NULL, FALSE
1607
			),
1608
			array_keys($this->qb_set),
1609
			array_values($this->qb_set)
1610
		);
1611
 
1612
		if ($reset === TRUE)
1613
		{
1614
			$this->_reset_write();
1615
		}
1616
 
1617
		return $sql;
1618
	}
1619
 
1620
	// --------------------------------------------------------------------
1621
 
1622
	/**
1623
	 * Insert
1624
	 *
1625
	 * Compiles an insert string and runs the query
1626
	 *
1627
	 * @param	string	the table to insert data into
1628
	 * @param	array	an associative array of insert values
1629
	 * @param	bool	$escape	Whether to escape values and identifiers
1630
	 * @return	bool	TRUE on success, FALSE on failure
1631
	 */
1632
	public function insert($table = '', $set = NULL, $escape = NULL)
1633
	{
1634
		if ($set !== NULL)
1635
		{
1636
			$this->set($set, '', $escape);
1637
		}
1638
 
1639
		if ($this->_validate_insert($table) === FALSE)
1640
		{
1641
			return FALSE;
1642
		}
1643
 
1644
		$sql = $this->_insert(
1645
			$this->protect_identifiers(
1646
				$this->qb_from[0], TRUE, $escape, FALSE
1647
			),
1648
			array_keys($this->qb_set),
1649
			array_values($this->qb_set)
1650
		);
1651
 
1652
		$this->_reset_write();
1653
		return $this->query($sql);
1654
	}
1655
 
1656
	// --------------------------------------------------------------------
1657
 
1658
	/**
1659
	 * Validate Insert
1660
	 *
1661
	 * This method is used by both insert() and get_compiled_insert() to
1662
	 * validate that the there data is actually being set and that table
1663
	 * has been chosen to be inserted into.
1664
	 *
1665
	 * @param	string	the table to insert data into
1666
	 * @return	string
1667
	 */
1668
	protected function _validate_insert($table = '')
1669
	{
1670
		if (count($this->qb_set) === 0)
1671
		{
1672
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1673
		}
1674
 
1675
		if ($table !== '')
1676
		{
1677
			$this->qb_from[0] = $table;
1678
		}
1679
		elseif ( ! isset($this->qb_from[0]))
1680
		{
1681
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1682
		}
1683
 
1684
		return TRUE;
1685
	}
1686
 
1687
	// --------------------------------------------------------------------
1688
 
1689
	/**
1690
	 * Replace
1691
	 *
1692
	 * Compiles an replace into string and runs the query
1693
	 *
1694
	 * @param	string	the table to replace data into
1695
	 * @param	array	an associative array of insert values
1696
	 * @return	bool	TRUE on success, FALSE on failure
1697
	 */
1698
	public function replace($table = '', $set = NULL)
1699
	{
1700
		if ($set !== NULL)
1701
		{
1702
			$this->set($set);
1703
		}
1704
 
1705
		if (count($this->qb_set) === 0)
1706
		{
1707
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1708
		}
1709
 
1710
		if ($table === '')
1711
		{
1712
			if ( ! isset($this->qb_from[0]))
1713
			{
1714
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1715
			}
1716
 
1717
			$table = $this->qb_from[0];
1718
		}
1719
 
1720
		$sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set));
1721
 
1722
		$this->_reset_write();
1723
		return $this->query($sql);
1724
	}
1725
 
1726
	// --------------------------------------------------------------------
1727
 
1728
	/**
1729
	 * Replace statement
1730
	 *
1731
	 * Generates a platform-specific replace string from the supplied data
1732
	 *
1733
	 * @param	string	the table name
1734
	 * @param	array	the insert keys
1735
	 * @param	array	the insert values
1736
	 * @return	string
1737
	 */
1738
	protected function _replace($table, $keys, $values)
1739
	{
1740
		return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1741
	}
1742
 
1743
	// --------------------------------------------------------------------
1744
 
1745
	/**
1746
	 * FROM tables
1747
	 *
1748
	 * Groups tables in FROM clauses if needed, so there is no confusion
1749
	 * about operator precedence.
1750
	 *
1751
	 * Note: This is only used (and overridden) by MySQL and CUBRID.
1752
	 *
1753
	 * @return	string
1754
	 */
1755
	protected function _from_tables()
1756
	{
1757
		return implode(', ', $this->qb_from);
1758
	}
1759
 
1760
	// --------------------------------------------------------------------
1761
 
1762
	/**
1763
	 * Get UPDATE query string
1764
	 *
1765
	 * Compiles an update query and returns the sql
1766
	 *
1767
	 * @param	string	the table to update
1768
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1769
	 * @return	string
1770
	 */
1771
	public function get_compiled_update($table = '', $reset = TRUE)
1772
	{
1773
		// Combine any cached components with the current statements
1774
		$this->_merge_cache();
1775
 
1776
		if ($this->_validate_update($table) === FALSE)
1777
		{
1778
			return FALSE;
1779
		}
1780
 
1781
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1782
 
1783
		if ($reset === TRUE)
1784
		{
1785
			$this->_reset_write();
1786
		}
1787
 
1788
		return $sql;
1789
	}
1790
 
1791
	// --------------------------------------------------------------------
1792
 
1793
	/**
1794
	 * UPDATE
1795
	 *
1796
	 * Compiles an update string and runs the query.
1797
	 *
1798
	 * @param	string	$table
1799
	 * @param	array	$set	An associative array of update values
1800
	 * @param	mixed	$where
1801
	 * @param	int	$limit
1802
	 * @return	bool	TRUE on success, FALSE on failure
1803
	 */
1804
	public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
1805
	{
1806
		// Combine any cached components with the current statements
1807
		$this->_merge_cache();
1808
 
1809
		if ($set !== NULL)
1810
		{
1811
			$this->set($set);
1812
		}
1813
 
1814
		if ($this->_validate_update($table) === FALSE)
1815
		{
1816
			return FALSE;
1817
		}
1818
 
1819
		if ($where !== NULL)
1820
		{
1821
			$this->where($where);
1822
		}
1823
 
1824
		if ( ! empty($limit))
1825
		{
1826
			$this->limit($limit);
1827
		}
1828
 
1829
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1830
		$this->_reset_write();
1831
		return $this->query($sql);
1832
	}
1833
 
1834
	// --------------------------------------------------------------------
1835
 
1836
	/**
1837
	 * Validate Update
1838
	 *
1839
	 * This method is used by both update() and get_compiled_update() to
1840
	 * validate that data is actually being set and that a table has been
1841
	 * chosen to be update.
1842
	 *
1843
	 * @param	string	the table to update data on
1844
	 * @return	bool
1845
	 */
1846
	protected function _validate_update($table)
1847
	{
1848
		if (count($this->qb_set) === 0)
1849
		{
1850
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1851
		}
1852
 
1853
		if ($table !== '')
1854
		{
1855
			$this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE));
1856
		}
1857
		elseif ( ! isset($this->qb_from[0]))
1858
		{
1859
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1860
		}
1861
 
1862
		return TRUE;
1863
	}
1864
 
1865
	// --------------------------------------------------------------------
1866
 
1867
	/**
1868
	 * Update_Batch
1869
	 *
1870
	 * Compiles an update string and runs the query
1871
	 *
1872
	 * @param	string	the table to retrieve the results from
1873
	 * @param	array	an associative array of update values
1874
	 * @param	string	the where key
1875
	 * @return	int	number of rows affected or FALSE on failure
1876
	 */
1877
	public function update_batch($table, $set = NULL, $index = NULL, $batch_size = 100)
1878
	{
1879
		// Combine any cached components with the current statements
1880
		$this->_merge_cache();
1881
 
1882
		if ($index === NULL)
1883
		{
1884
			return ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE;
1885
		}
1886
 
1887
		if ($set === NULL)
1888
		{
1889
			if (empty($this->qb_set))
1890
			{
1891
				return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1892
			}
1893
		}
1894
		else
1895
		{
1896
			if (empty($set))
1897
			{
1898
				return ($this->db_debug) ? $this->display_error('update_batch() called with no data') : FALSE;
1899
			}
1900
 
1901
			$this->set_update_batch($set, $index);
1902
		}
1903
 
1904
		if (strlen($table) === 0)
1905
		{
1906
			if ( ! isset($this->qb_from[0]))
1907
			{
1908
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1909
			}
1910
 
1911
			$table = $this->qb_from[0];
1912
		}
1913
 
1914
		// Batch this baby
1915
		$affected_rows = 0;
1916
		for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size)
1917
		{
1257 lars 1918
			if ($this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set, $i, $batch_size), $index)))
68 lars 1919
			{
1920
				$affected_rows += $this->affected_rows();
1921
			}
1922
 
1923
			$this->qb_where = array();
1924
		}
1925
 
1926
		$this->_reset_write();
1927
		return $affected_rows;
1928
	}
1929
 
1930
	// --------------------------------------------------------------------
1931
 
1932
	/**
1933
	 * Update_Batch statement
1934
	 *
1935
	 * Generates a platform-specific batch update string from the supplied data
1936
	 *
1937
	 * @param	string	$table	Table name
1938
	 * @param	array	$values	Update data
1939
	 * @param	string	$index	WHERE key
1940
	 * @return	string
1941
	 */
1942
	protected function _update_batch($table, $values, $index)
1943
	{
1257 lars 1944
		$index_escaped = $this->protect_identifiers($index);
1945
 
68 lars 1946
		$ids = array();
1947
		foreach ($values as $key => $val)
1948
		{
1949
			$ids[] = $val[$index];
1950
 
1951
			foreach (array_keys($val) as $field)
1952
			{
1953
				if ($field !== $index)
1954
				{
1257 lars 1955
					$final[$field][] = 'WHEN '.$index_escaped.' = '.$val[$index].' THEN '.$val[$field];
68 lars 1956
				}
1957
			}
1958
		}
1959
 
1960
		$cases = '';
1961
		foreach ($final as $k => $v)
1962
		{
1963
			$cases .= $k." = CASE \n"
1964
				.implode("\n", $v)."\n"
1965
				.'ELSE '.$k.' END, ';
1966
		}
1967
 
1257 lars 1968
		$this->where($index_escaped.' IN('.implode(',', $ids).')', NULL, FALSE);
68 lars 1969
 
1970
		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
1971
	}
1972
 
1973
	// --------------------------------------------------------------------
1974
 
1975
	/**
1976
	 * The "set_update_batch" function.  Allows key/value pairs to be set for batch updating
1977
	 *
1978
	 * @param	array
1979
	 * @param	string
1980
	 * @param	bool
1981
	 * @return	CI_DB_query_builder
1982
	 */
1983
	public function set_update_batch($key, $index = '', $escape = NULL)
1984
	{
1985
		$key = $this->_object_to_array_batch($key);
1986
 
1987
		if ( ! is_array($key))
1988
		{
1989
			// @todo error
1990
		}
1991
 
1992
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1993
 
1994
		foreach ($key as $k => $v)
1995
		{
1996
			$index_set = FALSE;
1997
			$clean = array();
1998
			foreach ($v as $k2 => $v2)
1999
			{
2000
				if ($k2 === $index)
2001
				{
2002
					$index_set = TRUE;
2003
				}
2004
 
2005
				$clean[$this->protect_identifiers($k2, FALSE, $escape)] = ($escape === FALSE) ? $v2 : $this->escape($v2);
2006
			}
2007
 
2008
			if ($index_set === FALSE)
2009
			{
2010
				return $this->display_error('db_batch_missing_index');
2011
			}
2012
 
2013
			$this->qb_set[] = $clean;
2014
		}
2015
 
2016
		return $this;
2017
	}
2018
 
2019
	// --------------------------------------------------------------------
2020
 
2021
	/**
2022
	 * Empty Table
2023
	 *
2024
	 * Compiles a delete string and runs "DELETE FROM table"
2025
	 *
2026
	 * @param	string	the table to empty
2027
	 * @return	bool	TRUE on success, FALSE on failure
2028
	 */
2029
	public function empty_table($table = '')
2030
	{
2031
		if ($table === '')
2032
		{
2033
			if ( ! isset($this->qb_from[0]))
2034
			{
2035
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2036
			}
2037
 
2038
			$table = $this->qb_from[0];
2039
		}
2040
		else
2041
		{
2042
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2043
		}
2044
 
2045
		$sql = $this->_delete($table);
2046
		$this->_reset_write();
2047
		return $this->query($sql);
2048
	}
2049
 
2050
	// --------------------------------------------------------------------
2051
 
2052
	/**
2053
	 * Truncate
2054
	 *
2055
	 * Compiles a truncate string and runs the query
2056
	 * If the database does not support the truncate() command
2057
	 * This function maps to "DELETE FROM table"
2058
	 *
2059
	 * @param	string	the table to truncate
2060
	 * @return	bool	TRUE on success, FALSE on failure
2061
	 */
2062
	public function truncate($table = '')
2063
	{
2064
		if ($table === '')
2065
		{
2066
			if ( ! isset($this->qb_from[0]))
2067
			{
2068
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2069
			}
2070
 
2071
			$table = $this->qb_from[0];
2072
		}
2073
		else
2074
		{
2075
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2076
		}
2077
 
2078
		$sql = $this->_truncate($table);
2079
		$this->_reset_write();
2080
		return $this->query($sql);
2081
	}
2082
 
2083
	// --------------------------------------------------------------------
2084
 
2085
	/**
2086
	 * Truncate statement
2087
	 *
2088
	 * Generates a platform-specific truncate string from the supplied data
2089
	 *
2090
	 * If the database does not support the truncate() command,
2091
	 * then this method maps to 'DELETE FROM table'
2092
	 *
2093
	 * @param	string	the table name
2094
	 * @return	string
2095
	 */
2096
	protected function _truncate($table)
2097
	{
2098
		return 'TRUNCATE '.$table;
2099
	}
2100
 
2101
	// --------------------------------------------------------------------
2102
 
2103
	/**
2104
	 * Get DELETE query string
2105
	 *
2106
	 * Compiles a delete query string and returns the sql
2107
	 *
2108
	 * @param	string	the table to delete from
2109
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
2110
	 * @return	string
2111
	 */
2112
	public function get_compiled_delete($table = '', $reset = TRUE)
2113
	{
2114
		$this->return_delete_sql = TRUE;
2115
		$sql = $this->delete($table, '', NULL, $reset);
2116
		$this->return_delete_sql = FALSE;
2117
		return $sql;
2118
	}
2119
 
2120
	// --------------------------------------------------------------------
2121
 
2122
	/**
2123
	 * Delete
2124
	 *
2125
	 * Compiles a delete string and runs the query
2126
	 *
2127
	 * @param	mixed	the table(s) to delete from. String or array
2128
	 * @param	mixed	the where clause
2129
	 * @param	mixed	the limit clause
2130
	 * @param	bool
2131
	 * @return	mixed
2132
	 */
2133
	public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
2134
	{
2135
		// Combine any cached components with the current statements
2136
		$this->_merge_cache();
2137
 
2138
		if ($table === '')
2139
		{
2140
			if ( ! isset($this->qb_from[0]))
2141
			{
2142
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2143
			}
2144
 
2145
			$table = $this->qb_from[0];
2146
		}
2147
		elseif (is_array($table))
2148
		{
2149
			empty($where) && $reset_data = FALSE;
2150
 
2151
			foreach ($table as $single_table)
2152
			{
2153
				$this->delete($single_table, $where, $limit, $reset_data);
2154
			}
2155
 
2156
			return;
2157
		}
2158
		else
2159
		{
2160
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2161
		}
2162
 
2163
		if ($where !== '')
2164
		{
2165
			$this->where($where);
2166
		}
2167
 
2168
		if ( ! empty($limit))
2169
		{
2170
			$this->limit($limit);
2171
		}
2172
 
2173
		if (count($this->qb_where) === 0)
2174
		{
2175
			return ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE;
2176
		}
2177
 
2178
		$sql = $this->_delete($table);
2179
		if ($reset_data)
2180
		{
2181
			$this->_reset_write();
2182
		}
2183
 
2184
		return ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql);
2185
	}
2186
 
2187
	// --------------------------------------------------------------------
2188
 
2189
	/**
2190
	 * Delete statement
2191
	 *
2192
	 * Generates a platform-specific delete string from the supplied data
2193
	 *
2194
	 * @param	string	the table name
2195
	 * @return	string
2196
	 */
2197
	protected function _delete($table)
2198
	{
2199
		return 'DELETE FROM '.$table.$this->_compile_wh('qb_where')
2200
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
2201
	}
2202
 
2203
	// --------------------------------------------------------------------
2204
 
2205
	/**
2206
	 * DB Prefix
2207
	 *
2208
	 * Prepends a database prefix if one exists in configuration
2209
	 *
2210
	 * @param	string	the table
2211
	 * @return	string
2212
	 */
2213
	public function dbprefix($table = '')
2214
	{
2215
		if ($table === '')
2216
		{
2217
			$this->display_error('db_table_name_required');
2218
		}
2219
 
2220
		return $this->dbprefix.$table;
2221
	}
2222
 
2223
	// --------------------------------------------------------------------
2224
 
2225
	/**
2226
	 * Set DB Prefix
2227
	 *
2228
	 * Set's the DB Prefix to something new without needing to reconnect
2229
	 *
2230
	 * @param	string	the prefix
2231
	 * @return	string
2232
	 */
2233
	public function set_dbprefix($prefix = '')
2234
	{
2235
		return $this->dbprefix = $prefix;
2236
	}
2237
 
2238
	// --------------------------------------------------------------------
2239
 
2240
	/**
2241
	 * Track Aliases
2242
	 *
2243
	 * Used to track SQL statements written with aliased tables.
2244
	 *
2245
	 * @param	string	The table to inspect
2246
	 * @return	string
2247
	 */
2248
	protected function _track_aliases($table)
2249
	{
2250
		if (is_array($table))
2251
		{
2252
			foreach ($table as $t)
2253
			{
2254
				$this->_track_aliases($t);
2255
			}
2256
			return;
2257
		}
2258
 
2259
		// Does the string contain a comma?  If so, we need to separate
2260
		// the string into discreet statements
2261
		if (strpos($table, ',') !== FALSE)
2262
		{
2263
			return $this->_track_aliases(explode(',', $table));
2264
		}
2265
 
2266
		// if a table alias is used we can recognize it by a space
2267
		if (strpos($table, ' ') !== FALSE)
2268
		{
2269
			// if the alias is written with the AS keyword, remove it
2270
			$table = preg_replace('/\s+AS\s+/i', ' ', $table);
2271
 
2272
			// Grab the alias
2273
			$table = trim(strrchr($table, ' '));
2274
 
2275
			// Store the alias, if it doesn't already exist
2276
			if ( ! in_array($table, $this->qb_aliased_tables))
2277
			{
2278
				$this->qb_aliased_tables[] = $table;
2279
			}
2280
		}
2281
	}
2282
 
2283
	// --------------------------------------------------------------------
2284
 
2285
	/**
2286
	 * Compile the SELECT statement
2287
	 *
2288
	 * Generates a query string based on which functions were used.
2289
	 * Should not be called directly.
2290
	 *
2291
	 * @param	bool	$select_override
2292
	 * @return	string
2293
	 */
2294
	protected function _compile_select($select_override = FALSE)
2295
	{
2296
		// Combine any cached components with the current statements
2297
		$this->_merge_cache();
2298
 
2299
		// Write the "select" portion of the query
2300
		if ($select_override !== FALSE)
2301
		{
2302
			$sql = $select_override;
2303
		}
2304
		else
2305
		{
2306
			$sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';
2307
 
2308
			if (count($this->qb_select) === 0)
2309
			{
2310
				$sql .= '*';
2311
			}
2312
			else
2313
			{
2314
				// Cycle through the "select" portion of the query and prep each column name.
2315
				// The reason we protect identifiers here rather than in the select() function
2316
				// is because until the user calls the from() function we don't know if there are aliases
2317
				foreach ($this->qb_select as $key => $val)
2318
				{
2319
					$no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL;
2320
					$this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape);
2321
				}
2322
 
2323
				$sql .= implode(', ', $this->qb_select);
2324
			}
2325
		}
2326
 
2327
		// Write the "FROM" portion of the query
2328
		if (count($this->qb_from) > 0)
2329
		{
2330
			$sql .= "\nFROM ".$this->_from_tables();
2331
		}
2332
 
2333
		// Write the "JOIN" portion of the query
2334
		if (count($this->qb_join) > 0)
2335
		{
2336
			$sql .= "\n".implode("\n", $this->qb_join);
2337
		}
2338
 
2339
		$sql .= $this->_compile_wh('qb_where')
2340
			.$this->_compile_group_by()
2341
			.$this->_compile_wh('qb_having')
2342
			.$this->_compile_order_by(); // ORDER BY
2343
 
2344
		// LIMIT
1257 lars 2345
		if ($this->qb_limit OR $this->qb_offset)
68 lars 2346
		{
2347
			return $this->_limit($sql."\n");
2348
		}
2349
 
2350
		return $sql;
2351
	}
2352
 
2353
	// --------------------------------------------------------------------
2354
 
2355
	/**
2356
	 * Compile WHERE, HAVING statements
2357
	 *
2358
	 * Escapes identifiers in WHERE and HAVING statements at execution time.
2359
	 *
2360
	 * Required so that aliases are tracked properly, regardless of whether
2361
	 * where(), or_where(), having(), or_having are called prior to from(),
2362
	 * join() and dbprefix is added only if needed.
2363
	 *
2364
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
2365
	 * @return	string	SQL statement
2366
	 */
2367
	protected function _compile_wh($qb_key)
2368
	{
2369
		if (count($this->$qb_key) > 0)
2370
		{
2371
			for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++)
2372
			{
2373
				// Is this condition already compiled?
2374
				if (is_string($this->{$qb_key}[$i]))
2375
				{
2376
					continue;
2377
				}
2378
				elseif ($this->{$qb_key}[$i]['escape'] === FALSE)
2379
				{
2380
					$this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'];
2381
					continue;
2382
				}
2383
 
2384
				// Split multiple conditions
2385
				$conditions = preg_split(
2386
					'/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i',
2387
					$this->{$qb_key}[$i]['condition'],
2388
					-1,
2389
					PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
2390
				);
2391
 
2392
				for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++)
2393
				{
2394
					if (($op = $this->_get_operator($conditions[$ci])) === FALSE
2395
						OR ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(?<!\)))?(\)?)$/i', $conditions[$ci], $matches))
2396
					{
2397
						continue;
2398
					}
2399
 
2400
					// $matches = array(
2401
					//	0 => '(test <= foo)',	/* the whole thing */
2402
					//	1 => '(',		/* optional */
2403
					//	2 => 'test',		/* the field name */
2404
					//	3 => ' <= ',		/* $op */
2405
					//	4 => 'foo',		/* optional, if $op is e.g. 'IS NULL' */
2406
					//	5 => ')'		/* optional */
2407
					// );
2408
 
2409
					if ( ! empty($matches[4]))
2410
					{
2411
						$this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4]));
2412
						$matches[4] = ' '.$matches[4];
2413
					}
2414
 
2415
					$conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2]))
2416
						.' '.trim($matches[3]).$matches[4].$matches[5];
2417
				}
2418
 
2419
				$this->{$qb_key}[$i] = implode('', $conditions);
2420
			}
2421
 
2422
			return ($qb_key === 'qb_having' ? "\nHAVING " : "\nWHERE ")
2423
				.implode("\n", $this->$qb_key);
2424
		}
2425
 
2426
		return '';
2427
	}
2428
 
2429
	// --------------------------------------------------------------------
2430
 
2431
	/**
2432
	 * Compile GROUP BY
2433
	 *
2434
	 * Escapes identifiers in GROUP BY statements at execution time.
2435
	 *
2436
	 * Required so that aliases are tracked properly, regardless of wether
2437
	 * group_by() is called prior to from(), join() and dbprefix is added
2438
	 * only if needed.
2439
	 *
2440
	 * @return	string	SQL statement
2441
	 */
2442
	protected function _compile_group_by()
2443
	{
2444
		if (count($this->qb_groupby) > 0)
2445
		{
2446
			for ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++)
2447
			{
2448
				// Is it already compiled?
2449
				if (is_string($this->qb_groupby[$i]))
2450
				{
2451
					continue;
2452
				}
2453
 
2454
				$this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field']))
2455
					? $this->qb_groupby[$i]['field']
2456
					: $this->protect_identifiers($this->qb_groupby[$i]['field']);
2457
			}
2458
 
2459
			return "\nGROUP BY ".implode(', ', $this->qb_groupby);
2460
		}
2461
 
2462
		return '';
2463
	}
2464
 
2465
	// --------------------------------------------------------------------
2466
 
2467
	/**
2468
	 * Compile ORDER BY
2469
	 *
2470
	 * Escapes identifiers in ORDER BY statements at execution time.
2471
	 *
2472
	 * Required so that aliases are tracked properly, regardless of wether
2473
	 * order_by() is called prior to from(), join() and dbprefix is added
2474
	 * only if needed.
2475
	 *
2476
	 * @return	string	SQL statement
2477
	 */
2478
	protected function _compile_order_by()
2479
	{
2480
		if (is_array($this->qb_orderby) && count($this->qb_orderby) > 0)
2481
		{
2482
			for ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++)
2483
			{
2484
				if ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field']))
2485
				{
2486
					$this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']);
2487
				}
2488
 
2489
				$this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction'];
2490
			}
2491
 
2492
			return $this->qb_orderby = "\nORDER BY ".implode(', ', $this->qb_orderby);
2493
		}
2494
		elseif (is_string($this->qb_orderby))
2495
		{
2496
			return $this->qb_orderby;
2497
		}
2498
 
2499
		return '';
2500
	}
2501
 
2502
	// --------------------------------------------------------------------
2503
 
2504
	/**
2505
	 * Object to Array
2506
	 *
2507
	 * Takes an object as input and converts the class variables to array key/vals
2508
	 *
2509
	 * @param	object
2510
	 * @return	array
2511
	 */
2512
	protected function _object_to_array($object)
2513
	{
2514
		if ( ! is_object($object))
2515
		{
2516
			return $object;
2517
		}
2518
 
2519
		$array = array();
2520
		foreach (get_object_vars($object) as $key => $val)
2521
		{
2522
			// There are some built in keys we need to ignore for this conversion
2523
			if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')
2524
			{
2525
				$array[$key] = $val;
2526
			}
2527
		}
2528
 
2529
		return $array;
2530
	}
2531
 
2532
	// --------------------------------------------------------------------
2533
 
2534
	/**
2535
	 * Object to Array
2536
	 *
2537
	 * Takes an object as input and converts the class variables to array key/vals
2538
	 *
2539
	 * @param	object
2540
	 * @return	array
2541
	 */
2542
	protected function _object_to_array_batch($object)
2543
	{
2544
		if ( ! is_object($object))
2545
		{
2546
			return $object;
2547
		}
2548
 
2549
		$array = array();
2550
		$out = get_object_vars($object);
2551
		$fields = array_keys($out);
2552
 
2553
		foreach ($fields as $val)
2554
		{
2555
			// There are some built in keys we need to ignore for this conversion
2556
			if ($val !== '_parent_name')
2557
			{
2558
				$i = 0;
2559
				foreach ($out[$val] as $data)
2560
				{
2561
					$array[$i++][$val] = $data;
2562
				}
2563
			}
2564
		}
2565
 
2566
		return $array;
2567
	}
2568
 
2569
	// --------------------------------------------------------------------
2570
 
2571
	/**
2572
	 * Start Cache
2573
	 *
2574
	 * Starts QB caching
2575
	 *
2576
	 * @return	CI_DB_query_builder
2577
	 */
2578
	public function start_cache()
2579
	{
2580
		$this->qb_caching = TRUE;
2581
		return $this;
2582
	}
2583
 
2584
	// --------------------------------------------------------------------
2585
 
2586
	/**
2587
	 * Stop Cache
2588
	 *
2589
	 * Stops QB caching
2590
	 *
2591
	 * @return	CI_DB_query_builder
2592
	 */
2593
	public function stop_cache()
2594
	{
2595
		$this->qb_caching = FALSE;
2596
		return $this;
2597
	}
2598
 
2599
	// --------------------------------------------------------------------
2600
 
2601
	/**
2602
	 * Flush Cache
2603
	 *
2604
	 * Empties the QB cache
2605
	 *
2606
	 * @return	CI_DB_query_builder
2607
	 */
2608
	public function flush_cache()
2609
	{
2610
		$this->_reset_run(array(
2611
			'qb_cache_select'		=> array(),
2612
			'qb_cache_from'			=> array(),
2613
			'qb_cache_join'			=> array(),
2614
			'qb_cache_where'		=> array(),
2615
			'qb_cache_groupby'		=> array(),
2616
			'qb_cache_having'		=> array(),
2617
			'qb_cache_orderby'		=> array(),
2618
			'qb_cache_set'			=> array(),
2619
			'qb_cache_exists'		=> array(),
2620
			'qb_cache_no_escape'	=> array()
2621
		));
2622
 
2623
		return $this;
2624
	}
2625
 
2626
	// --------------------------------------------------------------------
2627
 
2628
	/**
2629
	 * Merge Cache
2630
	 *
2631
	 * When called, this function merges any cached QB arrays with
2632
	 * locally called ones.
2633
	 *
2634
	 * @return	void
2635
	 */
2636
	protected function _merge_cache()
2637
	{
2638
		if (count($this->qb_cache_exists) === 0)
2639
		{
2640
			return;
2641
		}
2642
		elseif (in_array('select', $this->qb_cache_exists, TRUE))
2643
		{
2644
			$qb_no_escape = $this->qb_cache_no_escape;
2645
		}
2646
 
2647
		foreach (array_unique($this->qb_cache_exists) as $val) // select, from, etc.
2648
		{
2649
			$qb_variable	= 'qb_'.$val;
2650
			$qb_cache_var	= 'qb_cache_'.$val;
2651
			$qb_new 	= $this->$qb_cache_var;
2652
 
2653
			for ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++)
2654
			{
2655
				if ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE))
2656
				{
2657
					$qb_new[] = $this->{$qb_variable}[$i];
2658
					if ($val === 'select')
2659
					{
2660
						$qb_no_escape[] = $this->qb_no_escape[$i];
2661
					}
2662
				}
2663
			}
2664
 
2665
			$this->$qb_variable = $qb_new;
2666
			if ($val === 'select')
2667
			{
2668
				$this->qb_no_escape = $qb_no_escape;
2669
			}
2670
		}
2671
 
2672
		// If we are "protecting identifiers" we need to examine the "from"
2673
		// portion of the query to determine if there are any aliases
2674
		if ($this->_protect_identifiers === TRUE && count($this->qb_cache_from) > 0)
2675
		{
2676
			$this->_track_aliases($this->qb_from);
2677
		}
2678
	}
2679
 
2680
	// --------------------------------------------------------------------
2681
 
2682
	/**
2683
	 * Is literal
2684
	 *
2685
	 * Determines if a string represents a literal value or a field name
2686
	 *
2687
	 * @param	string	$str
2688
	 * @return	bool
2689
	 */
2690
	protected function _is_literal($str)
2691
	{
2692
		$str = trim($str);
2693
 
2694
		if (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE))
2695
		{
2696
			return TRUE;
2697
		}
2698
 
2699
		static $_str;
2700
 
2701
		if (empty($_str))
2702
		{
2703
			$_str = ($this->_escape_char !== '"')
2704
				? array('"', "'") : array("'");
2705
		}
2706
 
2707
		return in_array($str[0], $_str, TRUE);
2708
	}
2709
 
2710
	// --------------------------------------------------------------------
2711
 
2712
	/**
2713
	 * Reset Query Builder values.
2714
	 *
2715
	 * Publicly-visible method to reset the QB values.
2716
	 *
2717
	 * @return	CI_DB_query_builder
2718
	 */
2719
	public function reset_query()
2720
	{
2721
		$this->_reset_select();
2722
		$this->_reset_write();
2723
		return $this;
2724
	}
2725
 
2726
	// --------------------------------------------------------------------
2727
 
2728
	/**
2729
	 * Resets the query builder values.  Called by the get() function
2730
	 *
2731
	 * @param	array	An array of fields to reset
2732
	 * @return	void
2733
	 */
2734
	protected function _reset_run($qb_reset_items)
2735
	{
2736
		foreach ($qb_reset_items as $item => $default_value)
2737
		{
2738
			$this->$item = $default_value;
2739
		}
2740
	}
2741
 
2742
	// --------------------------------------------------------------------
2743
 
2744
	/**
2745
	 * Resets the query builder values.  Called by the get() function
2746
	 *
2747
	 * @return	void
2748
	 */
2749
	protected function _reset_select()
2750
	{
2751
		$this->_reset_run(array(
2752
			'qb_select'		=> array(),
2753
			'qb_from'		=> array(),
2754
			'qb_join'		=> array(),
2755
			'qb_where'		=> array(),
2756
			'qb_groupby'		=> array(),
2757
			'qb_having'		=> array(),
2758
			'qb_orderby'		=> array(),
2759
			'qb_aliased_tables'	=> array(),
2760
			'qb_no_escape'		=> array(),
2761
			'qb_distinct'		=> FALSE,
2762
			'qb_limit'		=> FALSE,
2763
			'qb_offset'		=> FALSE
2764
		));
2765
	}
2766
 
2767
	// --------------------------------------------------------------------
2768
 
2769
	/**
2770
	 * Resets the query builder "write" values.
2771
	 *
2772
	 * Called by the insert() update() insert_batch() update_batch() and delete() functions
2773
	 *
2774
	 * @return	void
2775
	 */
2776
	protected function _reset_write()
2777
	{
2778
		$this->_reset_run(array(
2779
			'qb_set'	=> array(),
2780
			'qb_from'	=> array(),
2781
			'qb_join'	=> array(),
2782
			'qb_where'	=> array(),
2783
			'qb_orderby'	=> array(),
2784
			'qb_keys'	=> array(),
2785
			'qb_limit'	=> FALSE
2786
		));
2787
	}
2788
 
2789
}