blob: dab56c7e783d169e297185fbe2bc1dd3126dd8f6 [file] [log] [blame]
Derek Allardd2df9bc2007-04-15 17:41:17 +00001<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Derek Allardd2df9bc2007-04-15 17:41:17 +00009 * @copyright Copyright (c) 2006, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * MySQLi Database Adapter Class - MySQLi only works with PHP 5
20 *
21 * Note: _DB is an extender class that the app controller
22 * creates dynamically based on whether the active record
23 * class is being used or not.
24 *
25 * @package CodeIgniter
26 * @subpackage Drivers
27 * @category Database
Derek Allard3d879d52008-01-18 19:41:32 +000028 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000029 * @link http://codeigniter.com/user_guide/database/
Derek Allardd2df9bc2007-04-15 17:41:17 +000030 */
31class CI_DB_mysqli_driver extends CI_DB {
32
33 /**
Derek Allard694b5b82007-12-18 15:58:03 +000034 * The syntax to count rows is slightly different across different
35 * database engines, so this string appears in each driver and is
36 * used for the count_all() and count_all_results() functions.
37 */
Derek Allard39b622d2008-01-16 21:10:09 +000038 var $_count_string = "SELECT COUNT(*) AS ";
Derek Allard6ddb5a12007-12-18 17:22:50 +000039 var $_random_keyword = ' RAND()'; // database specific random keyword
40
Derek Allard694b5b82007-12-18 15:58:03 +000041 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +000042 * Whether to use the MySQL "delete hack" which allows the number
43 * of affected rows to be shown. Uses a preg_replace when enabled,
44 * adding a bit more processing to all queries.
45 */
46 var $delete_hack = TRUE;
47
48 // --------------------------------------------------------------------
49
50 /**
51 * Non-persistent database connection
52 *
53 * @access private called by the base class
54 * @return resource
55 */
56 function db_connect()
57 {
58 return @mysqli_connect($this->hostname, $this->username, $this->password);
59 }
60
61 // --------------------------------------------------------------------
62
63 /**
64 * Persistent database connection
65 *
66 * @access private called by the base class
67 * @return resource
68 */
69 function db_pconnect()
70 {
71 return $this->db_connect();
72 }
73
74 // --------------------------------------------------------------------
75
76 /**
77 * Select the database
78 *
79 * @access private called by the base class
80 * @return resource
81 */
82 function db_select()
83 {
84 return @mysqli_select_db($this->conn_id, $this->database);
85 }
86
87 // --------------------------------------------------------------------
88
89 /**
Derek Allard39b622d2008-01-16 21:10:09 +000090 * Set client character set
91 *
92 * @access public
93 * @param string
94 * @param string
95 * @return resource
96 */
97 function db_set_charset($charset, $collation)
98 {
Derek Allard15648132008-02-10 21:46:18 +000099 // TODO - add support if needed
100 return TRUE;
Derek Allard39b622d2008-01-16 21:10:09 +0000101 }
102
103 // --------------------------------------------------------------------
104
105 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000106 * Version number query string
107 *
108 * @access public
109 * @return string
110 */
111 function _version()
112 {
113 return "SELECT version() AS ver";
114 }
115
116 // --------------------------------------------------------------------
117
118 /**
119 * Execute the query
120 *
121 * @access private called by the base class
122 * @param string an SQL query
123 * @return resource
124 */
125 function _execute($sql)
126 {
127 $sql = $this->_prep_query($sql);
128 $result = @mysqli_query($this->conn_id, $sql);
129 return $result;
130 }
131
132 // --------------------------------------------------------------------
133
134 /**
135 * Prep the query
136 *
137 * If needed, each database adapter can prep the query string
138 *
139 * @access private called by execute()
140 * @param string an SQL query
141 * @return string
142 */
143 function _prep_query($sql)
144 {
145 // "DELETE FROM TABLE" returns 0 affected rows This hack modifies
146 // the query so that it returns the number of affected rows
147 if ($this->delete_hack === TRUE)
148 {
149 if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
150 {
151 $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql);
152 }
153 }
154
155 return $sql;
156 }
157
158 // --------------------------------------------------------------------
159
160 /**
161 * Begin Transaction
162 *
163 * @access public
164 * @return bool
165 */
166 function trans_begin($test_mode = FALSE)
167 {
168 if ( ! $this->trans_enabled)
169 {
170 return TRUE;
171 }
172
173 // When transactions are nested we only begin/commit/rollback the outermost ones
174 if ($this->_trans_depth > 0)
175 {
176 return TRUE;
177 }
178
179 // Reset the transaction failure flag.
180 // If the $test_mode flag is set to TRUE transactions will be rolled back
181 // even if the queries produce a successful result.
182 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
183
184 $this->simple_query('SET AUTOCOMMIT=0');
185 $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
186 return TRUE;
187 }
188
189 // --------------------------------------------------------------------
190
191 /**
192 * Commit Transaction
193 *
194 * @access public
195 * @return bool
196 */
197 function trans_commit()
198 {
199 if ( ! $this->trans_enabled)
200 {
201 return TRUE;
202 }
203
204 // When transactions are nested we only begin/commit/rollback the outermost ones
205 if ($this->_trans_depth > 0)
206 {
207 return TRUE;
208 }
209
210 $this->simple_query('COMMIT');
211 $this->simple_query('SET AUTOCOMMIT=1');
212 return TRUE;
213 }
214
215 // --------------------------------------------------------------------
216
217 /**
218 * Rollback Transaction
219 *
220 * @access public
221 * @return bool
222 */
223 function trans_rollback()
224 {
225 if ( ! $this->trans_enabled)
226 {
227 return TRUE;
228 }
229
230 // When transactions are nested we only begin/commit/rollback the outermost ones
231 if ($this->_trans_depth > 0)
232 {
233 return TRUE;
234 }
235
236 $this->simple_query('ROLLBACK');
237 $this->simple_query('SET AUTOCOMMIT=1');
238 return TRUE;
239 }
240
241 // --------------------------------------------------------------------
242
243 /**
244 * Escape String
245 *
246 * @access public
247 * @param string
248 * @return string
249 */
250 function escape_str($str)
Derek Allard694b5b82007-12-18 15:58:03 +0000251 {
252 if (function_exists('mysqli_real_escape_string') AND is_resource($this->conn_id))
253 {
254 return mysqli_real_escape_string($this->conn_id, $str);
255 }
256 elseif (function_exists('mysql_escape_string'))
257 {
258 return mysql_escape_string($str);
259 }
260 else
261 {
262 return addslashes($str);
263 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000264 }
265
266 // --------------------------------------------------------------------
267
268 /**
269 * Affected Rows
270 *
271 * @access public
272 * @return integer
273 */
274 function affected_rows()
275 {
276 return @mysqli_affected_rows($this->conn_id);
277 }
278
279 // --------------------------------------------------------------------
280
281 /**
282 * Insert ID
283 *
284 * @access public
285 * @return integer
286 */
287 function insert_id()
288 {
289 return @mysqli_insert_id($this->conn_id);
290 }
291
292 // --------------------------------------------------------------------
293
294 /**
295 * "Count All" query
296 *
297 * Generates a platform-specific query string that counts all records in
298 * the specified database
299 *
300 * @access public
301 * @param string
302 * @return string
303 */
304 function count_all($table = '')
305 {
306 if ($table == '')
307 return '0';
308
Derek Allardf6cd45c2008-01-18 14:31:51 +0000309 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows'). " FROM " . $this->_protect_identifiers($this->dbprefix.$table));
Derek Allardd2df9bc2007-04-15 17:41:17 +0000310
311 if ($query->num_rows() == 0)
312 return '0';
313
314 $row = $query->row();
315 return $row->numrows;
316 }
317
318 // --------------------------------------------------------------------
319
320 /**
321 * List table query
322 *
323 * Generates a platform-specific query string so that the table names can be fetched
324 *
325 * @access private
Derek Allard39b622d2008-01-16 21:10:09 +0000326 * @param boolean
Derek Allardd2df9bc2007-04-15 17:41:17 +0000327 * @return string
328 */
Derek Allard694b5b82007-12-18 15:58:03 +0000329 function _list_tables($prefix_limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000330 {
Derek Allard694b5b82007-12-18 15:58:03 +0000331 $sql = "SHOW TABLES FROM `".$this->database."`";
332
Derek Allard39b622d2008-01-16 21:10:09 +0000333 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
Derek Allard694b5b82007-12-18 15:58:03 +0000334 {
Derek Allard39b622d2008-01-16 21:10:09 +0000335 $sql .= " LIKE '".$this->dbprefix."%'";
Derek Allard694b5b82007-12-18 15:58:03 +0000336 }
337
338 return $sql;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000339 }
340
341 // --------------------------------------------------------------------
342
343 /**
344 * Show column query
345 *
346 * Generates a platform-specific query string so that the column names can be fetched
347 *
348 * @access public
349 * @param string the table name
350 * @return string
351 */
352 function _list_columns($table = '')
353 {
354 return "SHOW COLUMNS FROM ".$this->_escape_table($table);
355 }
356
357 // --------------------------------------------------------------------
358
359 /**
360 * Field data query
361 *
362 * Generates a platform-specific query so that the column data can be retrieved
363 *
364 * @access public
365 * @param string the table name
366 * @return object
367 */
368 function _field_data($table)
369 {
370 return "SELECT * FROM ".$this->_escape_table($table)." LIMIT 1";
371 }
372
373 // --------------------------------------------------------------------
374
375 /**
376 * The error message string
377 *
378 * @access private
379 * @return string
380 */
381 function _error_message()
382 {
383 return mysqli_error($this->conn_id);
384 }
385
386 // --------------------------------------------------------------------
387
388 /**
389 * The error message number
390 *
391 * @access private
392 * @return integer
393 */
394 function _error_number()
395 {
396 return mysqli_errno($this->conn_id);
397 }
398
399 // --------------------------------------------------------------------
400
401 /**
402 * Escape Table Name
403 *
404 * This function adds backticks if the table name has a period
405 * in it. Some DBs will get cranky unless periods are escaped
406 *
407 * @access private
408 * @param string the table name
409 * @return string
410 */
411 function _escape_table($table)
412 {
Derek Allardc0743382008-02-11 05:54:44 +0000413 if (strpos($table, '.') !== FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000414 {
Derek Allardc0743382008-02-11 05:54:44 +0000415 $table = '`' . str_replace('.', '`.`', $table) . '`';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000416 }
417
418 return $table;
419 }
420
421 // --------------------------------------------------------------------
422
423 /**
Derek Allard39b622d2008-01-16 21:10:09 +0000424 * Protect Identifiers
425 *
426 * This function adds backticks if appropriate based on db type
427 *
428 * @access private
429 * @param mixed the item to escape
430 * @param boolean only affect the first word
431 * @return mixed the item with backticks
432 */
433 function _protect_identifiers($item, $first_word_only = FALSE)
434 {
435 if (is_array($item))
436 {
437 $escaped_array = array();
438
439 foreach($item as $k=>$v)
440 {
441 $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v, $first_word_only);
442 }
443
444 return $escaped_array;
445 }
446
447 // This function may get "item1 item2" as a string, and so
448 // we may need "`item1` `item2`" and not "`item1 item2`"
Derek Allard61579382008-01-16 22:22:42 +0000449 if (ctype_alnum($item) === FALSE)
Derek Allard39b622d2008-01-16 21:10:09 +0000450 {
Derek Allard9b3e7b52008-02-04 23:20:34 +0000451 if (strpos($item, '.') !== FALSE)
452 {
453 $aliased_tables = implode(".",$this->ar_aliased_tables).'.';
454 $table_name = substr($item, 0, strpos($item, '.')+1);
455 $item = (strpos($aliased_tables, $table_name) !== FALSE) ? $item = $item : $this->dbprefix.$item;
456 }
457
Derek Allard39b622d2008-01-16 21:10:09 +0000458 // This function may get "field >= 1", and need it to return "`field` >= 1"
Derek Allard61579382008-01-16 22:22:42 +0000459 $lbound = ($first_word_only === TRUE) ? '' : '|\s|\(';
Derek Allard39b622d2008-01-16 21:10:09 +0000460
Derek Allard61579382008-01-16 22:22:42 +0000461 $item = preg_replace('/(^'.$lbound.')([\w\d\-\_]+?)(\s|\)|$)/iS', '$1`$2`$3', $item);
462 }
463 else
464 {
465 return "`{$item}`";
Derek Allard39b622d2008-01-16 21:10:09 +0000466 }
467
468 $exceptions = array('AS', '/', '-', '%', '+', '*');
469
470 foreach ($exceptions as $exception)
471 {
Derek Allard61579382008-01-16 22:22:42 +0000472
Derek Allard39b622d2008-01-16 21:10:09 +0000473 if (stristr($item, " `{$exception}` ") !== FALSE)
474 {
475 $item = preg_replace('/ `('.preg_quote($exception).')` /i', ' $1 ', $item);
476 }
477 }
Derek Allard39b622d2008-01-16 21:10:09 +0000478 return $item;
479 }
480
481 // --------------------------------------------------------------------
482
483 /**
Derek Jonesc6ad0232008-01-29 18:44:54 +0000484 * From Tables
485 *
486 * This function implicitly groups FROM tables so there is no confusion
487 * about operator precedence in harmony with SQL standards
488 *
489 * @access public
490 * @param type
491 * @return type
492 */
493 function _from_tables($tables)
494 {
495 if (! is_array($tables))
496 {
497 $tables = array($tables);
498 }
499
500 return '('.implode(', ', $tables).')';
501 }
502
503 // --------------------------------------------------------------------
504
505 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000506 * Insert statement
507 *
508 * Generates a platform-specific insert string from the supplied data
509 *
510 * @access public
511 * @param string the table name
512 * @param array the insert keys
513 * @param array the insert values
514 * @return string
515 */
516 function _insert($table, $keys, $values)
517 {
518 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
519 }
520
521 // --------------------------------------------------------------------
522
523 /**
524 * Update statement
525 *
526 * Generates a platform-specific update string from the supplied data
527 *
528 * @access public
529 * @param string the table name
530 * @param array the update data
531 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000532 * @param array the orderby clause
533 * @param array the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000534 * @return string
535 */
Derek Allard39b622d2008-01-16 21:10:09 +0000536 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000537 {
538 foreach($values as $key => $val)
539 {
540 $valstr[] = $key." = ".$val;
541 }
Derek Allardda6d2402007-12-19 14:49:29 +0000542
543 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000544
545 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000546
Derek Allard32cf7eb2008-02-05 16:03:50 +0000547 $sql = "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr);
548 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
549 $sql .= $orderby.$limit;
550
551 return $sql;
Derek Allard39b622d2008-01-16 21:10:09 +0000552 }
553
554
555 // --------------------------------------------------------------------
556
557 /**
558 * Truncate statement
559 *
560 * Generates a platform-specific truncate string from the supplied data
561 * If the database does not support the truncate() command
562 * This function maps to "DELETE FROM table"
563 *
564 * @access public
565 * @param string the table name
566 * @return string
567 */
568 function _truncate($table)
569 {
570 return "TRUNCATE ".$this->_escape_table($table);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000571 }
572
573 // --------------------------------------------------------------------
574
575 /**
576 * Delete statement
577 *
578 * Generates a platform-specific delete string from the supplied data
579 *
580 * @access public
581 * @param string the table name
582 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000583 * @param string the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000584 * @return string
585 */
Derek Allard39b622d2008-01-16 21:10:09 +0000586 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000587 {
Derek Allard39b622d2008-01-16 21:10:09 +0000588 $conditions = '';
589
590 if (count($where) > 0 || count($like) > 0)
591 {
592 $conditions = "\nWHERE ";
593 $conditions .= implode("\n", $this->ar_where);
594
595 if (count($where) > 0 && count($like) > 0)
596 {
597 $conditions .= " AND ";
598 }
599 $conditions .= implode("\n", $like);
600 }
601
Derek Allarde77d77c2007-12-19 15:01:55 +0000602 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
603
Derek Allard39b622d2008-01-16 21:10:09 +0000604 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000605 }
606
607 // --------------------------------------------------------------------
608
609 /**
610 * Limit string
611 *
612 * Generates a platform-specific LIMIT clause
613 *
614 * @access public
615 * @param string the sql query string
616 * @param integer the number of rows to limit the query to
617 * @param integer the offset value
618 * @return string
619 */
620 function _limit($sql, $limit, $offset)
621 {
622 $sql .= "LIMIT ".$limit;
623
624 if ($offset > 0)
625 {
626 $sql .= " OFFSET ".$offset;
627 }
628
629 return $sql;
630 }
631
632 // --------------------------------------------------------------------
633
634 /**
635 * Close DB Connection
636 *
637 * @access public
638 * @param resource
639 * @return void
640 */
641 function _close($conn_id)
642 {
643 @mysqli_close($conn_id);
644 }
645
646
647}
648
adminff2d2512006-09-24 18:12:18 +0000649?>