blob: 10ef00c4902776505231b7c55a46b75108e1cf46 [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 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
8 *
9 * Licensed under the Open Software License version 3.0
10 *
11 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Timothy Warren80ab8162011-08-22 18:26:12 -040019 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
21 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc. (http://ellislab.com/)
22 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Timothy Warren80ab8162011-08-22 18:26:12 -040023 * @link http://codeigniter.com
Timothy Warren018af7a2011-09-07 12:07:35 -040024 * @since Version 2.1.0
Timothy Warren80ab8162011-08-22 18:26:12 -040025 * @filesource
26 */
27
28// ------------------------------------------------------------------------
29
30/**
Timothy Warren02615962011-08-24 08:21:36 -040031 * PDO Database Adapter Class
Timothy Warren80ab8162011-08-22 18:26:12 -040032 *
33 * Note: _DB is an extender class that the app controller
34 * creates dynamically based on whether the active record
35 * class is being used or not.
36 *
37 * @package CodeIgniter
38 * @subpackage Drivers
39 * @category Database
Derek Jonesf4a4bd82011-10-20 12:18:42 -050040 * @author EllisLab Dev Team
Timothy Warren80ab8162011-08-22 18:26:12 -040041 * @link http://codeigniter.com/user_guide/database/
42 */
43class CI_DB_pdo_driver extends CI_DB {
44
45 var $dbdriver = 'pdo';
46
47 // the character used to excape - not necessary for PDO
48 var $_escape_char = '';
Timothy Warrenc7ba6642011-09-14 12:25:14 -040049 var $_like_escape_str;
50 var $_like_escape_chr;
51
Timothy Warren80ab8162011-08-22 18:26:12 -040052
53 /**
54 * The syntax to count rows is slightly different across different
55 * database engines, so this string appears in each driver and is
56 * used for the count_all() and count_all_results() functions.
57 */
58 var $_count_string = "SELECT COUNT(*) AS ";
59 var $_random_keyword;
60
61
Timothy Warren51b0e642011-09-13 13:30:27 -040062 function __construct($params)
Timothy Warren80ab8162011-08-22 18:26:12 -040063 {
Timothy Warrenb5a43b02011-10-04 17:26:04 -040064 parent::__construct($params);
Timothy Warrenab347582011-08-23 12:29:29 -040065
Timothy Warrenc7ba6642011-09-14 12:25:14 -040066 // clause and character used for LIKE escape sequences
Timothy Warrend6691532011-10-07 10:03:01 -040067 if (strpos($this->hostname, 'mysql') !== FALSE)
Timothy Warrenc7ba6642011-09-14 12:25:14 -040068 {
69 $this->_like_escape_str = '';
70 $this->_like_escape_chr = '';
71 }
Timothy Warrend6691532011-10-07 10:03:01 -040072 else if (strpos($this->hostname, 'odbc') !== FALSE)
Timothy Warrenc7ba6642011-09-14 12:25:14 -040073 {
74 $this->_like_escape_str = " {escape '%s'} ";
75 $this->_like_escape_chr = '!';
76 }
77 else
78 {
79 $this->_like_escape_str = " ESCAPE '%s' ";
80 $this->_like_escape_chr = '!';
81 }
82
Timothy Warrenab347582011-08-23 12:29:29 -040083 $this->hostname = $this->hostname . ";dbname=".$this->database;
84 $this->trans_enabled = FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -040085
86 $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
87 }
88
89 /**
90 * Non-persistent database connection
91 *
92 * @access private called by the base class
93 * @return resource
94 */
95 function db_connect()
96 {
Timothy Warrenab347582011-08-23 12:29:29 -040097 return new PDO($this->hostname,$this->username,$this->password, array(
98 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT
Timothy Warren80ab8162011-08-22 18:26:12 -040099 ));
100 }
101
102 // --------------------------------------------------------------------
103
104 /**
105 * Persistent database connection
106 *
107 * @access private called by the base class
108 * @return resource
109 */
110 function db_pconnect()
111 {
Timothy Warrenab347582011-08-23 12:29:29 -0400112 return new PDO($this->hostname,$this->username,$this->password, array(
113 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
114 PDO::ATTR_PERSISTENT => true
Timothy Warren80ab8162011-08-22 18:26:12 -0400115 ));
116 }
117
118 // --------------------------------------------------------------------
119
120 /**
121 * Reconnect
122 *
123 * Keep / reestablish the db connection if no queries have been
124 * sent for a length of time exceeding the server's idle timeout
125 *
126 * @access public
127 * @return void
128 */
129 function reconnect()
130 {
Timothy Warren02615962011-08-24 08:21:36 -0400131 if ($this->db->db_debug)
132 {
133 return $this->db->display_error('db_unsuported_feature');
134 }
135 return FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -0400136 }
137
138 // --------------------------------------------------------------------
139
140 /**
141 * Select the database
142 *
143 * @access private called by the base class
144 * @return resource
145 */
146 function db_select()
147 {
148 // Not needed for PDO
149 return TRUE;
150 }
151
152 // --------------------------------------------------------------------
153
154 /**
155 * Set client character set
156 *
157 * @access public
158 * @param string
159 * @param string
160 * @return resource
161 */
162 function db_set_charset($charset, $collation)
163 {
164 // @todo - add support if needed
165 return TRUE;
166 }
167
168 // --------------------------------------------------------------------
169
170 /**
171 * Version number query string
172 *
173 * @access public
174 * @return string
175 */
176 function _version()
177 {
Timothy Warren36fb8de2011-08-24 08:29:05 -0400178 return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
Timothy Warren80ab8162011-08-22 18:26:12 -0400179 }
180
181 // --------------------------------------------------------------------
182
183 /**
184 * Execute the query
185 *
186 * @access private called by the base class
187 * @param string an SQL query
Timothy Warren51a48882011-09-14 13:47:06 -0400188 * @return object
Timothy Warren80ab8162011-08-22 18:26:12 -0400189 */
190 function _execute($sql)
191 {
192 $sql = $this->_prep_query($sql);
Timothy Warren51a48882011-09-14 13:47:06 -0400193 $result_id = $this->conn_id->query($sql);
194
Timothy Warrend6691532011-10-07 10:03:01 -0400195 if (is_object($result_id))
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400196 {
197 $this->affect_rows = $result_id->rowCount();
198 }
199 else
200 {
201 $this->affect_rows = 0;
202 }
Timothy Warren51a48882011-09-14 13:47:06 -0400203
204 return $result_id;
Timothy Warren80ab8162011-08-22 18:26:12 -0400205 }
206
207 // --------------------------------------------------------------------
208
209 /**
210 * Prep the query
211 *
212 * If needed, each database adapter can prep the query string
213 *
214 * @access private called by execute()
215 * @param string an SQL query
216 * @return string
217 */
218 function _prep_query($sql)
219 {
220 return $sql;
221 }
222
223 // --------------------------------------------------------------------
224
225 /**
226 * Begin Transaction
227 *
228 * @access public
229 * @return bool
230 */
231 function trans_begin($test_mode = FALSE)
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 // Reset the transaction failure flag.
245 // If the $test_mode flag is set to TRUE transactions will be rolled back
246 // even if the queries produce a successful result.
247 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
248
Timothy Warrenab347582011-08-23 12:29:29 -0400249 return $this->conn_id->beginTransaction();
Timothy Warren80ab8162011-08-22 18:26:12 -0400250 }
251
252 // --------------------------------------------------------------------
253
254 /**
255 * Commit Transaction
256 *
257 * @access public
258 * @return bool
259 */
260 function trans_commit()
261 {
262 if ( ! $this->trans_enabled)
263 {
264 return TRUE;
265 }
266
267 // When transactions are nested we only begin/commit/rollback the outermost ones
268 if ($this->_trans_depth > 0)
269 {
270 return TRUE;
271 }
272
Timothy Warrenab347582011-08-23 12:29:29 -0400273 $ret = $this->conn->commit();
Timothy Warren80ab8162011-08-22 18:26:12 -0400274 return $ret;
275 }
276
277 // --------------------------------------------------------------------
278
279 /**
280 * Rollback Transaction
281 *
282 * @access public
283 * @return bool
284 */
285 function trans_rollback()
286 {
287 if ( ! $this->trans_enabled)
288 {
289 return TRUE;
290 }
291
292 // When transactions are nested we only begin/commit/rollback the outermost ones
293 if ($this->_trans_depth > 0)
294 {
295 return TRUE;
296 }
297
Timothy Warrenab347582011-08-23 12:29:29 -0400298 $ret = $this->conn_id->rollBack();
Timothy Warren80ab8162011-08-22 18:26:12 -0400299 return $ret;
300 }
301
302 // --------------------------------------------------------------------
303
304 /**
305 * Escape String
306 *
307 * @access public
308 * @param string
309 * @param bool whether or not the string will be used in a LIKE condition
310 * @return string
311 */
312 function escape_str($str, $like = FALSE)
313 {
314 if (is_array($str))
315 {
316 foreach ($str as $key => $val)
317 {
318 $str[$key] = $this->escape_str($val, $like);
319 }
320
321 return $str;
322 }
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400323
Timothy Warren47663972011-10-05 16:44:50 -0400324 //Escape the string
325 $str = $this->conn_id->quote($str);
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400326
Timothy Warren47663972011-10-05 16:44:50 -0400327 //If there are duplicated quotes, trim them away
Timothy Warrend6691532011-10-07 10:03:01 -0400328 if (strpos($str, "'") === 0)
Timothy Warrenec193322011-10-07 09:53:35 -0400329 {
330 $str = substr($str, 1, -1);
331 }
332
Timothy Warren80ab8162011-08-22 18:26:12 -0400333 // escape LIKE condition wildcards
334 if ($like === TRUE)
335 {
336 $str = str_replace( array('%', '_', $this->_like_escape_chr),
337 array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
338 $str);
339 }
340
341 return $str;
342 }
343
344 // --------------------------------------------------------------------
345
346 /**
347 * Affected Rows
348 *
349 * @access public
350 * @return integer
351 */
352 function affected_rows()
353 {
Timothy Warren51a48882011-09-14 13:47:06 -0400354 return $this->affect_rows;
Timothy Warren80ab8162011-08-22 18:26:12 -0400355 }
356
357 // --------------------------------------------------------------------
358
359 /**
360 * Insert ID
Timothy Warren57cea512011-09-14 14:26:28 -0400361 *
Timothy Warren80ab8162011-08-22 18:26:12 -0400362 * @access public
363 * @return integer
364 */
Timothy Warren51a48882011-09-14 13:47:06 -0400365 function insert_id($name=NULL)
Timothy Warren80ab8162011-08-22 18:26:12 -0400366 {
Timothy Warren33512752011-10-07 09:51:49 -0400367 //Convenience method for postgres insertid
Timothy Warrend6691532011-10-07 10:03:01 -0400368 if (strpos($this->hostname, 'pgsql') !== FALSE)
Timothy Warren33512752011-10-07 09:51:49 -0400369 {
370 $v = $this->_version();
371
372 $table = func_num_args() > 0 ? func_get_arg(0) : NULL;
373
374 if ($table == NULL && $v >= '8.1')
375 {
376 $sql='SELECT LASTVAL() as ins_id';
377 }
378 $query = $this->query($sql);
379 $row = $query->row();
380 return $row->ins_id;
381 }
382 else
383 {
384 return $this->conn_id->lastInsertId($name);
385 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400386 }
387
388 // --------------------------------------------------------------------
389
390 /**
391 * "Count All" query
392 *
393 * Generates a platform-specific query string that counts all records in
394 * the specified database
395 *
396 * @access public
397 * @param string
398 * @return string
399 */
400 function count_all($table = '')
401 {
402 if ($table == '')
403 {
404 return 0;
405 }
406
407 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
408
409 if ($query->num_rows() == 0)
410 {
411 return 0;
412 }
413
414 $row = $query->row();
415 $this->_reset_select();
416 return (int) $row->numrows;
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
422 * Show table query
423 *
424 * Generates a platform-specific query string so that the table names can be fetched
425 *
426 * @access private
427 * @param boolean
428 * @return string
429 */
430 function _list_tables($prefix_limit = FALSE)
431 {
432 $sql = "SHOW TABLES FROM `".$this->database."`";
433
434 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
435 {
Timothy Warren33512752011-10-07 09:51:49 -0400436 //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
Timothy Warren80ab8162011-08-22 18:26:12 -0400437 return FALSE; // not currently supported
438 }
439
440 return $sql;
441 }
442
443 // --------------------------------------------------------------------
444
445 /**
446 * Show column query
447 *
448 * Generates a platform-specific query string so that the column names can be fetched
449 *
450 * @access public
451 * @param string the table name
452 * @return string
453 */
454 function _list_columns($table = '')
455 {
456 return "SHOW COLUMNS FROM ".$table;
457 }
458
459 // --------------------------------------------------------------------
460
461 /**
462 * Field data query
463 *
464 * Generates a platform-specific query so that the column data can be retrieved
465 *
466 * @access public
467 * @param string the table name
468 * @return object
469 */
470 function _field_data($table)
471 {
472 return "SELECT TOP 1 FROM ".$table;
473 }
474
475 // --------------------------------------------------------------------
476
477 /**
478 * The error message string
479 *
480 * @access private
481 * @return string
482 */
483 function _error_message()
484 {
Timothy Warrenab347582011-08-23 12:29:29 -0400485 $error_array = $this->conn_id->errorInfo();
486 return $error_array[2];
Timothy Warren80ab8162011-08-22 18:26:12 -0400487 }
488
489 // --------------------------------------------------------------------
490
491 /**
492 * The error message number
493 *
494 * @access private
495 * @return integer
496 */
497 function _error_number()
498 {
Timothy Warrenab347582011-08-23 12:29:29 -0400499 return $this->conn_id->errorCode();
Timothy Warren80ab8162011-08-22 18:26:12 -0400500 }
501
502 // --------------------------------------------------------------------
503
504 /**
505 * Escape the SQL Identifiers
506 *
507 * This function escapes column and table names
508 *
509 * @access private
510 * @param string
511 * @return string
512 */
513 function _escape_identifiers($item)
514 {
515 if ($this->_escape_char == '')
516 {
517 return $item;
518 }
519
520 foreach ($this->_reserved_identifiers as $id)
521 {
522 if (strpos($item, '.'.$id) !== FALSE)
523 {
524 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
525
526 // remove duplicates if the user already included the escape
527 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
528 }
529 }
530
531 if (strpos($item, '.') !== FALSE)
532 {
533 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
Timothy Warren018af7a2011-09-07 12:07:35 -0400534
Timothy Warren80ab8162011-08-22 18:26:12 -0400535 }
536 else
537 {
538 $str = $this->_escape_char.$item.$this->_escape_char;
539 }
540
541 // remove duplicates if the user already included the escape
542 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
543 }
544
545 // --------------------------------------------------------------------
546
547 /**
548 * From Tables
549 *
550 * This function implicitly groups FROM tables so there is no confusion
551 * about operator precedence in harmony with SQL standards
552 *
553 * @access public
554 * @param type
555 * @return type
556 */
557 function _from_tables($tables)
558 {
559 if ( ! is_array($tables))
560 {
561 $tables = array($tables);
562 }
563
Timothy Warrenab347582011-08-23 12:29:29 -0400564 return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
Timothy Warren80ab8162011-08-22 18:26:12 -0400565 }
566
567 // --------------------------------------------------------------------
568
569 /**
570 * Insert statement
571 *
572 * Generates a platform-specific insert string from the supplied data
573 *
574 * @access public
575 * @param string the table name
576 * @param array the insert keys
577 * @param array the insert values
578 * @return string
579 */
580 function _insert($table, $keys, $values)
581 {
582 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
583 }
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400584
585 // --------------------------------------------------------------------
586
587 /**
588 * Insert_batch statement
589 *
590 * Generates a platform-specific insert string from the supplied data
591 *
592 * @access public
593 * @param string the table name
594 * @param array the insert keys
595 * @param array the insert values
596 * @return string
597 */
598 function _insert_batch($table, $keys, $values)
599 {
600 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
601 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400602
603 // --------------------------------------------------------------------
604
605 /**
606 * Update statement
607 *
608 * Generates a platform-specific update string from the supplied data
609 *
610 * @access public
611 * @param string the table name
612 * @param array the update data
613 * @param array the where clause
614 * @param array the orderby clause
615 * @param array the limit clause
616 * @return string
617 */
618 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
619 {
620 foreach ($values as $key => $val)
621 {
622 $valstr[] = $key." = ".$val;
623 }
624
625 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
626
627 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
628
629 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
630
631 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
632
633 $sql .= $orderby.$limit;
634
635 return $sql;
636 }
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400637
638 // --------------------------------------------------------------------
639
640 /**
641 * Update_Batch statement
642 *
643 * Generates a platform-specific batch update string from the supplied data
644 *
645 * @access public
646 * @param string the table name
647 * @param array the update data
648 * @param array the where clause
649 * @return string
650 */
651 function _update_batch($table, $values, $index, $where = NULL)
652 {
653 $ids = array();
654 $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
655
656 foreach ($values as $key => $val)
657 {
658 $ids[] = $val[$index];
659
660 foreach (array_keys($val) as $field)
661 {
662 if ($field != $index)
663 {
664 $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
665 }
666 }
667 }
668
669 $sql = "UPDATE ".$table." SET ";
670 $cases = '';
671
672 foreach ($final as $k => $v)
673 {
674 $cases .= $k.' = CASE '."\n";
675 foreach ($v as $row)
676 {
677 $cases .= $row."\n";
678 }
679
680 $cases .= 'ELSE '.$k.' END, ';
681 }
682
683 $sql .= substr($cases, 0, -2);
684
685 $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
686
687 return $sql;
688 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400689
690
691 // --------------------------------------------------------------------
692
693 /**
694 * Truncate statement
695 *
696 * Generates a platform-specific truncate string from the supplied data
697 * If the database does not support the truncate() command
698 * This function maps to "DELETE FROM table"
699 *
700 * @access public
701 * @param string the table name
702 * @return string
703 */
704 function _truncate($table)
705 {
706 return $this->_delete($table);
707 }
708
709 // --------------------------------------------------------------------
710
711 /**
712 * Delete statement
713 *
714 * Generates a platform-specific delete string from the supplied data
715 *
716 * @access public
717 * @param string the table name
718 * @param array the where clause
719 * @param string the limit clause
720 * @return string
721 */
722 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
723 {
724 $conditions = '';
725
726 if (count($where) > 0 OR count($like) > 0)
727 {
728 $conditions = "\nWHERE ";
729 $conditions .= implode("\n", $this->ar_where);
730
731 if (count($where) > 0 && count($like) > 0)
732 {
733 $conditions .= " AND ";
734 }
735 $conditions .= implode("\n", $like);
736 }
737
738 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
739
740 return "DELETE FROM ".$table.$conditions.$limit;
741 }
742
743 // --------------------------------------------------------------------
744
745 /**
746 * Limit string
747 *
748 * Generates a platform-specific LIMIT clause
749 *
750 * @access public
751 * @param string the sql query string
752 * @param integer the number of rows to limit the query to
753 * @param integer the offset value
754 * @return string
755 */
756 function _limit($sql, $limit, $offset)
757 {
Timothy Warrend6691532011-10-07 10:03:01 -0400758 if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE)
Timothy Warren0a43ad82011-09-15 20:15:19 -0400759 {
760 if ($offset == 0)
761 {
762 $offset = '';
763 }
764 else
765 {
766 $offset .= ", ";
767 }
768
769 return $sql."LIMIT ".$offset.$limit;
770 }
771 else
772 {
773 $sql .= "LIMIT ".$limit;
774
775 if ($offset > 0)
776 {
777 $sql .= " OFFSET ".$offset;
778 }
779
780 return $sql;
781 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400782 }
783
784 // --------------------------------------------------------------------
785
786 /**
787 * Close DB Connection
788 *
789 * @access public
790 * @param resource
791 * @return void
792 */
793 function _close($conn_id)
794 {
Timothy Warren6a450cf2011-08-23 12:46:11 -0400795 $this->conn_id = null;
Timothy Warren80ab8162011-08-22 18:26:12 -0400796 }
797
798
799}
800
801
802
803/* End of file pdo_driver.php */
804/* Location: ./system/database/drivers/pdo/pdo_driver.php */