Subversion-Projekte lars-tiefland.ci

Revision

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