blob: d295ca9f273a7eac930b9efb1452bd00abb99614 [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
8 * @author Rick Ellis
9 * @copyright Copyright (c) 2006, EllisLab, Inc.
Derek Allard6838f002007-10-04 19:29:59 +000010 * @license http://www.codeigniter.com/user_guide/license.html
Derek Allardd2df9bc2007-04-15 17:41:17 +000011 * @link http://www.codeigniter.com
12 * @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
28 * @author Rick Ellis
29 * @link http://www.codeigniter.com/user_guide/database/
30 */
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 {
99 // TODO - add support if needed
100 return TRUE;
101 }
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 Allard6ddb5a12007-12-18 17:22:50 +0000309 $query = $this->query($this->_count_string . "FROM `".$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 {
413 if (stristr($table, '.'))
414 {
415 $table = preg_replace("/\./", "`.`", $table);
416 }
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`"
449 if (strpos($item, ' ') !== FALSE)
450 {
451 // This function may get "field >= 1", and need it to return "`field` >= 1"
452 if ($first_word_only === TRUE)
453 {
454 return '`'.preg_replace('/ /', '` ', $item, 1);
455 }
456
457 $item = preg_replace('/(^|\s|\()([\w\d\-\_]+?)(\s|\)|$)/iS', '$1`$2`$3', $item);
458 }
459
460 $exceptions = array('AS', '/', '-', '%', '+', '*');
461
462 foreach ($exceptions as $exception)
463 {
464 if (stristr($item, " `{$exception}` ") !== FALSE)
465 {
466 $item = preg_replace('/ `('.preg_quote($exception).')` /i', ' $1 ', $item);
467 }
468 }
469
470 return $item;
471 }
472
473 // --------------------------------------------------------------------
474
475 /**
Derek Allardd2df9bc2007-04-15 17:41:17 +0000476 * Insert statement
477 *
478 * Generates a platform-specific insert string from the supplied data
479 *
480 * @access public
481 * @param string the table name
482 * @param array the insert keys
483 * @param array the insert values
484 * @return string
485 */
486 function _insert($table, $keys, $values)
487 {
488 return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
489 }
490
491 // --------------------------------------------------------------------
492
493 /**
494 * Update statement
495 *
496 * Generates a platform-specific update string from the supplied data
497 *
498 * @access public
499 * @param string the table name
500 * @param array the update data
501 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000502 * @param array the orderby clause
503 * @param array the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000504 * @return string
505 */
Derek Allard39b622d2008-01-16 21:10:09 +0000506 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000507 {
508 foreach($values as $key => $val)
509 {
510 $valstr[] = $key." = ".$val;
511 }
Derek Allardda6d2402007-12-19 14:49:29 +0000512
513 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
Derek Allard39b622d2008-01-16 21:10:09 +0000514
515 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000516
Derek Allard39b622d2008-01-16 21:10:09 +0000517 return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where).$orderby.$limit;
518 }
519
520
521 // --------------------------------------------------------------------
522
523 /**
524 * Truncate statement
525 *
526 * Generates a platform-specific truncate string from the supplied data
527 * If the database does not support the truncate() command
528 * This function maps to "DELETE FROM table"
529 *
530 * @access public
531 * @param string the table name
532 * @return string
533 */
534 function _truncate($table)
535 {
536 return "TRUNCATE ".$this->_escape_table($table);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000537 }
538
539 // --------------------------------------------------------------------
540
541 /**
542 * Delete statement
543 *
544 * Generates a platform-specific delete string from the supplied data
545 *
546 * @access public
547 * @param string the table name
548 * @param array the where clause
Derek Allard39b622d2008-01-16 21:10:09 +0000549 * @param string the limit clause
Derek Allardd2df9bc2007-04-15 17:41:17 +0000550 * @return string
551 */
Derek Allard39b622d2008-01-16 21:10:09 +0000552 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000553 {
Derek Allard39b622d2008-01-16 21:10:09 +0000554 $conditions = '';
555
556 if (count($where) > 0 || count($like) > 0)
557 {
558 $conditions = "\nWHERE ";
559 $conditions .= implode("\n", $this->ar_where);
560
561 if (count($where) > 0 && count($like) > 0)
562 {
563 $conditions .= " AND ";
564 }
565 $conditions .= implode("\n", $like);
566 }
567
Derek Allarde77d77c2007-12-19 15:01:55 +0000568 $limit = (!$limit) ? '' : ' LIMIT '.$limit;
569
Derek Allard39b622d2008-01-16 21:10:09 +0000570 return "DELETE FROM ".$table.$conditions.$limit;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000571 }
572
573 // --------------------------------------------------------------------
574
575 /**
576 * Limit string
577 *
578 * Generates a platform-specific LIMIT clause
579 *
580 * @access public
581 * @param string the sql query string
582 * @param integer the number of rows to limit the query to
583 * @param integer the offset value
584 * @return string
585 */
586 function _limit($sql, $limit, $offset)
587 {
588 $sql .= "LIMIT ".$limit;
589
590 if ($offset > 0)
591 {
592 $sql .= " OFFSET ".$offset;
593 }
594
595 return $sql;
596 }
597
598 // --------------------------------------------------------------------
599
600 /**
601 * Close DB Connection
602 *
603 * @access public
604 * @param resource
605 * @return void
606 */
607 function _close($conn_id)
608 {
609 @mysqli_close($conn_id);
610 }
611
612
613}
614
adminff2d2512006-09-24 18:12:18 +0000615?>