blob: bd60d9ffe3feafad042d2b59483b65c42a165d3c [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +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 ExpressionEngine Dev Team
Derek Jones7f3719f2010-01-05 13:35:37 +00009 * @copyright Copyright (c) 2008 - 2010, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * MySQL Database Adapter Class
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 ExpressionEngine Dev Team
29 * @link http://codeigniter.com/user_guide/database/
30 */
31class CI_DB_mysql_driver extends CI_DB {
32
33 var $dbdriver = 'mysql';
34
35 // The character used for escaping
36 var $_escape_char = '`';
Derek Jonese4ed5832009-02-20 21:44:59 +000037
38 // clause and character used for LIKE escape sequences - not used in MySQL
39 var $_like_escape_str = '';
40 var $_like_escape_chr = '';
41
Derek Allard2067d1a2008-11-13 22:59:24 +000042 /**
43 * Whether to use the MySQL "delete hack" which allows the number
44 * of affected rows to be shown. Uses a preg_replace when enabled,
45 * adding a bit more processing to all queries.
46 */
47 var $delete_hack = TRUE;
48
49 /**
50 * The syntax to count rows is slightly different across different
51 * database engines, so this string appears in each driver and is
52 * used for the count_all() and count_all_results() functions.
53 */
54 var $_count_string = 'SELECT COUNT(*) AS ';
55 var $_random_keyword = ' RAND()'; // database specific random keyword
56
57 /**
58 * Non-persistent database connection
59 *
60 * @access private called by the base class
61 * @return resource
62 */
63 function db_connect()
64 {
65 if ($this->port != '')
66 {
67 $this->hostname .= ':'.$this->port;
68 }
69
70 return @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
71 }
72
73 // --------------------------------------------------------------------
74
75 /**
76 * Persistent database connection
77 *
78 * @access private called by the base class
79 * @return resource
80 */
81 function db_pconnect()
82 {
83 if ($this->port != '')
84 {
85 $this->hostname .= ':'.$this->port;
86 }
87
88 return @mysql_pconnect($this->hostname, $this->username, $this->password);
89 }
90
91 // --------------------------------------------------------------------
92
93 /**
Derek Jones87cbafc2009-02-27 16:29:59 +000094 * Reconnect
95 *
96 * Keep / reestablish the db connection if no queries have been
97 * sent for a length of time exceeding the server's idle timeout
98 *
99 * @access public
100 * @return void
101 */
102 function reconnect()
103 {
104 if (mysql_ping($this->conn_id) === FALSE)
105 {
106 $this->conn_id = FALSE;
107 }
108 }
109
110 // --------------------------------------------------------------------
111
112 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000113 * Select the database
114 *
115 * @access private called by the base class
116 * @return resource
117 */
118 function db_select()
119 {
120 return @mysql_select_db($this->database, $this->conn_id);
121 }
122
123 // --------------------------------------------------------------------
124
125 /**
126 * Set client character set
127 *
128 * @access public
129 * @param string
130 * @param string
131 * @return resource
132 */
133 function db_set_charset($charset, $collation)
134 {
135 return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id);
136 }
137
138 // --------------------------------------------------------------------
139
140 /**
141 * Version number query string
142 *
143 * @access public
144 * @return string
145 */
146 function _version()
147 {
148 return "SELECT version() AS ver";
149 }
150
151 // --------------------------------------------------------------------
152
153 /**
154 * Execute the query
155 *
156 * @access private called by the base class
157 * @param string an SQL query
158 * @return resource
159 */
160 function _execute($sql)
161 {
162 $sql = $this->_prep_query($sql);
163 return @mysql_query($sql, $this->conn_id);
164 }
165
166 // --------------------------------------------------------------------
167
168 /**
169 * Prep the query
170 *
171 * If needed, each database adapter can prep the query string
172 *
173 * @access private called by execute()
174 * @param string an SQL query
175 * @return string
176 */
177 function _prep_query($sql)
178 {
179 // "DELETE FROM TABLE" returns 0 affected rows This hack modifies
180 // the query so that it returns the number of affected rows
181 if ($this->delete_hack === TRUE)
182 {
183 if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql))
184 {
185 $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql);
186 }
187 }
188
189 return $sql;
190 }
191
192 // --------------------------------------------------------------------
193
194 /**
195 * Begin Transaction
196 *
197 * @access public
198 * @return bool
199 */
200 function trans_begin($test_mode = FALSE)
201 {
202 if ( ! $this->trans_enabled)
203 {
204 return TRUE;
205 }
206
207 // When transactions are nested we only begin/commit/rollback the outermost ones
208 if ($this->_trans_depth > 0)
209 {
210 return TRUE;
211 }
212
213 // Reset the transaction failure flag.
214 // If the $test_mode flag is set to TRUE transactions will be rolled back
215 // even if the queries produce a successful result.
216 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
217
218 $this->simple_query('SET AUTOCOMMIT=0');
219 $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
220 return TRUE;
221 }
222
223 // --------------------------------------------------------------------
224
225 /**
226 * Commit Transaction
227 *
228 * @access public
229 * @return bool
230 */
231 function trans_commit()
232 {
233 if ( ! $this->trans_enabled)
234 {
235 return TRUE;
236 }
237
238 // When transactions are nested we only begin/commit/rollback the outermost ones
239 if ($this->_trans_depth > 0)
240 {
241 return TRUE;
242 }
243
244 $this->simple_query('COMMIT');
245 $this->simple_query('SET AUTOCOMMIT=1');
246 return TRUE;
247 }
248
249 // --------------------------------------------------------------------
250
251 /**
252 * Rollback Transaction
253 *
254 * @access public
255 * @return bool
256 */
257 function trans_rollback()
258 {
259 if ( ! $this->trans_enabled)
260 {
261 return TRUE;
262 }
263
264 // When transactions are nested we only begin/commit/rollback the outermost ones
265 if ($this->_trans_depth > 0)
266 {
267 return TRUE;
268 }
269
270 $this->simple_query('ROLLBACK');
271 $this->simple_query('SET AUTOCOMMIT=1');
272 return TRUE;
273 }
274
275 // --------------------------------------------------------------------
276
277 /**
278 * Escape String
279 *
280 * @access public
281 * @param string
Derek Jonese4ed5832009-02-20 21:44:59 +0000282 * @param bool whether or not the string will be used in a LIKE condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 * @return string
284 */
Derek Jonese4ed5832009-02-20 21:44:59 +0000285 function escape_str($str, $like = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000286 {
287 if (is_array($str))
288 {
289 foreach($str as $key => $val)
290 {
Derek Jonese4ed5832009-02-20 21:44:59 +0000291 $str[$key] = $this->escape_str($val, $like);
Derek Allard2067d1a2008-11-13 22:59:24 +0000292 }
293
294 return $str;
295 }
296
297 if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id))
298 {
Derek Jonese4ed5832009-02-20 21:44:59 +0000299 $str = mysql_real_escape_string($str, $this->conn_id);
Derek Allard2067d1a2008-11-13 22:59:24 +0000300 }
301 elseif (function_exists('mysql_escape_string'))
302 {
Derek Jonese4ed5832009-02-20 21:44:59 +0000303 $str = mysql_escape_string($str);
Derek Allard2067d1a2008-11-13 22:59:24 +0000304 }
305 else
306 {
Derek Jonese4ed5832009-02-20 21:44:59 +0000307 $str = addslashes($str);
Derek Allard2067d1a2008-11-13 22:59:24 +0000308 }
Derek Jonese4ed5832009-02-20 21:44:59 +0000309
310 // escape LIKE condition wildcards
311 if ($like === TRUE)
312 {
313 $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
314 }
315
316 return $str;
Derek Allard2067d1a2008-11-13 22:59:24 +0000317 }
318
319 // --------------------------------------------------------------------
320
321 /**
322 * Affected Rows
323 *
324 * @access public
325 * @return integer
326 */
327 function affected_rows()
328 {
329 return @mysql_affected_rows($this->conn_id);
330 }
331
332 // --------------------------------------------------------------------
333
334 /**
335 * Insert ID
336 *
337 * @access public
338 * @return integer
339 */
340 function insert_id()
341 {
342 return @mysql_insert_id($this->conn_id);
343 }
344
345 // --------------------------------------------------------------------
346
347 /**
348 * "Count All" query
349 *
350 * Generates a platform-specific query string that counts all records in
351 * the specified database
352 *
353 * @access public
354 * @param string
355 * @return string
356 */
357 function count_all($table = '')
358 {
359 if ($table == '')
Derek Allarde37ab382009-02-03 16:13:57 +0000360 {
361 return 0;
362 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000363
Derek Allarde37ab382009-02-03 16:13:57 +0000364 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
365
Derek Allard2067d1a2008-11-13 22:59:24 +0000366 if ($query->num_rows() == 0)
Derek Allarde37ab382009-02-03 16:13:57 +0000367 {
368 return 0;
369 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000370
371 $row = $query->row();
Derek Allarde37ab382009-02-03 16:13:57 +0000372 return (int) $row->numrows;
Derek Allard2067d1a2008-11-13 22:59:24 +0000373 }
374
375 // --------------------------------------------------------------------
376
377 /**
378 * List table query
379 *
380 * Generates a platform-specific query string so that the table names can be fetched
381 *
382 * @access private
383 * @param boolean
384 * @return string
385 */
386 function _list_tables($prefix_limit = FALSE)
387 {
388 $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char;
389
390 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
391 {
Derek Jones3c11b6f2009-02-20 22:36:27 +0000392 $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'";
Derek Allard2067d1a2008-11-13 22:59:24 +0000393 }
394
395 return $sql;
396 }
397
398 // --------------------------------------------------------------------
399
400 /**
401 * Show column query
402 *
403 * Generates a platform-specific query string so that the column names can be fetched
404 *
405 * @access public
406 * @param string the table name
407 * @return string
408 */
409 function _list_columns($table = '')
410 {
Greg Aker1edde302010-01-26 00:17:01 +0000411 return "SHOW COLUMNS FROM ".$this->_protect_identifiers($table, TRUE, NULL, FALSE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000412 }
413
414 // --------------------------------------------------------------------
415
416 /**
417 * Field data query
418 *
419 * Generates a platform-specific query so that the column data can be retrieved
420 *
421 * @access public
422 * @param string the table name
423 * @return object
424 */
425 function _field_data($table)
426 {
427 return "SELECT * FROM ".$table." LIMIT 1";
428 }
429
430 // --------------------------------------------------------------------
431
432 /**
433 * The error message string
434 *
435 * @access private
436 * @return string
437 */
438 function _error_message()
439 {
440 return mysql_error($this->conn_id);
441 }
442
443 // --------------------------------------------------------------------
444
445 /**
446 * The error message number
447 *
448 * @access private
449 * @return integer
450 */
451 function _error_number()
452 {
453 return mysql_errno($this->conn_id);
454 }
455
456 // --------------------------------------------------------------------
457
458 /**
459 * Escape the SQL Identifiers
460 *
461 * This function escapes column and table names
462 *
463 * @access private
464 * @param string
465 * @return string
466 */
467 function _escape_identifiers($item)
468 {
469 if ($this->_escape_char == '')
470 {
471 return $item;
472 }
473
474 foreach ($this->_reserved_identifiers as $id)
475 {
476 if (strpos($item, '.'.$id) !== FALSE)
477 {
478 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
479
480 // remove duplicates if the user already included the escape
481 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
482 }
483 }
484
485 if (strpos($item, '.') !== FALSE)
486 {
487 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
488 }
489 else
490 {
491 $str = $this->_escape_char.$item.$this->_escape_char;
492 }
493
494 // remove duplicates if the user already included the escape
495 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
496 }
497
498 // --------------------------------------------------------------------
499
500 /**
501 * From Tables
502 *
503 * This function implicitly groups FROM tables so there is no confusion
504 * about operator precedence in harmony with SQL standards
505 *
506 * @access public
507 * @param type
508 * @return type
509 */
510 function _from_tables($tables)
511 {
512 if ( ! is_array($tables))
513 {
514 $tables = array($tables);
515 }
516
517 return '('.implode(', ', $tables).')';
518 }
519
520 // --------------------------------------------------------------------
521
522 /**
523 * Insert statement
524 *
525 * Generates a platform-specific insert string from the supplied data
526 *
527 * @access public
528 * @param string the table name
529 * @param array the insert keys
530 * @param array the insert values
531 * @return string
532 */
533 function _insert($table, $keys, $values)
534 {
535 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
536 }
537
538 // --------------------------------------------------------------------
539
540 /**
541 * Update statement
542 *
543 * Generates a platform-specific update string from the supplied data
544 *
545 * @access public
546 * @param string the table name
547 * @param array the update data
548 * @param array the where clause
549 * @param array the orderby clause
550 * @param array the limit clause
551 * @return string
552 */
553 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
554 {
555 foreach($values as $key => $val)
556 {
557 $valstr[] = $key." = ".$val;
558 }
559
560 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
561
562 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
563
564 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
565
566 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
567
568 $sql .= $orderby.$limit;
569
570 return $sql;
571 }
572
573 // --------------------------------------------------------------------
574
575 /**
576 * Truncate statement
577 *
578 * Generates a platform-specific truncate string from the supplied data
579 * If the database does not support the truncate() command
580 * This function maps to "DELETE FROM table"
581 *
582 * @access public
583 * @param string the table name
584 * @return string
585 */
586 function _truncate($table)
587 {
588 return "TRUNCATE ".$table;
589 }
590
591 // --------------------------------------------------------------------
592
593 /**
594 * Delete statement
595 *
596 * Generates a platform-specific delete string from the supplied data
597 *
598 * @access public
599 * @param string the table name
600 * @param array the where clause
601 * @param string the limit clause
602 * @return string
603 */
604 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
605 {
606 $conditions = '';
607
608 if (count($where) > 0 OR count($like) > 0)
609 {
610 $conditions = "\nWHERE ";
611 $conditions .= implode("\n", $this->ar_where);
612
613 if (count($where) > 0 && count($like) > 0)
614 {
615 $conditions .= " AND ";
616 }
617 $conditions .= implode("\n", $like);
618 }
619
620 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
621
622 return "DELETE FROM ".$table.$conditions.$limit;
623 }
624
625 // --------------------------------------------------------------------
626
627 /**
628 * Limit string
629 *
630 * Generates a platform-specific LIMIT clause
631 *
632 * @access public
633 * @param string the sql query string
634 * @param integer the number of rows to limit the query to
635 * @param integer the offset value
636 * @return string
637 */
638 function _limit($sql, $limit, $offset)
639 {
640 if ($offset == 0)
641 {
642 $offset = '';
643 }
644 else
645 {
646 $offset .= ", ";
647 }
648
649 return $sql."LIMIT ".$offset.$limit;
650 }
651
652 // --------------------------------------------------------------------
653
654 /**
655 * Close DB Connection
656 *
657 * @access public
658 * @param resource
659 * @return void
660 */
661 function _close($conn_id)
662 {
663 @mysql_close($conn_id);
664 }
665
666}
667
668
669/* End of file mysql_driver.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000670/* Location: ./system/database/drivers/mysql/mysql_driver.php */