blob: 244a15e1ec3d5d39bb414cf71a8295a01e3709cc [file] [log] [blame]
Timothy Warren80ab8162011-08-22 18:26:12 -04001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
9 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
10 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Timothy Warren018af7a2011-09-07 12:07:35 -040012 * @since Version 2.1.0
Timothy Warren80ab8162011-08-22 18:26:12 -040013 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
Timothy Warren02615962011-08-24 08:21:36 -040019 * PDO Database Adapter Class
Timothy Warren80ab8162011-08-22 18:26:12 -040020 *
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_pdo_driver extends CI_DB {
32
33 var $dbdriver = 'pdo';
34
35 // the character used to excape - not necessary for PDO
36 var $_escape_char = '';
Timothy Warrenc7ba6642011-09-14 12:25:14 -040037 var $_like_escape_str;
38 var $_like_escape_chr;
39
Timothy Warren80ab8162011-08-22 18:26:12 -040040
41 /**
42 * The syntax to count rows is slightly different across different
43 * database engines, so this string appears in each driver and is
44 * used for the count_all() and count_all_results() functions.
45 */
46 var $_count_string = "SELECT COUNT(*) AS ";
47 var $_random_keyword;
48
49
Timothy Warren51b0e642011-09-13 13:30:27 -040050 function __construct($params)
Timothy Warren80ab8162011-08-22 18:26:12 -040051 {
Timothy Warrenb5a43b02011-10-04 17:26:04 -040052 parent::__construct($params);
Timothy Warrenab347582011-08-23 12:29:29 -040053
Timothy Warrenc7ba6642011-09-14 12:25:14 -040054 // clause and character used for LIKE escape sequences
55 if(strpos($this->hostname, 'mysql') !== FALSE)
56 {
57 $this->_like_escape_str = '';
58 $this->_like_escape_chr = '';
59 }
60 else if(strpos($this->hostname, 'odbc') !== FALSE)
61 {
62 $this->_like_escape_str = " {escape '%s'} ";
63 $this->_like_escape_chr = '!';
64 }
65 else
66 {
67 $this->_like_escape_str = " ESCAPE '%s' ";
68 $this->_like_escape_chr = '!';
69 }
70
Timothy Warrenab347582011-08-23 12:29:29 -040071 $this->hostname = $this->hostname . ";dbname=".$this->database;
72 $this->trans_enabled = FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -040073
74 $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
75 }
76
77 /**
78 * Non-persistent database connection
79 *
80 * @access private called by the base class
81 * @return resource
82 */
83 function db_connect()
84 {
Timothy Warrenab347582011-08-23 12:29:29 -040085 return new PDO($this->hostname,$this->username,$this->password, array(
86 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT
Timothy Warren80ab8162011-08-22 18:26:12 -040087 ));
88 }
89
90 // --------------------------------------------------------------------
91
92 /**
93 * Persistent database connection
94 *
95 * @access private called by the base class
96 * @return resource
97 */
98 function db_pconnect()
99 {
Timothy Warrenab347582011-08-23 12:29:29 -0400100 return new PDO($this->hostname,$this->username,$this->password, array(
101 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
102 PDO::ATTR_PERSISTENT => true
Timothy Warren80ab8162011-08-22 18:26:12 -0400103 ));
104 }
105
106 // --------------------------------------------------------------------
107
108 /**
109 * Reconnect
110 *
111 * Keep / reestablish the db connection if no queries have been
112 * sent for a length of time exceeding the server's idle timeout
113 *
114 * @access public
115 * @return void
116 */
117 function reconnect()
118 {
Timothy Warren02615962011-08-24 08:21:36 -0400119 if ($this->db->db_debug)
120 {
121 return $this->db->display_error('db_unsuported_feature');
122 }
123 return FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -0400124 }
125
126 // --------------------------------------------------------------------
127
128 /**
129 * Select the database
130 *
131 * @access private called by the base class
132 * @return resource
133 */
134 function db_select()
135 {
136 // Not needed for PDO
137 return TRUE;
138 }
139
140 // --------------------------------------------------------------------
141
142 /**
143 * Set client character set
144 *
145 * @access public
146 * @param string
147 * @param string
148 * @return resource
149 */
150 function db_set_charset($charset, $collation)
151 {
152 // @todo - add support if needed
153 return TRUE;
154 }
155
156 // --------------------------------------------------------------------
157
158 /**
159 * Version number query string
160 *
161 * @access public
162 * @return string
163 */
164 function _version()
165 {
Timothy Warren36fb8de2011-08-24 08:29:05 -0400166 return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
Timothy Warren80ab8162011-08-22 18:26:12 -0400167 }
168
169 // --------------------------------------------------------------------
170
171 /**
172 * Execute the query
173 *
174 * @access private called by the base class
175 * @param string an SQL query
Timothy Warren51a48882011-09-14 13:47:06 -0400176 * @return object
Timothy Warren80ab8162011-08-22 18:26:12 -0400177 */
178 function _execute($sql)
179 {
180 $sql = $this->_prep_query($sql);
Timothy Warren51a48882011-09-14 13:47:06 -0400181 $result_id = $this->conn_id->query($sql);
182
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400183 if(is_object($result_id))
184 {
185 $this->affect_rows = $result_id->rowCount();
186 }
187 else
188 {
189 $this->affect_rows = 0;
190 }
Timothy Warren51a48882011-09-14 13:47:06 -0400191
192 return $result_id;
Timothy Warren80ab8162011-08-22 18:26:12 -0400193 }
194
195 // --------------------------------------------------------------------
196
197 /**
198 * Prep the query
199 *
200 * If needed, each database adapter can prep the query string
201 *
202 * @access private called by execute()
203 * @param string an SQL query
204 * @return string
205 */
206 function _prep_query($sql)
207 {
208 return $sql;
209 }
210
211 // --------------------------------------------------------------------
212
213 /**
214 * Begin Transaction
215 *
216 * @access public
217 * @return bool
218 */
219 function trans_begin($test_mode = FALSE)
220 {
221 if ( ! $this->trans_enabled)
222 {
223 return TRUE;
224 }
225
226 // When transactions are nested we only begin/commit/rollback the outermost ones
227 if ($this->_trans_depth > 0)
228 {
229 return TRUE;
230 }
231
232 // Reset the transaction failure flag.
233 // If the $test_mode flag is set to TRUE transactions will be rolled back
234 // even if the queries produce a successful result.
235 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
236
Timothy Warrenab347582011-08-23 12:29:29 -0400237 return $this->conn_id->beginTransaction();
Timothy Warren80ab8162011-08-22 18:26:12 -0400238 }
239
240 // --------------------------------------------------------------------
241
242 /**
243 * Commit Transaction
244 *
245 * @access public
246 * @return bool
247 */
248 function trans_commit()
249 {
250 if ( ! $this->trans_enabled)
251 {
252 return TRUE;
253 }
254
255 // When transactions are nested we only begin/commit/rollback the outermost ones
256 if ($this->_trans_depth > 0)
257 {
258 return TRUE;
259 }
260
Timothy Warrenab347582011-08-23 12:29:29 -0400261 $ret = $this->conn->commit();
Timothy Warren80ab8162011-08-22 18:26:12 -0400262 return $ret;
263 }
264
265 // --------------------------------------------------------------------
266
267 /**
268 * Rollback Transaction
269 *
270 * @access public
271 * @return bool
272 */
273 function trans_rollback()
274 {
275 if ( ! $this->trans_enabled)
276 {
277 return TRUE;
278 }
279
280 // When transactions are nested we only begin/commit/rollback the outermost ones
281 if ($this->_trans_depth > 0)
282 {
283 return TRUE;
284 }
285
Timothy Warrenab347582011-08-23 12:29:29 -0400286 $ret = $this->conn_id->rollBack();
Timothy Warren80ab8162011-08-22 18:26:12 -0400287 return $ret;
288 }
289
290 // --------------------------------------------------------------------
291
292 /**
293 * Escape String
294 *
295 * @access public
296 * @param string
297 * @param bool whether or not the string will be used in a LIKE condition
298 * @return string
299 */
300 function escape_str($str, $like = FALSE)
301 {
302 if (is_array($str))
303 {
304 foreach ($str as $key => $val)
305 {
306 $str[$key] = $this->escape_str($val, $like);
307 }
308
309 return $str;
310 }
311
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400312 // Remove invisible characters
Timothy Warren80ab8162011-08-22 18:26:12 -0400313 $str = remove_invisible_characters($str);
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400314
315 //Make sure to escape slashes and quotes
316 $replace = array(
317 "\\" => "\\\\",
318 "'" => "\\'",
319 "\"" => "\\\"",
320 );
321
322 $str = strtr($str, $replace);
323
Timothy Warren80ab8162011-08-22 18:26:12 -0400324
325 // escape LIKE condition wildcards
326 if ($like === TRUE)
327 {
328 $str = str_replace( array('%', '_', $this->_like_escape_chr),
329 array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
330 $str);
331 }
332
333 return $str;
334 }
335
336 // --------------------------------------------------------------------
337
338 /**
339 * Affected Rows
340 *
341 * @access public
342 * @return integer
343 */
344 function affected_rows()
345 {
Timothy Warren51a48882011-09-14 13:47:06 -0400346 return $this->affect_rows;
Timothy Warren80ab8162011-08-22 18:26:12 -0400347 }
348
349 // --------------------------------------------------------------------
350
351 /**
352 * Insert ID
Timothy Warren57cea512011-09-14 14:26:28 -0400353 *
Timothy Warren80ab8162011-08-22 18:26:12 -0400354 * @access public
355 * @return integer
356 */
Timothy Warren51a48882011-09-14 13:47:06 -0400357 function insert_id($name=NULL)
Timothy Warren80ab8162011-08-22 18:26:12 -0400358 {
Timothy Warren51a48882011-09-14 13:47:06 -0400359 return $this->conn_id->lastInsertId($name);
Timothy Warren80ab8162011-08-22 18:26:12 -0400360 }
361
362 // --------------------------------------------------------------------
363
364 /**
365 * "Count All" query
366 *
367 * Generates a platform-specific query string that counts all records in
368 * the specified database
369 *
370 * @access public
371 * @param string
372 * @return string
373 */
374 function count_all($table = '')
375 {
376 if ($table == '')
377 {
378 return 0;
379 }
380
381 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
382
383 if ($query->num_rows() == 0)
384 {
385 return 0;
386 }
387
388 $row = $query->row();
389 $this->_reset_select();
390 return (int) $row->numrows;
391 }
392
393 // --------------------------------------------------------------------
394
395 /**
396 * Show table query
397 *
398 * Generates a platform-specific query string so that the table names can be fetched
399 *
400 * @access private
401 * @param boolean
402 * @return string
403 */
404 function _list_tables($prefix_limit = FALSE)
405 {
406 $sql = "SHOW TABLES FROM `".$this->database."`";
407
408 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
409 {
410 //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
411 return FALSE; // not currently supported
412 }
413
414 return $sql;
415 }
416
417 // --------------------------------------------------------------------
418
419 /**
420 * Show column query
421 *
422 * Generates a platform-specific query string so that the column names can be fetched
423 *
424 * @access public
425 * @param string the table name
426 * @return string
427 */
428 function _list_columns($table = '')
429 {
430 return "SHOW COLUMNS FROM ".$table;
431 }
432
433 // --------------------------------------------------------------------
434
435 /**
436 * Field data query
437 *
438 * Generates a platform-specific query so that the column data can be retrieved
439 *
440 * @access public
441 * @param string the table name
442 * @return object
443 */
444 function _field_data($table)
445 {
446 return "SELECT TOP 1 FROM ".$table;
447 }
448
449 // --------------------------------------------------------------------
450
451 /**
452 * The error message string
453 *
454 * @access private
455 * @return string
456 */
457 function _error_message()
458 {
Timothy Warrenab347582011-08-23 12:29:29 -0400459 $error_array = $this->conn_id->errorInfo();
460 return $error_array[2];
Timothy Warren80ab8162011-08-22 18:26:12 -0400461 }
462
463 // --------------------------------------------------------------------
464
465 /**
466 * The error message number
467 *
468 * @access private
469 * @return integer
470 */
471 function _error_number()
472 {
Timothy Warrenab347582011-08-23 12:29:29 -0400473 return $this->conn_id->errorCode();
Timothy Warren80ab8162011-08-22 18:26:12 -0400474 }
475
476 // --------------------------------------------------------------------
477
478 /**
479 * Escape the SQL Identifiers
480 *
481 * This function escapes column and table names
482 *
483 * @access private
484 * @param string
485 * @return string
486 */
487 function _escape_identifiers($item)
488 {
489 if ($this->_escape_char == '')
490 {
491 return $item;
492 }
493
494 foreach ($this->_reserved_identifiers as $id)
495 {
496 if (strpos($item, '.'.$id) !== FALSE)
497 {
498 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
499
500 // remove duplicates if the user already included the escape
501 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
502 }
503 }
504
505 if (strpos($item, '.') !== FALSE)
506 {
507 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
Timothy Warren018af7a2011-09-07 12:07:35 -0400508
Timothy Warren80ab8162011-08-22 18:26:12 -0400509 }
510 else
511 {
512 $str = $this->_escape_char.$item.$this->_escape_char;
513 }
514
515 // remove duplicates if the user already included the escape
516 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
517 }
518
519 // --------------------------------------------------------------------
520
521 /**
522 * From Tables
523 *
524 * This function implicitly groups FROM tables so there is no confusion
525 * about operator precedence in harmony with SQL standards
526 *
527 * @access public
528 * @param type
529 * @return type
530 */
531 function _from_tables($tables)
532 {
533 if ( ! is_array($tables))
534 {
535 $tables = array($tables);
536 }
537
Timothy Warrenab347582011-08-23 12:29:29 -0400538 return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
Timothy Warren80ab8162011-08-22 18:26:12 -0400539 }
540
541 // --------------------------------------------------------------------
542
543 /**
544 * Insert statement
545 *
546 * Generates a platform-specific insert string from the supplied data
547 *
548 * @access public
549 * @param string the table name
550 * @param array the insert keys
551 * @param array the insert values
552 * @return string
553 */
554 function _insert($table, $keys, $values)
555 {
556 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
557 }
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400558
559 // --------------------------------------------------------------------
560
561 /**
562 * Insert_batch statement
563 *
564 * Generates a platform-specific insert string from the supplied data
565 *
566 * @access public
567 * @param string the table name
568 * @param array the insert keys
569 * @param array the insert values
570 * @return string
571 */
572 function _insert_batch($table, $keys, $values)
573 {
574 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
575 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400576
577 // --------------------------------------------------------------------
578
579 /**
580 * Update statement
581 *
582 * Generates a platform-specific update string from the supplied data
583 *
584 * @access public
585 * @param string the table name
586 * @param array the update data
587 * @param array the where clause
588 * @param array the orderby clause
589 * @param array the limit clause
590 * @return string
591 */
592 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
593 {
594 foreach ($values as $key => $val)
595 {
596 $valstr[] = $key." = ".$val;
597 }
598
599 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
600
601 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
602
603 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
604
605 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
606
607 $sql .= $orderby.$limit;
608
609 return $sql;
610 }
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400611
612 // --------------------------------------------------------------------
613
614 /**
615 * Update_Batch statement
616 *
617 * Generates a platform-specific batch update string from the supplied data
618 *
619 * @access public
620 * @param string the table name
621 * @param array the update data
622 * @param array the where clause
623 * @return string
624 */
625 function _update_batch($table, $values, $index, $where = NULL)
626 {
627 $ids = array();
628 $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
629
630 foreach ($values as $key => $val)
631 {
632 $ids[] = $val[$index];
633
634 foreach (array_keys($val) as $field)
635 {
636 if ($field != $index)
637 {
638 $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
639 }
640 }
641 }
642
643 $sql = "UPDATE ".$table." SET ";
644 $cases = '';
645
646 foreach ($final as $k => $v)
647 {
648 $cases .= $k.' = CASE '."\n";
649 foreach ($v as $row)
650 {
651 $cases .= $row."\n";
652 }
653
654 $cases .= 'ELSE '.$k.' END, ';
655 }
656
657 $sql .= substr($cases, 0, -2);
658
659 $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
660
661 return $sql;
662 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400663
664
665 // --------------------------------------------------------------------
666
667 /**
668 * Truncate statement
669 *
670 * Generates a platform-specific truncate string from the supplied data
671 * If the database does not support the truncate() command
672 * This function maps to "DELETE FROM table"
673 *
674 * @access public
675 * @param string the table name
676 * @return string
677 */
678 function _truncate($table)
679 {
680 return $this->_delete($table);
681 }
682
683 // --------------------------------------------------------------------
684
685 /**
686 * Delete statement
687 *
688 * Generates a platform-specific delete string from the supplied data
689 *
690 * @access public
691 * @param string the table name
692 * @param array the where clause
693 * @param string the limit clause
694 * @return string
695 */
696 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
697 {
698 $conditions = '';
699
700 if (count($where) > 0 OR count($like) > 0)
701 {
702 $conditions = "\nWHERE ";
703 $conditions .= implode("\n", $this->ar_where);
704
705 if (count($where) > 0 && count($like) > 0)
706 {
707 $conditions .= " AND ";
708 }
709 $conditions .= implode("\n", $like);
710 }
711
712 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
713
714 return "DELETE FROM ".$table.$conditions.$limit;
715 }
716
717 // --------------------------------------------------------------------
718
719 /**
720 * Limit string
721 *
722 * Generates a platform-specific LIMIT clause
723 *
724 * @access public
725 * @param string the sql query string
726 * @param integer the number of rows to limit the query to
727 * @param integer the offset value
728 * @return string
729 */
730 function _limit($sql, $limit, $offset)
731 {
Timothy Warren5fc36d82011-09-16 12:31:37 -0400732 if(strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE)
Timothy Warren0a43ad82011-09-15 20:15:19 -0400733 {
734 if ($offset == 0)
735 {
736 $offset = '';
737 }
738 else
739 {
740 $offset .= ", ";
741 }
742
743 return $sql."LIMIT ".$offset.$limit;
744 }
745 else
746 {
747 $sql .= "LIMIT ".$limit;
748
749 if ($offset > 0)
750 {
751 $sql .= " OFFSET ".$offset;
752 }
753
754 return $sql;
755 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400756 }
757
758 // --------------------------------------------------------------------
759
760 /**
761 * Close DB Connection
762 *
763 * @access public
764 * @param resource
765 * @return void
766 */
767 function _close($conn_id)
768 {
Timothy Warren6a450cf2011-08-23 12:46:11 -0400769 $this->conn_id = null;
Timothy Warren80ab8162011-08-22 18:26:12 -0400770 }
771
772
773}
774
775
776
777/* End of file pdo_driver.php */
778/* Location: ./system/database/drivers/pdo/pdo_driver.php */