GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

CI_DB_query_builder   D
last analyzed

Complexity

Total Complexity 327

Size/Duplication

Total Lines 2697
Duplicated Lines 5.38 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 7
Bugs 7 Features 0
Metric Value
dl 145
loc 2697
rs 4.4102
c 7
b 7
f 0
wmc 327
lcom 1
cbo 1

80 Methods

Rating   Name   Duplication   Size   Complexity  
B select() 0 30 6
A select_max() 0 4 1
A select_min() 0 4 1
A select_avg() 0 4 1
A select_sum() 0 4 1
B _max_min_avg_sum() 0 32 6
A _create_alias_from_table() 0 10 2
A distinct() 0 5 2
B from() 10 40 6
D join() 0 74 14
A where() 0 4 1
A or_where() 0 4 1
F _wh() 0 51 14
A where_in() 0 4 1
A or_where_in() 0 4 1
A where_not_in() 0 4 1
A or_where_not_in() 0 4 1
D _where_in() 0 47 11
A like() 0 4 1
A not_like() 0 4 1
A or_like() 0 4 1
A or_not_like() 0 4 1
D _like() 0 54 13
A group_start() 0 19 4
A or_group_start() 0 4 1
A not_group_start() 0 4 1
A or_not_group_start() 0 4 1
A group_end() 0 16 2
A _group_get_type() 0 10 2
C group_by() 0 30 7
A having() 0 4 1
A or_having() 0 4 1
C order_by() 0 48 12
A limit() 0 7 3
A offset() 0 5 2
A _limit() 0 4 2
B set() 0 19 5
A get_compiled_select() 0 17 3
A get() 0 17 3
B count_all_results() 0 25 6
A get_where() 0 21 4
C insert_batch() 19 34 8
A _insert_batch() 0 4 1
C set_insert_batch() 0 47 9
A get_compiled_insert() 0 22 3
A insert() 0 23 3
B _validate_insert() 12 18 6
C replace() 13 27 7
A _replace() 0 4 1
A _from_tables() 0 4 1
A get_compiled_update() 0 19 3
B update() 0 29 5
B _validate_update() 4 18 6
D update_batch() 19 42 10
B _update_batch() 28 28 5
C set_update_batch() 0 35 8
A empty_table() 20 20 4
A truncate() 20 20 4
A _truncate() 0 4 1
A get_compiled_delete() 0 7 1
C delete() 0 53 13
A _delete() 0 5 2
A dbprefix() 0 9 2
A set_dbprefix() 0 4 1
B _track_aliases() 0 34 6
C _compile_select() 0 58 9
C _compile_wh() 0 61 11
B _compile_group_by() 0 22 6
C _compile_order_by() 0 23 7
B _object_to_array() 0 19 6
B _object_to_array_batch() 0 26 5
A start_cache() 0 5 1
A stop_cache() 0 5 1
A flush_cache() 0 17 1
D _merge_cache() 0 43 10
B _is_literal() 0 19 7
A reset_query() 0 6 1
A _reset_run() 0 7 2
A _reset_select() 0 17 1
A _reset_write() 0 12 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CI_DB_query_builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CI_DB_query_builder, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 52 and the first side effect is on line 38.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 - 2015, 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. (http://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	http://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		http://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 View Code Duplication
					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 View Code Duplication
				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
		// Split multiple conditions
535
		if ($escape === TRUE && preg_match_all('/\sAND\s|\sOR\s/i', $cond, $m, PREG_OFFSET_CAPTURE))
536
		{
537
			$newcond = '';
538
			$m[0][] = array('', strlen($cond));
539
540
			for ($i = 0, $c = count($m[0]), $s = 0;
541
				$i < $c;
542
				$s = $m[0][$i][1] + strlen($m[0][$i][0]), $i++)
543
			{
544
				$temp = substr($cond, $s, ($m[0][$i][1] - $s));
545
546
				$newcond .= preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $temp, $match)
547
						? $this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3])
548
						: $temp;
549
550
				$newcond .= $m[0][$i][0];
551
			}
552
553
			$cond = ' ON '.$newcond;
554
		}
555
		// Split apart the condition and protect the identifiers
556
		elseif ($escape === TRUE && preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $cond, $match))
557
		{
558
			$cond = ' ON '.$this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]);
559
		}
560
		elseif ( ! $this->_has_operator($cond))
561
		{
562
			$cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';
563
		}
564
		else
565
		{
566
			$cond = ' ON '.$cond;
567
		}
568
569
		// Do we want to escape the table name?
570
		if ($escape === TRUE)
571
		{
572
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
573
		}
574
575
		// Assemble the JOIN statement
576
		$this->qb_join[] = $join = $type.'JOIN '.$table.$cond;
577
578
		if ($this->qb_caching === TRUE)
579
		{
580
			$this->qb_cache_join[] = $join;
581
			$this->qb_cache_exists[] = 'join';
582
		}
583
584
		return $this;
585
	}
586
587
	// --------------------------------------------------------------------
588
589
	/**
590
	 * WHERE
591
	 *
592
	 * Generates the WHERE portion of the query.
593
	 * Separates multiple calls with 'AND'.
594
	 *
595
	 * @param	mixed
596
	 * @param	mixed
597
	 * @param	bool
598
	 * @return	CI_DB_query_builder
599
	 */
600
	public function where($key, $value = NULL, $escape = NULL)
601
	{
602
		return $this->_wh('qb_where', $key, $value, 'AND ', $escape);
603
	}
604
605
	// --------------------------------------------------------------------
606
607
	/**
608
	 * OR WHERE
609
	 *
610
	 * Generates the WHERE portion of the query.
611
	 * Separates multiple calls with 'OR'.
612
	 *
613
	 * @param	mixed
614
	 * @param	mixed
615
	 * @param	bool
616
	 * @return	CI_DB_query_builder
617
	 */
618
	public function or_where($key, $value = NULL, $escape = NULL)
619
	{
620
		return $this->_wh('qb_where', $key, $value, 'OR ', $escape);
621
	}
622
623
	// --------------------------------------------------------------------
624
625
	/**
626
	 * WHERE, HAVING
627
	 *
628
	 * @used-by	where()
629
	 * @used-by	or_where()
630
	 * @used-by	having()
631
	 * @used-by	or_having()
632
	 *
633
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
634
	 * @param	mixed	$key
635
	 * @param	mixed	$value
636
	 * @param	string	$type
637
	 * @param	bool	$escape
638
	 * @return	CI_DB_query_builder
639
	 */
640
	protected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL)
641
	{
642
		$qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';
643
644
		if ( ! is_array($key))
645
		{
646
			$key = array($key => $value);
647
		}
648
649
		// If the escape value was not set will base it on the global setting
650
		is_bool($escape) OR $escape = $this->_protect_identifiers;
651
652
		foreach ($key as $k => $v)
653
		{
654
			$prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)
655
				? $this->_group_get_type('')
656
				: $this->_group_get_type($type);
657
658
			if ($v !== NULL)
659
			{
660
				if ($escape === TRUE)
661
				{
662
					$v = ' '.$this->escape($v);
663
				}
664
665
				if ( ! $this->_has_operator($k))
666
				{
667
					$k .= ' = ';
668
				}
669
			}
670
			elseif ( ! $this->_has_operator($k))
671
			{
672
				// value appears not to have been set, assign the test to IS NULL
673
				$k .= ' IS NULL';
674
			}
675
			elseif (preg_match('/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE))
676
			{
677
				$k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL');
678
			}
679
680
			$this->{$qb_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
681
			if ($this->qb_caching === TRUE)
682
			{
683
				$this->{$qb_cache_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
684
				$this->qb_cache_exists[] = substr($qb_key, 3);
685
			}
686
687
		}
688
689
		return $this;
690
	}
691
692
	// --------------------------------------------------------------------
693
694
	/**
695
	 * WHERE IN
696
	 *
697
	 * Generates a WHERE field IN('item', 'item') SQL query,
698
	 * joined with 'AND' if appropriate.
699
	 *
700
	 * @param	string	$key	The field to search
701
	 * @param	array	$values	The values searched on
702
	 * @param	bool	$escape
703
	 * @return	CI_DB_query_builder
704
	 */
705
	public function where_in($key = NULL, $values = NULL, $escape = NULL)
706
	{
707
		return $this->_where_in($key, $values, FALSE, 'AND ', $escape);
708
	}
709
710
	// --------------------------------------------------------------------
711
712
	/**
713
	 * OR WHERE IN
714
	 *
715
	 * Generates a WHERE field IN('item', 'item') SQL query,
716
	 * joined with 'OR' if appropriate.
717
	 *
718
	 * @param	string	$key	The field to search
719
	 * @param	array	$values	The values searched on
720
	 * @param	bool	$escape
721
	 * @return	CI_DB_query_builder
722
	 */
723
	public function or_where_in($key = NULL, $values = NULL, $escape = NULL)
724
	{
725
		return $this->_where_in($key, $values, FALSE, 'OR ', $escape);
726
	}
727
728
	// --------------------------------------------------------------------
729
730
	/**
731
	 * WHERE NOT IN
732
	 *
733
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
734
	 * joined with 'AND' if appropriate.
735
	 *
736
	 * @param	string	$key	The field to search
737
	 * @param	array	$values	The values searched on
738
	 * @param	bool	$escape
739
	 * @return	CI_DB_query_builder
740
	 */
741
	public function where_not_in($key = NULL, $values = NULL, $escape = NULL)
742
	{
743
		return $this->_where_in($key, $values, TRUE, 'AND ', $escape);
744
	}
745
746
	// --------------------------------------------------------------------
747
748
	/**
749
	 * OR WHERE NOT IN
750
	 *
751
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
752
	 * joined with 'OR' if appropriate.
753
	 *
754
	 * @param	string	$key	The field to search
755
	 * @param	array	$values	The values searched on
756
	 * @param	bool	$escape
757
	 * @return	CI_DB_query_builder
758
	 */
759
	public function or_where_not_in($key = NULL, $values = NULL, $escape = NULL)
760
	{
761
		return $this->_where_in($key, $values, TRUE, 'OR ', $escape);
762
	}
763
764
	// --------------------------------------------------------------------
765
766
	/**
767
	 * Internal WHERE IN
768
	 *
769
	 * @used-by	where_in()
770
	 * @used-by	or_where_in()
771
	 * @used-by	where_not_in()
772
	 * @used-by	or_where_not_in()
773
	 *
774
	 * @param	string	$key	The field to search
775
	 * @param	array	$values	The values searched on
776
	 * @param	bool	$not	If the statement would be IN or NOT IN
777
	 * @param	string	$type
778
	 * @param	bool	$escape
779
	 * @return	CI_DB_query_builder
780
	 */
781
	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ', $escape = NULL)
782
	{
783
		if ($key === NULL OR $values === NULL)
784
		{
785
			return $this;
786
		}
787
788
		if ( ! is_array($values))
789
		{
790
			$values = array($values);
791
		}
792
793
		is_bool($escape) OR $escape = $this->_protect_identifiers;
794
795
		$not = ($not) ? ' NOT' : '';
796
797
		if ($escape === TRUE)
798
		{
799
			$where_in = array();
800
			foreach ($values as $value)
801
			{
802
				$where_in[] = $this->escape($value);
803
			}
804
		}
805
		else
806
		{
807
			$where_in = array_values($values);
808
		}
809
810
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
811
			? $this->_group_get_type('')
812
			: $this->_group_get_type($type);
813
814
		$where_in = array(
815
			'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')',
816
			'escape' => $escape
817
		);
818
819
		$this->qb_where[] = $where_in;
820
		if ($this->qb_caching === TRUE)
821
		{
822
			$this->qb_cache_where[] = $where_in;
823
			$this->qb_cache_exists[] = 'where';
824
		}
825
826
		return $this;
827
	}
828
829
	// --------------------------------------------------------------------
830
831
	/**
832
	 * LIKE
833
	 *
834
	 * Generates a %LIKE% portion of the query.
835
	 * Separates multiple calls with 'AND'.
836
	 *
837
	 * @param	mixed	$field
838
	 * @param	string	$match
839
	 * @param	string	$side
840
	 * @param	bool	$escape
841
	 * @return	CI_DB_query_builder
842
	 */
843
	public function like($field, $match = '', $side = 'both', $escape = NULL)
844
	{
845
		return $this->_like($field, $match, 'AND ', $side, '', $escape);
846
	}
847
848
	// --------------------------------------------------------------------
849
850
	/**
851
	 * NOT LIKE
852
	 *
853
	 * Generates a NOT LIKE portion of the query.
854
	 * Separates multiple calls with 'AND'.
855
	 *
856
	 * @param	mixed	$field
857
	 * @param	string	$match
858
	 * @param	string	$side
859
	 * @param	bool	$escape
860
	 * @return	CI_DB_query_builder
861
	 */
862
	public function not_like($field, $match = '', $side = 'both', $escape = NULL)
863
	{
864
		return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape);
865
	}
866
867
	// --------------------------------------------------------------------
868
869
	/**
870
	 * OR LIKE
871
	 *
872
	 * Generates a %LIKE% portion of the query.
873
	 * Separates multiple calls with 'OR'.
874
	 *
875
	 * @param	mixed	$field
876
	 * @param	string	$match
877
	 * @param	string	$side
878
	 * @param	bool	$escape
879
	 * @return	CI_DB_query_builder
880
	 */
881
	public function or_like($field, $match = '', $side = 'both', $escape = NULL)
882
	{
883
		return $this->_like($field, $match, 'OR ', $side, '', $escape);
884
	}
885
886
	// --------------------------------------------------------------------
887
888
	/**
889
	 * OR NOT LIKE
890
	 *
891
	 * Generates a NOT LIKE portion of the query.
892
	 * Separates multiple calls with 'OR'.
893
	 *
894
	 * @param	mixed	$field
895
	 * @param	string	$match
896
	 * @param	string	$side
897
	 * @param	bool	$escape
898
	 * @return	CI_DB_query_builder
899
	 */
900
	public function or_not_like($field, $match = '', $side = 'both', $escape = NULL)
901
	{
902
		return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape);
903
	}
904
905
	// --------------------------------------------------------------------
906
907
	/**
908
	 * Internal LIKE
909
	 *
910
	 * @used-by	like()
911
	 * @used-by	or_like()
912
	 * @used-by	not_like()
913
	 * @used-by	or_not_like()
914
	 *
915
	 * @param	mixed	$field
916
	 * @param	string	$match
917
	 * @param	string	$type
918
	 * @param	string	$side
919
	 * @param	string	$not
920
	 * @param	bool	$escape
921
	 * @return	CI_DB_query_builder
922
	 */
923
	protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)
924
	{
925
		if ( ! is_array($field))
926
		{
927
			$field = array($field => $match);
928
		}
929
930
		is_bool($escape) OR $escape = $this->_protect_identifiers;
931
		// lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)
932
		$side = strtolower($side);
933
934
		foreach ($field as $k => $v)
935
		{
936
			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
937
				? $this->_group_get_type('') : $this->_group_get_type($type);
938
939
			if ($escape === TRUE)
940
			{
941
				$v = $this->escape_like_str($v);
942
			}
943
944
			if ($side === 'none')
945
			{
946
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}'";
947
			}
948
			elseif ($side === 'before')
949
			{
950
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}'";
951
			}
952
			elseif ($side === 'after')
953
			{
954
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}%'";
955
			}
956
			else
957
			{
958
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}%'";
959
			}
960
961
			// some platforms require an escape sequence definition for LIKE wildcards
962
			if ($escape === TRUE && $this->_like_escape_str !== '')
963
			{
964
				$like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr);
965
			}
966
967
			$this->qb_where[] = array('condition' => $like_statement, 'escape' => $escape);
968
			if ($this->qb_caching === TRUE)
969
			{
970
				$this->qb_cache_where[] = array('condition' => $like_statement, 'escape' => $escape);
971
				$this->qb_cache_exists[] = 'where';
972
			}
973
		}
974
975
		return $this;
976
	}
977
978
	// --------------------------------------------------------------------
979
980
	/**
981
	 * Starts a query group.
982
	 *
983
	 * @param	string	$not	(Internal use only)
984
	 * @param	string	$type	(Internal use only)
985
	 * @return	CI_DB_query_builder
986
	 */
987
	public function group_start($not = '', $type = 'AND ')
988
	{
989
		$type = $this->_group_get_type($type);
990
991
		$this->qb_where_group_started = TRUE;
992
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
993
		$where = array(
994
			'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (',
995
			'escape' => FALSE
996
		);
997
998
		$this->qb_where[] = $where;
999
		if ($this->qb_caching)
1000
		{
1001
			$this->qb_cache_where[] = $where;
1002
		}
1003
1004
		return $this;
1005
	}
1006
1007
	// --------------------------------------------------------------------
1008
1009
	/**
1010
	 * Starts a query group, but ORs the group
1011
	 *
1012
	 * @return	CI_DB_query_builder
1013
	 */
1014
	public function or_group_start()
1015
	{
1016
		return $this->group_start('', 'OR ');
1017
	}
1018
1019
	// --------------------------------------------------------------------
1020
1021
	/**
1022
	 * Starts a query group, but NOTs the group
1023
	 *
1024
	 * @return	CI_DB_query_builder
1025
	 */
1026
	public function not_group_start()
1027
	{
1028
		return $this->group_start('NOT ', 'AND ');
1029
	}
1030
1031
	// --------------------------------------------------------------------
1032
1033
	/**
1034
	 * Starts a query group, but OR NOTs the group
1035
	 *
1036
	 * @return	CI_DB_query_builder
1037
	 */
1038
	public function or_not_group_start()
1039
	{
1040
		return $this->group_start('NOT ', 'OR ');
1041
	}
1042
1043
	// --------------------------------------------------------------------
1044
1045
	/**
1046
	 * Ends a query group
1047
	 *
1048
	 * @return	CI_DB_query_builder
1049
	 */
1050
	public function group_end()
1051
	{
1052
		$this->qb_where_group_started = FALSE;
1053
		$where = array(
1054
			'condition' => str_repeat(' ', $this->qb_where_group_count--).')',
1055
			'escape' => FALSE
1056
		);
1057
1058
		$this->qb_where[] = $where;
1059
		if ($this->qb_caching)
1060
		{
1061
			$this->qb_cache_where[] = $where;
1062
		}
1063
1064
		return $this;
1065
	}
1066
1067
	// --------------------------------------------------------------------
1068
1069
	/**
1070
	 * Group_get_type
1071
	 *
1072
	 * @used-by	group_start()
1073
	 * @used-by	_like()
1074
	 * @used-by	_wh()
1075
	 * @used-by	_where_in()
1076
	 *
1077
	 * @param	string	$type
1078
	 * @return	string
1079
	 */
1080
	protected function _group_get_type($type)
1081
	{
1082
		if ($this->qb_where_group_started)
1083
		{
1084
			$type = '';
1085
			$this->qb_where_group_started = FALSE;
1086
		}
1087
1088
		return $type;
1089
	}
1090
1091
	// --------------------------------------------------------------------
1092
1093
	/**
1094
	 * GROUP BY
1095
	 *
1096
	 * @param	string	$by
1097
	 * @param	bool	$escape
1098
	 * @return	CI_DB_query_builder
1099
	 */
1100
	public function group_by($by, $escape = NULL)
1101
	{
1102
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1103
1104
		if (is_string($by))
1105
		{
1106
			$by = ($escape === TRUE)
1107
				? explode(',', $by)
1108
				: array($by);
1109
		}
1110
1111
		foreach ($by as $val)
1112
		{
1113
			$val = trim($val);
1114
1115
			if ($val !== '')
1116
			{
1117
				$val = array('field' => $val, 'escape' => $escape);
1118
1119
				$this->qb_groupby[] = $val;
1120
				if ($this->qb_caching === TRUE)
1121
				{
1122
					$this->qb_cache_groupby[] = $val;
1123
					$this->qb_cache_exists[] = 'groupby';
1124
				}
1125
			}
1126
		}
1127
1128
		return $this;
1129
	}
1130
1131
	// --------------------------------------------------------------------
1132
1133
	/**
1134
	 * HAVING
1135
	 *
1136
	 * Separates multiple calls with 'AND'.
1137
	 *
1138
	 * @param	string	$key
1139
	 * @param	string	$value
1140
	 * @param	bool	$escape
1141
	 * @return	object
1142
	 */
1143
	public function having($key, $value = NULL, $escape = NULL)
1144
	{
1145
		return $this->_wh('qb_having', $key, $value, 'AND ', $escape);
1146
	}
1147
1148
	// --------------------------------------------------------------------
1149
1150
	/**
1151
	 * OR HAVING
1152
	 *
1153
	 * Separates multiple calls with 'OR'.
1154
	 *
1155
	 * @param	string	$key
1156
	 * @param	string	$value
1157
	 * @param	bool	$escape
1158
	 * @return	object
1159
	 */
1160
	public function or_having($key, $value = NULL, $escape = NULL)
1161
	{
1162
		return $this->_wh('qb_having', $key, $value, 'OR ', $escape);
1163
	}
1164
1165
	// --------------------------------------------------------------------
1166
1167
	/**
1168
	 * ORDER BY
1169
	 *
1170
	 * @param	string	$orderby
1171
	 * @param	string	$direction	ASC, DESC or RANDOM
1172
	 * @param	bool	$escape
1173
	 * @return	CI_DB_query_builder
1174
	 */
1175
	public function order_by($orderby, $direction = '', $escape = NULL)
1176
	{
1177
		$direction = strtoupper(trim($direction));
1178
1179
		if ($direction === 'RANDOM')
1180
		{
1181
			$direction = '';
1182
1183
			// Do we have a seed value?
1184
			$orderby = ctype_digit((string) $orderby)
1185
				? sprintf($this->_random_keyword[1], $orderby)
1186
				: $this->_random_keyword[0];
1187
		}
1188
		elseif (empty($orderby))
1189
		{
1190
			return $this;
1191
		}
1192
		elseif ($direction !== '')
1193
		{
1194
			$direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
1195
		}
1196
1197
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1198
1199
		if ($escape === FALSE)
1200
		{
1201
			$qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE);
1202
		}
1203
		else
1204
		{
1205
			$qb_orderby = array();
1206
			foreach (explode(',', $orderby) as $field)
1207
			{
1208
				$qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE))
1209
					? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE)
1210
					: array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE);
1211
			}
1212
		}
1213
1214
		$this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby);
1215
		if ($this->qb_caching === TRUE)
1216
		{
1217
			$this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby);
1218
			$this->qb_cache_exists[] = 'orderby';
1219
		}
1220
1221
		return $this;
1222
	}
1223
1224
	// --------------------------------------------------------------------
1225
1226
	/**
1227
	 * LIMIT
1228
	 *
1229
	 * @param	int	$value	LIMIT value
1230
	 * @param	int	$offset	OFFSET value
1231
	 * @return	CI_DB_query_builder
1232
	 */
1233
	public function limit($value, $offset = 0)
1234
	{
1235
		is_null($value) OR $this->qb_limit = (int) $value;
1236
		empty($offset) OR $this->qb_offset = (int) $offset;
1237
1238
		return $this;
1239
	}
1240
1241
	// --------------------------------------------------------------------
1242
1243
	/**
1244
	 * Sets the OFFSET value
1245
	 *
1246
	 * @param	int	$offset	OFFSET value
1247
	 * @return	CI_DB_query_builder
1248
	 */
1249
	public function offset($offset)
1250
	{
1251
		empty($offset) OR $this->qb_offset = (int) $offset;
1252
		return $this;
1253
	}
1254
1255
	// --------------------------------------------------------------------
1256
1257
	/**
1258
	 * LIMIT string
1259
	 *
1260
	 * Generates a platform-specific LIMIT clause.
1261
	 *
1262
	 * @param	string	$sql	SQL Query
1263
	 * @return	string
1264
	 */
1265
	protected function _limit($sql)
1266
	{
1267
		return $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').$this->qb_limit;
1268
	}
1269
1270
	// --------------------------------------------------------------------
1271
1272
	/**
1273
	 * The "set" function.
1274
	 *
1275
	 * Allows key/value pairs to be set for inserting or updating
1276
	 *
1277
	 * @param	mixed
1278
	 * @param	string
1279
	 * @param	bool
1280
	 * @return	CI_DB_query_builder
1281
	 */
1282
	public function set($key, $value = '', $escape = NULL)
1283
	{
1284
		$key = $this->_object_to_array($key);
1285
1286
		if ( ! is_array($key))
1287
		{
1288
			$key = array($key => $value);
1289
		}
1290
1291
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1292
1293
		foreach ($key as $k => $v)
1294
		{
1295
			$this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
1296
				? $this->escape($v) : $v;
1297
		}
1298
1299
		return $this;
1300
	}
1301
1302
	// --------------------------------------------------------------------
1303
1304
	/**
1305
	 * Get SELECT query string
1306
	 *
1307
	 * Compiles a SELECT query string and returns the sql.
1308
	 *
1309
	 * @param	string	the table name to select from (optional)
1310
	 * @param	bool	TRUE: resets QB values; FALSE: leave QB values alone
1311
	 * @return	string
1312
	 */
1313
	public function get_compiled_select($table = '', $reset = TRUE)
1314
	{
1315
		if ($table !== '')
1316
		{
1317
			$this->_track_aliases($table);
1318
			$this->from($table);
1319
		}
1320
1321
		$select = $this->_compile_select();
1322
1323
		if ($reset === TRUE)
1324
		{
1325
			$this->_reset_select();
1326
		}
1327
1328
		return $select;
1329
	}
1330
1331
	// --------------------------------------------------------------------
1332
1333
	/**
1334
	 * Get
1335
	 *
1336
	 * Compiles the select statement based on the other functions called
1337
	 * and runs the query
1338
	 *
1339
	 * @param	string	the table
1340
	 * @param	string	the limit clause
1341
	 * @param	string	the offset clause
1342
	 * @return	object
1343
	 */
1344
	public function get($table = '', $limit = NULL, $offset = NULL)
1345
	{
1346
		if ($table !== '')
1347
		{
1348
			$this->_track_aliases($table);
1349
			$this->from($table);
1350
		}
1351
1352
		if ( ! empty($limit))
1353
		{
1354
			$this->limit($limit, $offset);
1355
		}
1356
1357
		$result = $this->query($this->_compile_select());
1358
		$this->_reset_select();
1359
		return $result;
1360
	}
1361
1362
	// --------------------------------------------------------------------
1363
1364
	/**
1365
	 * "Count All Results" query
1366
	 *
1367
	 * Generates a platform-specific query string that counts all records
1368
	 * returned by an Query Builder query.
1369
	 *
1370
	 * @param	string
1371
	 * @param	bool	the reset clause
1372
	 * @return	int
1373
	 */
1374
	public function count_all_results($table = '', $reset = TRUE)
1375
	{
1376
		if ($table !== '')
1377
		{
1378
			$this->_track_aliases($table);
1379
			$this->from($table);
1380
		}
1381
1382
		$result = ($this->qb_distinct === TRUE OR ! empty($this->qb_orderby))
1383
			? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results")
1384
			: $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows')));
1385
1386
		if ($reset === TRUE)
1387
		{
1388
			$this->_reset_select();
1389
		}
1390
1391
		if ($result->num_rows() === 0)
1392
		{
1393
			return 0;
1394
		}
1395
1396
		$row = $result->row();
1397
		return (int) $row->numrows;
1398
	}
1399
1400
	// --------------------------------------------------------------------
1401
1402
	/**
1403
	 * Get_Where
1404
	 *
1405
	 * Allows the where clause, limit and offset to be added directly
1406
	 *
1407
	 * @param	string	$table
1408
	 * @param	string	$where
1409
	 * @param	int	$limit
1410
	 * @param	int	$offset
1411
	 * @return	object
1412
	 */
1413
	public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)
1414
	{
1415
		if ($table !== '')
1416
		{
1417
			$this->from($table);
1418
		}
1419
1420
		if ($where !== NULL)
1421
		{
1422
			$this->where($where);
1423
		}
1424
1425
		if ( ! empty($limit))
1426
		{
1427
			$this->limit($limit, $offset);
1428
		}
1429
1430
		$result = $this->query($this->_compile_select());
1431
		$this->_reset_select();
1432
		return $result;
1433
	}
1434
1435
	// --------------------------------------------------------------------
1436
1437
	/**
1438
	 * Insert_Batch
1439
	 *
1440
	 * Compiles batch insert strings and runs the queries
1441
	 *
1442
	 * @param	string	$table	Table to insert into
1443
	 * @param	array	$set 	An associative array of insert values
1444
	 * @param	bool	$escape	Whether to escape values and identifiers
1445
	 * @return	int	Number of rows inserted or FALSE on failure
1446
	 */
1447
	public function insert_batch($table = '', $set = NULL, $escape = NULL)
1448
	{
1449
		if ($set !== NULL)
1450
		{
1451
			$this->set_insert_batch($set, '', $escape);
1452
		}
1453
1454 View Code Duplication
		if (count($this->qb_set) === 0)
1455
		{
1456
			// No valid data array. Folds in cases where keys and values did not match up
1457
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1458
		}
1459
1460 View Code Duplication
		if ($table === '')
1461
		{
1462
			if ( ! isset($this->qb_from[0]))
1463
			{
1464
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1465
			}
1466
1467
			$table = $this->qb_from[0];
1468
		}
1469
1470
		// Batch this baby
1471
		$affected_rows = 0;
1472 View Code Duplication
		for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100)
1473
		{
1474
			$this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, 100)));
1475
			$affected_rows += $this->affected_rows();
1476
		}
1477
1478
		$this->_reset_write();
1479
		return $affected_rows;
1480
	}
1481
1482
	// --------------------------------------------------------------------
1483
1484
	/**
1485
	 * Insert batch statement
1486
	 *
1487
	 * Generates a platform-specific insert string from the supplied data.
1488
	 *
1489
	 * @param	string	$table	Table name
1490
	 * @param	array	$keys	INSERT keys
1491
	 * @param	array	$values	INSERT values
1492
	 * @return	string
1493
	 */
1494
	protected function _insert_batch($table, $keys, $values)
1495
	{
1496
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
1497
	}
1498
1499
	// --------------------------------------------------------------------
1500
1501
	/**
1502
	 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
1503
	 *
1504
	 * @param	mixed
1505
	 * @param	string
1506
	 * @param	bool
1507
	 * @return	CI_DB_query_builder
1508
	 */
1509
	public function set_insert_batch($key, $value = '', $escape = NULL)
1510
	{
1511
		$key = $this->_object_to_array_batch($key);
1512
1513
		if ( ! is_array($key))
1514
		{
1515
			$key = array($key => $value);
1516
		}
1517
1518
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1519
1520
		$keys = array_keys($this->_object_to_array(current($key)));
1521
		sort($keys);
1522
1523
		foreach ($key as $row)
1524
		{
1525
			$row = $this->_object_to_array($row);
1526
			if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
1527
			{
1528
				// batch function above returns an error on an empty array
1529
				$this->qb_set[] = array();
1530
				return;
1531
			}
1532
1533
			ksort($row); // puts $row in the same order as our keys
1534
1535
			if ($escape !== FALSE)
1536
			{
1537
				$clean = array();
1538
				foreach ($row as $value)
1539
				{
1540
					$clean[] = $this->escape($value);
1541
				}
1542
1543
				$row = $clean;
1544
			}
1545
1546
			$this->qb_set[] = '('.implode(',', $row).')';
1547
		}
1548
1549
		foreach ($keys as $k)
1550
		{
1551
			$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
1552
		}
1553
1554
		return $this;
1555
	}
1556
1557
	// --------------------------------------------------------------------
1558
1559
	/**
1560
	 * Get INSERT query string
1561
	 *
1562
	 * Compiles an insert query and returns the sql
1563
	 *
1564
	 * @param	string	the table to insert into
1565
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1566
	 * @return	string
1567
	 */
1568
	public function get_compiled_insert($table = '', $reset = TRUE)
1569
	{
1570
		if ($this->_validate_insert($table) === FALSE)
1571
		{
1572
			return FALSE;
1573
		}
1574
1575
		$sql = $this->_insert(
1576
			$this->protect_identifiers(
1577
				$this->qb_from[0], TRUE, NULL, FALSE
1578
			),
1579
			array_keys($this->qb_set),
1580
			array_values($this->qb_set)
1581
		);
1582
1583
		if ($reset === TRUE)
1584
		{
1585
			$this->_reset_write();
1586
		}
1587
1588
		return $sql;
1589
	}
1590
1591
	// --------------------------------------------------------------------
1592
1593
	/**
1594
	 * Insert
1595
	 *
1596
	 * Compiles an insert string and runs the query
1597
	 *
1598
	 * @param	string	the table to insert data into
1599
	 * @param	array	an associative array of insert values
1600
	 * @param	bool	$escape	Whether to escape values and identifiers
1601
	 * @return	object
1602
	 */
1603
	public function insert($table = '', $set = NULL, $escape = NULL)
1604
	{
1605
		if ($set !== NULL)
1606
		{
1607
			$this->set($set, '', $escape);
1608
		}
1609
1610
		if ($this->_validate_insert($table) === FALSE)
1611
		{
1612
			return FALSE;
1613
		}
1614
1615
		$sql = $this->_insert(
1616
			$this->protect_identifiers(
1617
				$this->qb_from[0], TRUE, $escape, FALSE
1618
			),
1619
			array_keys($this->qb_set),
1620
			array_values($this->qb_set)
1621
		);
1622
1623
		$this->_reset_write();
1624
		return $this->query($sql);
1625
	}
1626
1627
	// --------------------------------------------------------------------
1628
1629
	/**
1630
	 * Validate Insert
1631
	 *
1632
	 * This method is used by both insert() and get_compiled_insert() to
1633
	 * validate that the there data is actually being set and that table
1634
	 * has been chosen to be inserted into.
1635
	 *
1636
	 * @param	string	the table to insert data into
1637
	 * @return	string
1638
	 */
1639
	protected function _validate_insert($table = '')
1640
	{
1641 View Code Duplication
		if (count($this->qb_set) === 0)
1642
		{
1643
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1644
		}
1645
1646 View Code Duplication
		if ($table !== '')
1647
		{
1648
			$this->qb_from[0] = $table;
1649
		}
1650
		elseif ( ! isset($this->qb_from[0]))
1651
		{
1652
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1653
		}
1654
1655
		return TRUE;
1656
	}
1657
1658
	// --------------------------------------------------------------------
1659
1660
	/**
1661
	 * Replace
1662
	 *
1663
	 * Compiles an replace into string and runs the query
1664
	 *
1665
	 * @param	string	the table to replace data into
1666
	 * @param	array	an associative array of insert values
1667
	 * @return	object
1668
	 */
1669
	public function replace($table = '', $set = NULL)
1670
	{
1671
		if ($set !== NULL)
1672
		{
1673
			$this->set($set);
1674
		}
1675
1676 View Code Duplication
		if (count($this->qb_set) === 0)
1677
		{
1678
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1679
		}
1680
1681 View Code Duplication
		if ($table === '')
1682
		{
1683
			if ( ! isset($this->qb_from[0]))
1684
			{
1685
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1686
			}
1687
1688
			$table = $this->qb_from[0];
1689
		}
1690
1691
		$sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set));
1692
1693
		$this->_reset_write();
1694
		return $this->query($sql);
1695
	}
1696
1697
	// --------------------------------------------------------------------
1698
1699
	/**
1700
	 * Replace statement
1701
	 *
1702
	 * Generates a platform-specific replace string from the supplied data
1703
	 *
1704
	 * @param	string	the table name
1705
	 * @param	array	the insert keys
1706
	 * @param	array	the insert values
1707
	 * @return	string
1708
	 */
1709
	protected function _replace($table, $keys, $values)
1710
	{
1711
		return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1712
	}
1713
1714
	// --------------------------------------------------------------------
1715
1716
	/**
1717
	 * FROM tables
1718
	 *
1719
	 * Groups tables in FROM clauses if needed, so there is no confusion
1720
	 * about operator precedence.
1721
	 *
1722
	 * Note: This is only used (and overridden) by MySQL and CUBRID.
1723
	 *
1724
	 * @return	string
1725
	 */
1726
	protected function _from_tables()
1727
	{
1728
		return implode(', ', $this->qb_from);
1729
	}
1730
1731
	// --------------------------------------------------------------------
1732
1733
	/**
1734
	 * Get UPDATE query string
1735
	 *
1736
	 * Compiles an update query and returns the sql
1737
	 *
1738
	 * @param	string	the table to update
1739
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1740
	 * @return	string
1741
	 */
1742
	public function get_compiled_update($table = '', $reset = TRUE)
1743
	{
1744
		// Combine any cached components with the current statements
1745
		$this->_merge_cache();
1746
1747
		if ($this->_validate_update($table) === FALSE)
1748
		{
1749
			return FALSE;
1750
		}
1751
1752
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1753
1754
		if ($reset === TRUE)
1755
		{
1756
			$this->_reset_write();
1757
		}
1758
1759
		return $sql;
1760
	}
1761
1762
	// --------------------------------------------------------------------
1763
1764
	/**
1765
	 * UPDATE
1766
	 *
1767
	 * Compiles an update string and runs the query.
1768
	 *
1769
	 * @param	string	$table
1770
	 * @param	array	$set	An associative array of update values
1771
	 * @param	mixed	$where
1772
	 * @param	int	$limit
1773
	 * @return	object
1774
	 */
1775
	public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
1776
	{
1777
		// Combine any cached components with the current statements
1778
		$this->_merge_cache();
1779
1780
		if ($set !== NULL)
1781
		{
1782
			$this->set($set);
1783
		}
1784
1785
		if ($this->_validate_update($table) === FALSE)
1786
		{
1787
			return FALSE;
1788
		}
1789
1790
		if ($where !== NULL)
1791
		{
1792
			$this->where($where);
1793
		}
1794
1795
		if ( ! empty($limit))
1796
		{
1797
			$this->limit($limit);
1798
		}
1799
1800
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1801
		$this->_reset_write();
1802
		return $this->query($sql);
1803
	}
1804
1805
	// --------------------------------------------------------------------
1806
1807
	/**
1808
	 * Validate Update
1809
	 *
1810
	 * This method is used by both update() and get_compiled_update() to
1811
	 * validate that data is actually being set and that a table has been
1812
	 * chosen to be update.
1813
	 *
1814
	 * @param	string	the table to update data on
1815
	 * @return	bool
1816
	 */
1817
	protected function _validate_update($table)
1818
	{
1819 View Code Duplication
		if (count($this->qb_set) === 0)
1820
		{
1821
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1822
		}
1823
1824
		if ($table !== '')
1825
		{
1826
			$this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE));
1827
		}
1828
		elseif ( ! isset($this->qb_from[0]))
1829
		{
1830
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1831
		}
1832
1833
		return TRUE;
1834
	}
1835
1836
	// --------------------------------------------------------------------
1837
1838
	/**
1839
	 * Update_Batch
1840
	 *
1841
	 * Compiles an update string and runs the query
1842
	 *
1843
	 * @param	string	the table to retrieve the results from
1844
	 * @param	array	an associative array of update values
1845
	 * @param	string	the where key
1846
	 * @return	int	number of rows affected or FALSE on failure
1847
	 */
1848
	public function update_batch($table = '', $set = NULL, $index = NULL)
1849
	{
1850
		// Combine any cached components with the current statements
1851
		$this->_merge_cache();
1852
1853
		if ($index === NULL)
1854
		{
1855
			return ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE;
1856
		}
1857
1858
		if ($set !== NULL)
1859
		{
1860
			$this->set_update_batch($set, $index);
1861
		}
1862
1863 View Code Duplication
		if (count($this->qb_set) === 0)
1864
		{
1865
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
1866
		}
1867
1868 View Code Duplication
		if ($table === '')
1869
		{
1870
			if ( ! isset($this->qb_from[0]))
1871
			{
1872
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1873
			}
1874
1875
			$table = $this->qb_from[0];
1876
		}
1877
1878
		// Batch this baby
1879
		$affected_rows = 0;
1880 View Code Duplication
		for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100)
1881
		{
1882
			$this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set, $i, 100), $this->protect_identifiers($index)));
1883
			$affected_rows += $this->affected_rows();
1884
			$this->qb_where = array();
1885
		}
1886
1887
		$this->_reset_write();
1888
		return $affected_rows;
1889
	}
1890
1891
	// --------------------------------------------------------------------
1892
1893
	/**
1894
	 * Update_Batch statement
1895
	 *
1896
	 * Generates a platform-specific batch update string from the supplied data
1897
	 *
1898
	 * @param	string	$table	Table name
1899
	 * @param	array	$values	Update data
1900
	 * @param	string	$index	WHERE key
1901
	 * @return	string
1902
	 */
1903 View Code Duplication
	protected function _update_batch($table, $values, $index)
1904
	{
1905
		$ids = array();
1906
		foreach ($values as $key => $val)
1907
		{
1908
			$ids[] = $val[$index];
1909
1910
			foreach (array_keys($val) as $field)
1911
			{
1912
				if ($field !== $index)
1913
				{
1914
					$final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
1915
				}
1916
			}
1917
		}
1918
1919
		$cases = '';
1920
		foreach ($final as $k => $v)
1921
		{
1922
			$cases .= $k." = CASE \n"
1923
				.implode("\n", $v)."\n"
1924
				.'ELSE '.$k.' END, ';
1925
		}
1926
1927
		$this->where($index.' IN('.implode(',', $ids).')', NULL, FALSE);
1928
1929
		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
1930
	}
1931
1932
	// --------------------------------------------------------------------
1933
1934
	/**
1935
	 * The "set_update_batch" function.  Allows key/value pairs to be set for batch updating
1936
	 *
1937
	 * @param	array
1938
	 * @param	string
1939
	 * @param	bool
1940
	 * @return	CI_DB_query_builder
1941
	 */
1942
	public function set_update_batch($key, $index = '', $escape = NULL)
1943
	{
1944
		$key = $this->_object_to_array_batch($key);
1945
1946
		if ( ! is_array($key))
1947
		{
1948
			// @todo error
1949
		}
1950
1951
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1952
1953
		foreach ($key as $k => $v)
1954
		{
1955
			$index_set = FALSE;
1956
			$clean = array();
1957
			foreach ($v as $k2 => $v2)
1958
			{
1959
				if ($k2 === $index)
1960
				{
1961
					$index_set = TRUE;
1962
				}
1963
1964
				$clean[$this->protect_identifiers($k2, FALSE, $escape)] = ($escape === FALSE) ? $v2 : $this->escape($v2);
1965
			}
1966
1967
			if ($index_set === FALSE)
1968
			{
1969
				return $this->display_error('db_batch_missing_index');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->display_error('db_batch_missing_index'); of type string|null adds the type string to the return on line 1969 which is incompatible with the return type documented by CI_DB_query_builder::set_update_batch of type CI_DB_query_builder|null.
Loading history...
1970
			}
1971
1972
			$this->qb_set[] = $clean;
1973
		}
1974
1975
		return $this;
1976
	}
1977
1978
	// --------------------------------------------------------------------
1979
1980
	/**
1981
	 * Empty Table
1982
	 *
1983
	 * Compiles a delete string and runs "DELETE FROM table"
1984
	 *
1985
	 * @param	string	the table to empty
1986
	 * @return	object
1987
	 */
1988 View Code Duplication
	public function empty_table($table = '')
1989
	{
1990
		if ($table === '')
1991
		{
1992
			if ( ! isset($this->qb_from[0]))
1993
			{
1994
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
1995
			}
1996
1997
			$table = $this->qb_from[0];
1998
		}
1999
		else
2000
		{
2001
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2002
		}
2003
2004
		$sql = $this->_delete($table);
2005
		$this->_reset_write();
2006
		return $this->query($sql);
2007
	}
2008
2009
	// --------------------------------------------------------------------
2010
2011
	/**
2012
	 * Truncate
2013
	 *
2014
	 * Compiles a truncate string and runs the query
2015
	 * If the database does not support the truncate() command
2016
	 * This function maps to "DELETE FROM table"
2017
	 *
2018
	 * @param	string	the table to truncate
2019
	 * @return	object
2020
	 */
2021 View Code Duplication
	public function truncate($table = '')
2022
	{
2023
		if ($table === '')
2024
		{
2025
			if ( ! isset($this->qb_from[0]))
2026
			{
2027
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2028
			}
2029
2030
			$table = $this->qb_from[0];
2031
		}
2032
		else
2033
		{
2034
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2035
		}
2036
2037
		$sql = $this->_truncate($table);
2038
		$this->_reset_write();
2039
		return $this->query($sql);
2040
	}
2041
2042
	// --------------------------------------------------------------------
2043
2044
	/**
2045
	 * Truncate statement
2046
	 *
2047
	 * Generates a platform-specific truncate string from the supplied data
2048
	 *
2049
	 * If the database does not support the truncate() command,
2050
	 * then this method maps to 'DELETE FROM table'
2051
	 *
2052
	 * @param	string	the table name
2053
	 * @return	string
2054
	 */
2055
	protected function _truncate($table)
2056
	{
2057
		return 'TRUNCATE '.$table;
2058
	}
2059
2060
	// --------------------------------------------------------------------
2061
2062
	/**
2063
	 * Get DELETE query string
2064
	 *
2065
	 * Compiles a delete query string and returns the sql
2066
	 *
2067
	 * @param	string	the table to delete from
2068
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
2069
	 * @return	string
2070
	 */
2071
	public function get_compiled_delete($table = '', $reset = TRUE)
2072
	{
2073
		$this->return_delete_sql = TRUE;
2074
		$sql = $this->delete($table, '', NULL, $reset);
2075
		$this->return_delete_sql = FALSE;
2076
		return $sql;
2077
	}
2078
2079
	// --------------------------------------------------------------------
2080
2081
	/**
2082
	 * Delete
2083
	 *
2084
	 * Compiles a delete string and runs the query
2085
	 *
2086
	 * @param	mixed	the table(s) to delete from. String or array
2087
	 * @param	mixed	the where clause
2088
	 * @param	mixed	the limit clause
2089
	 * @param	bool
2090
	 * @return	mixed
2091
	 */
2092
	public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
2093
	{
2094
		// Combine any cached components with the current statements
2095
		$this->_merge_cache();
2096
2097
		if ($table === '')
2098
		{
2099
			if ( ! isset($this->qb_from[0]))
2100
			{
2101
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2102
			}
2103
2104
			$table = $this->qb_from[0];
2105
		}
2106
		elseif (is_array($table))
2107
		{
2108
			empty($where) && $reset_data = FALSE;
2109
2110
			foreach ($table as $single_table)
2111
			{
2112
				$this->delete($single_table, $where, $limit, $reset_data);
2113
			}
2114
2115
			return;
2116
		}
2117
		else
2118
		{
2119
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2120
		}
2121
2122
		if ($where !== '')
2123
		{
2124
			$this->where($where);
2125
		}
2126
2127
		if ( ! empty($limit))
2128
		{
2129
			$this->limit($limit);
2130
		}
2131
2132
		if (count($this->qb_where) === 0)
2133
		{
2134
			return ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE;
2135
		}
2136
2137
		$sql = $this->_delete($table);
2138
		if ($reset_data)
2139
		{
2140
			$this->_reset_write();
2141
		}
2142
2143
		return ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql);
2144
	}
2145
2146
	// --------------------------------------------------------------------
2147
2148
	/**
2149
	 * Delete statement
2150
	 *
2151
	 * Generates a platform-specific delete string from the supplied data
2152
	 *
2153
	 * @param	string	the table name
2154
	 * @return	string
2155
	 */
2156
	protected function _delete($table)
2157
	{
2158
		return 'DELETE FROM '.$table.$this->_compile_wh('qb_where')
2159
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
2160
	}
2161
2162
	// --------------------------------------------------------------------
2163
2164
	/**
2165
	 * DB Prefix
2166
	 *
2167
	 * Prepends a database prefix if one exists in configuration
2168
	 *
2169
	 * @param	string	the table
2170
	 * @return	string
2171
	 */
2172
	public function dbprefix($table = '')
2173
	{
2174
		if ($table === '')
2175
		{
2176
			$this->display_error('db_table_name_required');
2177
		}
2178
2179
		return $this->dbprefix.$table;
2180
	}
2181
2182
	// --------------------------------------------------------------------
2183
2184
	/**
2185
	 * Set DB Prefix
2186
	 *
2187
	 * Set's the DB Prefix to something new without needing to reconnect
2188
	 *
2189
	 * @param	string	the prefix
2190
	 * @return	string
2191
	 */
2192
	public function set_dbprefix($prefix = '')
2193
	{
2194
		return $this->dbprefix = $prefix;
2195
	}
2196
2197
	// --------------------------------------------------------------------
2198
2199
	/**
2200
	 * Track Aliases
2201
	 *
2202
	 * Used to track SQL statements written with aliased tables.
2203
	 *
2204
	 * @param	string	The table to inspect
2205
	 * @return	string
2206
	 */
2207
	protected function _track_aliases($table)
2208
	{
2209
		if (is_array($table))
2210
		{
2211
			foreach ($table as $t)
2212
			{
2213
				$this->_track_aliases($t);
2214
			}
2215
			return;
2216
		}
2217
2218
		// Does the string contain a comma?  If so, we need to separate
2219
		// the string into discreet statements
2220
		if (strpos($table, ',') !== FALSE)
2221
		{
2222
			return $this->_track_aliases(explode(',', $table));
2223
		}
2224
2225
		// if a table alias is used we can recognize it by a space
2226
		if (strpos($table, ' ') !== FALSE)
2227
		{
2228
			// if the alias is written with the AS keyword, remove it
2229
			$table = preg_replace('/\s+AS\s+/i', ' ', $table);
2230
2231
			// Grab the alias
2232
			$table = trim(strrchr($table, ' '));
2233
2234
			// Store the alias, if it doesn't already exist
2235
			if ( ! in_array($table, $this->qb_aliased_tables))
2236
			{
2237
				$this->qb_aliased_tables[] = $table;
2238
			}
2239
		}
2240
	}
2241
2242
	// --------------------------------------------------------------------
2243
2244
	/**
2245
	 * Compile the SELECT statement
2246
	 *
2247
	 * Generates a query string based on which functions were used.
2248
	 * Should not be called directly.
2249
	 *
2250
	 * @param	bool	$select_override
2251
	 * @return	string
2252
	 */
2253
	protected function _compile_select($select_override = FALSE)
2254
	{
2255
		// Combine any cached components with the current statements
2256
		$this->_merge_cache();
2257
2258
		// Write the "select" portion of the query
2259
		if ($select_override !== FALSE)
2260
		{
2261
			$sql = $select_override;
2262
		}
2263
		else
2264
		{
2265
			$sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';
2266
2267
			if (count($this->qb_select) === 0)
2268
			{
2269
				$sql .= '*';
2270
			}
2271
			else
2272
			{
2273
				// Cycle through the "select" portion of the query and prep each column name.
2274
				// The reason we protect identifiers here rather than in the select() function
2275
				// is because until the user calls the from() function we don't know if there are aliases
2276
				foreach ($this->qb_select as $key => $val)
2277
				{
2278
					$no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL;
2279
					$this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape);
2280
				}
2281
2282
				$sql .= implode(', ', $this->qb_select);
2283
			}
2284
		}
2285
2286
		// Write the "FROM" portion of the query
2287
		if (count($this->qb_from) > 0)
2288
		{
2289
			$sql .= "\nFROM ".$this->_from_tables();
2290
		}
2291
2292
		// Write the "JOIN" portion of the query
2293
		if (count($this->qb_join) > 0)
2294
		{
2295
			$sql .= "\n".implode("\n", $this->qb_join);
2296
		}
2297
2298
		$sql .= $this->_compile_wh('qb_where')
2299
			.$this->_compile_group_by()
2300
			.$this->_compile_wh('qb_having')
2301
			.$this->_compile_order_by(); // ORDER BY
2302
2303
		// LIMIT
2304
		if ($this->qb_limit)
2305
		{
2306
			return $this->_limit($sql."\n");
2307
		}
2308
2309
		return $sql;
2310
	}
2311
2312
	// --------------------------------------------------------------------
2313
2314
	/**
2315
	 * Compile WHERE, HAVING statements
2316
	 *
2317
	 * Escapes identifiers in WHERE and HAVING statements at execution time.
2318
	 *
2319
	 * Required so that aliases are tracked properly, regardless of wether
2320
	 * where(), or_where(), having(), or_having are called prior to from(),
2321
	 * join() and dbprefix is added only if needed.
2322
	 *
2323
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
2324
	 * @return	string	SQL statement
2325
	 */
2326
	protected function _compile_wh($qb_key)
2327
	{
2328
		if (count($this->$qb_key) > 0)
2329
		{
2330
			for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++)
2331
			{
2332
				// Is this condition already compiled?
2333
				if (is_string($this->{$qb_key}[$i]))
2334
				{
2335
					continue;
2336
				}
2337
				elseif ($this->{$qb_key}[$i]['escape'] === FALSE)
2338
				{
2339
					$this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'];
2340
					continue;
2341
				}
2342
2343
				// Split multiple conditions
2344
				$conditions = preg_split(
2345
					'/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i',
2346
					$this->{$qb_key}[$i]['condition'],
2347
					-1,
2348
					PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
2349
				);
2350
2351
				for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++)
2352
				{
2353
					if (($op = $this->_get_operator($conditions[$ci])) === FALSE
2354
						OR ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(?<!\)))?(\)?)$/i', $conditions[$ci], $matches))
2355
					{
2356
						continue;
2357
					}
2358
2359
					// $matches = array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2360
					//	0 => '(test <= foo)',	/* the whole thing */
2361
					//	1 => '(',		/* optional */
2362
					//	2 => 'test',		/* the field name */
2363
					//	3 => ' <= ',		/* $op */
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2364
					//	4 => 'foo',		/* optional, if $op is e.g. 'IS NULL' */
2365
					//	5 => ')'		/* optional */
2366
					// );
2367
2368
					if ( ! empty($matches[4]))
2369
					{
2370
						$this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4]));
2371
						$matches[4] = ' '.$matches[4];
2372
					}
2373
2374
					$conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2]))
2375
						.' '.trim($matches[3]).$matches[4].$matches[5];
2376
				}
2377
2378
				$this->{$qb_key}[$i] = implode('', $conditions);
2379
			}
2380
2381
			return ($qb_key === 'qb_having' ? "\nHAVING " : "\nWHERE ")
2382
				.implode("\n", $this->$qb_key);
2383
		}
2384
2385
		return '';
2386
	}
2387
2388
	// --------------------------------------------------------------------
2389
2390
	/**
2391
	 * Compile GROUP BY
2392
	 *
2393
	 * Escapes identifiers in GROUP BY statements at execution time.
2394
	 *
2395
	 * Required so that aliases are tracked properly, regardless of wether
2396
	 * group_by() is called prior to from(), join() and dbprefix is added
2397
	 * only if needed.
2398
	 *
2399
	 * @return	string	SQL statement
2400
	 */
2401
	protected function _compile_group_by()
2402
	{
2403
		if (count($this->qb_groupby) > 0)
2404
		{
2405
			for ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++)
2406
			{
2407
				// Is it already compiled?
2408
				if (is_string($this->qb_groupby[$i]))
2409
				{
2410
					continue;
2411
				}
2412
2413
				$this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field']))
2414
					? $this->qb_groupby[$i]['field']
2415
					: $this->protect_identifiers($this->qb_groupby[$i]['field']);
2416
			}
2417
2418
			return "\nGROUP BY ".implode(', ', $this->qb_groupby);
2419
		}
2420
2421
		return '';
2422
	}
2423
2424
	// --------------------------------------------------------------------
2425
2426
	/**
2427
	 * Compile ORDER BY
2428
	 *
2429
	 * Escapes identifiers in ORDER BY statements at execution time.
2430
	 *
2431
	 * Required so that aliases are tracked properly, regardless of wether
2432
	 * order_by() is called prior to from(), join() and dbprefix is added
2433
	 * only if needed.
2434
	 *
2435
	 * @return	string	SQL statement
2436
	 */
2437
	protected function _compile_order_by()
2438
	{
2439
		if (is_array($this->qb_orderby) && count($this->qb_orderby) > 0)
2440
		{
2441
			for ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++)
2442
			{
2443
				if ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field']))
2444
				{
2445
					$this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']);
2446
				}
2447
2448
				$this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction'];
2449
			}
2450
2451
			return $this->qb_orderby = "\nORDER BY ".implode(', ', $this->qb_orderby);
2452
		}
2453
		elseif (is_string($this->qb_orderby))
2454
		{
2455
			return $this->qb_orderby;
2456
		}
2457
2458
		return '';
2459
	}
2460
2461
	// --------------------------------------------------------------------
2462
2463
	/**
2464
	 * Object to Array
2465
	 *
2466
	 * Takes an object as input and converts the class variables to array key/vals
2467
	 *
2468
	 * @param	object
2469
	 * @return	array
2470
	 */
2471
	protected function _object_to_array($object)
2472
	{
2473
		if ( ! is_object($object))
2474
		{
2475
			return $object;
2476
		}
2477
2478
		$array = array();
2479
		foreach (get_object_vars($object) as $key => $val)
2480
		{
2481
			// There are some built in keys we need to ignore for this conversion
2482
			if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')
2483
			{
2484
				$array[$key] = $val;
2485
			}
2486
		}
2487
2488
		return $array;
2489
	}
2490
2491
	// --------------------------------------------------------------------
2492
2493
	/**
2494
	 * Object to Array
2495
	 *
2496
	 * Takes an object as input and converts the class variables to array key/vals
2497
	 *
2498
	 * @param	object
2499
	 * @return	array
2500
	 */
2501
	protected function _object_to_array_batch($object)
2502
	{
2503
		if ( ! is_object($object))
2504
		{
2505
			return $object;
2506
		}
2507
2508
		$array = array();
2509
		$out = get_object_vars($object);
2510
		$fields = array_keys($out);
2511
2512
		foreach ($fields as $val)
2513
		{
2514
			// There are some built in keys we need to ignore for this conversion
2515
			if ($val !== '_parent_name')
2516
			{
2517
				$i = 0;
2518
				foreach ($out[$val] as $data)
2519
				{
2520
					$array[$i++][$val] = $data;
2521
				}
2522
			}
2523
		}
2524
2525
		return $array;
2526
	}
2527
2528
	// --------------------------------------------------------------------
2529
2530
	/**
2531
	 * Start Cache
2532
	 *
2533
	 * Starts QB caching
2534
	 *
2535
	 * @return	CI_DB_query_builder
2536
	 */
2537
	public function start_cache()
2538
	{
2539
		$this->qb_caching = TRUE;
2540
		return $this;
2541
	}
2542
2543
	// --------------------------------------------------------------------
2544
2545
	/**
2546
	 * Stop Cache
2547
	 *
2548
	 * Stops QB caching
2549
	 *
2550
	 * @return	CI_DB_query_builder
2551
	 */
2552
	public function stop_cache()
2553
	{
2554
		$this->qb_caching = FALSE;
2555
		return $this;
2556
	}
2557
2558
	// --------------------------------------------------------------------
2559
2560
	/**
2561
	 * Flush Cache
2562
	 *
2563
	 * Empties the QB cache
2564
	 *
2565
	 * @return	CI_DB_query_builder
2566
	 */
2567
	public function flush_cache()
2568
	{
2569
		$this->_reset_run(array(
2570
			'qb_cache_select'		=> array(),
2571
			'qb_cache_from'			=> array(),
2572
			'qb_cache_join'			=> array(),
2573
			'qb_cache_where'		=> array(),
2574
			'qb_cache_groupby'		=> array(),
2575
			'qb_cache_having'		=> array(),
2576
			'qb_cache_orderby'		=> array(),
2577
			'qb_cache_set'			=> array(),
2578
			'qb_cache_exists'		=> array(),
2579
			'qb_cache_no_escape'	=> array()
2580
		));
2581
2582
		return $this;
2583
	}
2584
2585
	// --------------------------------------------------------------------
2586
2587
	/**
2588
	 * Merge Cache
2589
	 *
2590
	 * When called, this function merges any cached QB arrays with
2591
	 * locally called ones.
2592
	 *
2593
	 * @return	void
2594
	 */
2595
	protected function _merge_cache()
2596
	{
2597
		if (count($this->qb_cache_exists) === 0)
2598
		{
2599
			return;
2600
		}
2601
		elseif (in_array('select', $this->qb_cache_exists, TRUE))
2602
		{
2603
			$qb_no_escape = $this->qb_cache_no_escape;
2604
		}
2605
2606
		foreach (array_unique($this->qb_cache_exists) as $val) // select, from, etc.
2607
		{
2608
			$qb_variable	= 'qb_'.$val;
2609
			$qb_cache_var	= 'qb_cache_'.$val;
2610
			$qb_new 	= $this->$qb_cache_var;
2611
2612
			for ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++)
2613
			{
2614
				if ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE))
2615
				{
2616
					$qb_new[] = $this->{$qb_variable}[$i];
2617
					if ($val === 'select')
2618
					{
2619
						$qb_no_escape[] = $this->qb_no_escape[$i];
2620
					}
2621
				}
2622
			}
2623
2624
			$this->$qb_variable = $qb_new;
2625
			if ($val === 'select')
2626
			{
2627
				$this->qb_no_escape = $qb_no_escape;
2628
			}
2629
		}
2630
2631
		// If we are "protecting identifiers" we need to examine the "from"
2632
		// portion of the query to determine if there are any aliases
2633
		if ($this->_protect_identifiers === TRUE && count($this->qb_cache_from) > 0)
2634
		{
2635
			$this->_track_aliases($this->qb_from);
2636
		}
2637
	}
2638
2639
	// --------------------------------------------------------------------
2640
2641
	/**
2642
	 * Is literal
2643
	 *
2644
	 * Determines if a string represents a literal value or a field name
2645
	 *
2646
	 * @param	string	$str
2647
	 * @return	bool
2648
	 */
2649
	protected function _is_literal($str)
2650
	{
2651
		$str = trim($str);
2652
2653
		if (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE))
2654
		{
2655
			return TRUE;
2656
		}
2657
2658
		static $_str;
2659
2660
		if (empty($_str))
2661
		{
2662
			$_str = ($this->_escape_char !== '"')
2663
				? array('"', "'") : array("'");
2664
		}
2665
2666
		return in_array($str[0], $_str, TRUE);
2667
	}
2668
2669
	// --------------------------------------------------------------------
2670
2671
	/**
2672
	 * Reset Query Builder values.
2673
	 *
2674
	 * Publicly-visible method to reset the QB values.
2675
	 *
2676
	 * @return	CI_DB_query_builder
2677
	 */
2678
	public function reset_query()
2679
	{
2680
		$this->_reset_select();
2681
		$this->_reset_write();
2682
		return $this;
2683
	}
2684
2685
	// --------------------------------------------------------------------
2686
2687
	/**
2688
	 * Resets the query builder values.  Called by the get() function
2689
	 *
2690
	 * @param	array	An array of fields to reset
2691
	 * @return	void
2692
	 */
2693
	protected function _reset_run($qb_reset_items)
2694
	{
2695
		foreach ($qb_reset_items as $item => $default_value)
2696
		{
2697
			$this->$item = $default_value;
2698
		}
2699
	}
2700
2701
	// --------------------------------------------------------------------
2702
2703
	/**
2704
	 * Resets the query builder values.  Called by the get() function
2705
	 *
2706
	 * @return	void
2707
	 */
2708
	protected function _reset_select()
2709
	{
2710
		$this->_reset_run(array(
2711
			'qb_select'		=> array(),
2712
			'qb_from'		=> array(),
2713
			'qb_join'		=> array(),
2714
			'qb_where'		=> array(),
2715
			'qb_groupby'		=> array(),
2716
			'qb_having'		=> array(),
2717
			'qb_orderby'		=> array(),
2718
			'qb_aliased_tables'	=> array(),
2719
			'qb_no_escape'		=> array(),
2720
			'qb_distinct'		=> FALSE,
2721
			'qb_limit'		=> FALSE,
2722
			'qb_offset'		=> FALSE
2723
		));
2724
	}
2725
2726
	// --------------------------------------------------------------------
2727
2728
	/**
2729
	 * Resets the query builder "write" values.
2730
	 *
2731
	 * Called by the insert() update() insert_batch() update_batch() and delete() functions
2732
	 *
2733
	 * @return	void
2734
	 */
2735
	protected function _reset_write()
2736
	{
2737
		$this->_reset_run(array(
2738
			'qb_set'	=> array(),
2739
			'qb_from'	=> array(),
2740
			'qb_join'	=> array(),
2741
			'qb_where'	=> array(),
2742
			'qb_orderby'	=> array(),
2743
			'qb_keys'	=> array(),
2744
			'qb_limit'	=> FALSE
2745
		));
2746
	}
2747
2748
}
2749