blob: f698932730913b15bc7a791a4323cdac3cd14dba [file] [log] [blame]
Timothy Warren24f325c2011-10-07 10:03:01 -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
12 * @since Version 2.1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * PDO 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 */
Timothy Warren9f5316e2011-09-14 12:25:14 -040031
Timothy Warren24f325c2011-10-07 10:03:01 -040032class CI_DB_pdo_driver extends CI_DB {
33
34 var $dbdriver = 'pdo';
35
36 // the character used to excape - not necessary for PDO
37 var $_escape_char = '';
38 var $_like_escape_str;
39 var $_like_escape_chr;
Timothy Warren9f5316e2011-09-14 12:25:14 -040040
Timothy Warren24f325c2011-10-07 10:03:01 -040041
42 /**
43 * The syntax to count rows is slightly different across different
44 * database engines, so this string appears in each driver and is
45 * used for the count_all() and count_all_results() functions.
46 */
47 var $_count_string = "SELECT COUNT(*) AS ";
48 var $_random_keyword;
Timothy Warren530becb2011-10-25 10:37:31 -040049
50 var $options = array();
Timothy Warren24f325c2011-10-07 10:03:01 -040051
52 function __construct($params)
53 {
54 parent::__construct($params);
Timothy Warren9f5316e2011-09-14 12:25:14 -040055
Timothy Warren24f325c2011-10-07 10:03:01 -040056 // clause and character used for LIKE escape sequences
57 if (strpos($this->hostname, 'mysql') !== FALSE)
58 {
59 $this->_like_escape_str = '';
60 $this->_like_escape_chr = '';
Timothy Warren530becb2011-10-25 10:37:31 -040061
Timothy Warrend019fd62011-10-26 11:26:17 -040062 //Prior to this version, the charset can't be set in the dsn
63 if(is_php('5.3.6'))
64 {
65 $this->hostname .= ";charset={$this->char_set}";
66 }
67
Timothy Warren530becb2011-10-25 10:37:31 -040068 //Set the charset with the connection options
69 $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}";
Timothy Warren24f325c2011-10-07 10:03:01 -040070 }
71 else if (strpos($this->hostname, 'odbc') !== FALSE)
72 {
73 $this->_like_escape_str = " {escape '%s'} ";
74 $this->_like_escape_chr = '!';
75 }
76 else
77 {
78 $this->_like_escape_str = " ESCAPE '%s' ";
79 $this->_like_escape_chr = '!';
80 }
Timothy Warren70eeb932011-10-26 11:31:49 -040081
82 $this->hostname .= ";dbname=".$this->database;
Timothy Warren24f325c2011-10-07 10:03:01 -040083 $this->trans_enabled = FALSE;
84
85 $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
86 }
87
88 /**
89 * Non-persistent database connection
90 *
91 * @access private called by the base class
92 * @return resource
93 */
94 function db_connect()
95 {
Timothy Warren530becb2011-10-25 10:37:31 -040096 $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
Timothy Warren70eeb932011-10-26 11:31:49 -040097
98 return new PDO($this->hostname, $this->username, $this->password, $this->options);
Timothy Warren24f325c2011-10-07 10:03:01 -040099 }
100
101 // --------------------------------------------------------------------
102
103 /**
104 * Persistent database connection
105 *
106 * @access private called by the base class
107 * @return resource
108 */
109 function db_pconnect()
110 {
Timothy Warren70eeb932011-10-26 11:31:49 -0400111 $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
112 $this->options['PDO::ATTR_PERSISTENT'] = TRUE;
113
114 return new PDO($this->hostname, $this->username, $this->password, $this->options);
Timothy Warren24f325c2011-10-07 10:03:01 -0400115 }
116
117 // --------------------------------------------------------------------
118
119 /**
120 * Reconnect
121 *
122 * Keep / reestablish the db connection if no queries have been
123 * sent for a length of time exceeding the server's idle timeout
124 *
125 * @access public
126 * @return void
127 */
128 function reconnect()
129 {
130 if ($this->db->db_debug)
131 {
132 return $this->db->display_error('db_unsuported_feature');
133 }
134 return FALSE;
135 }
136
137 // --------------------------------------------------------------------
138
139 /**
140 * Select the database
141 *
142 * @access private called by the base class
143 * @return resource
144 */
145 function db_select()
146 {
147 // Not needed for PDO
148 return TRUE;
149 }
150
151 // --------------------------------------------------------------------
152
153 /**
154 * Set client character set
155 *
156 * @access public
157 * @param string
158 * @param string
159 * @return resource
160 */
161 function db_set_charset($charset, $collation)
162 {
163 // @todo - add support if needed
164 return TRUE;
165 }
166
167 // --------------------------------------------------------------------
168
169 /**
170 * Version number query string
171 *
172 * @access public
173 * @return string
174 */
175 function _version()
176 {
177 return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
178 }
179
180 // --------------------------------------------------------------------
181
182 /**
183 * Execute the query
184 *
185 * @access private called by the base class
186 * @param string an SQL query
187 * @return object
188 */
189 function _execute($sql)
190 {
191 $sql = $this->_prep_query($sql);
192 $result_id = $this->conn_id->query($sql);
Timothy Warren9f5316e2011-09-14 12:25:14 -0400193
Timothy Warren24f325c2011-10-07 10:03:01 -0400194 if (is_object($result_id))
195 {
196 $this->affect_rows = $result_id->rowCount();
197 }
198 else
199 {
200 $this->affect_rows = 0;
201 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400202
Timothy Warren24f325c2011-10-07 10:03:01 -0400203 return $result_id;
204 }
205
206 // --------------------------------------------------------------------
207
208 /**
209 * Prep the query
210 *
211 * If needed, each database adapter can prep the query string
212 *
213 * @access private called by execute()
214 * @param string an SQL query
215 * @return string
216 */
217 function _prep_query($sql)
218 {
219 return $sql;
220 }
221
222 // --------------------------------------------------------------------
223
224 /**
225 * Begin Transaction
226 *
227 * @access public
228 * @return bool
229 */
230 function trans_begin($test_mode = FALSE)
231 {
232 if ( ! $this->trans_enabled)
233 {
234 return TRUE;
235 }
236
237 // When transactions are nested we only begin/commit/rollback the outermost ones
238 if ($this->_trans_depth > 0)
239 {
240 return TRUE;
241 }
242
243 // Reset the transaction failure flag.
244 // If the $test_mode flag is set to TRUE transactions will be rolled back
245 // even if the queries produce a successful result.
Timothy Warrend019fd62011-10-26 11:26:17 -0400246 $this->_trans_failure = (bool) ($test_mode === TRUE);
Timothy Warren24f325c2011-10-07 10:03:01 -0400247
248 return $this->conn_id->beginTransaction();
249 }
250
251 // --------------------------------------------------------------------
252
253 /**
254 * Commit Transaction
255 *
256 * @access public
257 * @return bool
258 */
259 function trans_commit()
260 {
261 if ( ! $this->trans_enabled)
262 {
263 return TRUE;
264 }
265
266 // When transactions are nested we only begin/commit/rollback the outermost ones
267 if ($this->_trans_depth > 0)
268 {
269 return TRUE;
270 }
271
272 $ret = $this->conn->commit();
273 return $ret;
274 }
275
276 // --------------------------------------------------------------------
277
278 /**
279 * Rollback Transaction
280 *
281 * @access public
282 * @return bool
283 */
284 function trans_rollback()
285 {
286 if ( ! $this->trans_enabled)
287 {
288 return TRUE;
289 }
290
291 // When transactions are nested we only begin/commit/rollback the outermost ones
292 if ($this->_trans_depth > 0)
293 {
294 return TRUE;
295 }
296
297 $ret = $this->conn_id->rollBack();
298 return $ret;
299 }
300
301 // --------------------------------------------------------------------
302
303 /**
304 * Escape String
305 *
306 * @access public
307 * @param string
308 * @param bool whether or not the string will be used in a LIKE condition
309 * @return string
310 */
311 function escape_str($str, $like = FALSE)
312 {
313 if (is_array($str))
314 {
315 foreach ($str as $key => $val)
316 {
317 $str[$key] = $this->escape_str($val, $like);
318 }
319
320 return $str;
321 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400322
Timothy Warren24f325c2011-10-07 10:03:01 -0400323 //Escape the string
324 $str = $this->conn_id->quote($str);
Timothy Warren9f5316e2011-09-14 12:25:14 -0400325
Timothy Warren24f325c2011-10-07 10:03:01 -0400326 //If there are duplicated quotes, trim them away
327 if (strpos($str, "'") === 0)
328 {
329 $str = substr($str, 1, -1);
330 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400331
Timothy Warren24f325c2011-10-07 10:03:01 -0400332 // escape LIKE condition wildcards
333 if ($like === TRUE)
334 {
335 $str = str_replace( array('%', '_', $this->_like_escape_chr),
336 array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
337 $str);
338 }
339
340 return $str;
341 }
342
343 // --------------------------------------------------------------------
344
345 /**
346 * Affected Rows
347 *
348 * @access public
349 * @return integer
350 */
351 function affected_rows()
352 {
353 return $this->affect_rows;
354 }
355
356 // --------------------------------------------------------------------
357
358 /**
359 * Insert ID
360 *
361 * @access public
362 * @return integer
363 */
364 function insert_id($name=NULL)
365 {
366 //Convenience method for postgres insertid
367 if (strpos($this->hostname, 'pgsql') !== FALSE)
368 {
369 $v = $this->_version();
370
371 $table = func_num_args() > 0 ? func_get_arg(0) : NULL;
372
373 if ($table == NULL && $v >= '8.1')
374 {
375 $sql='SELECT LASTVAL() as ins_id';
376 }
377 $query = $this->query($sql);
378 $row = $query->row();
379 return $row->ins_id;
380 }
381 else
382 {
383 return $this->conn_id->lastInsertId($name);
384 }
385 }
386
387 // --------------------------------------------------------------------
388
389 /**
390 * "Count All" query
391 *
392 * Generates a platform-specific query string that counts all records in
393 * the specified database
394 *
395 * @access public
396 * @param string
397 * @return string
398 */
399 function count_all($table = '')
400 {
401 if ($table == '')
402 {
403 return 0;
404 }
405
406 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
407
408 if ($query->num_rows() == 0)
409 {
410 return 0;
411 }
412
413 $row = $query->row();
414 $this->_reset_select();
415 return (int) $row->numrows;
416 }
417
418 // --------------------------------------------------------------------
419
420 /**
421 * Show table query
422 *
423 * Generates a platform-specific query string so that the table names can be fetched
424 *
425 * @access private
426 * @param boolean
427 * @return string
428 */
429 function _list_tables($prefix_limit = FALSE)
430 {
431 $sql = "SHOW TABLES FROM `".$this->database."`";
432
433 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
434 {
435 //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
436 return FALSE; // not currently supported
437 }
438
439 return $sql;
440 }
441
442 // --------------------------------------------------------------------
443
444 /**
445 * Show column query
446 *
447 * Generates a platform-specific query string so that the column names can be fetched
448 *
449 * @access public
450 * @param string the table name
451 * @return string
452 */
453 function _list_columns($table = '')
454 {
455 return "SHOW COLUMNS FROM ".$table;
456 }
457
458 // --------------------------------------------------------------------
459
460 /**
461 * Field data query
462 *
463 * Generates a platform-specific query so that the column data can be retrieved
464 *
465 * @access public
466 * @param string the table name
467 * @return object
468 */
469 function _field_data($table)
470 {
471 return "SELECT TOP 1 FROM ".$table;
472 }
473
474 // --------------------------------------------------------------------
475
476 /**
477 * The error message string
478 *
479 * @access private
480 * @return string
481 */
482 function _error_message()
483 {
484 $error_array = $this->conn_id->errorInfo();
485 return $error_array[2];
486 }
487
488 // --------------------------------------------------------------------
489
490 /**
491 * The error message number
492 *
493 * @access private
494 * @return integer
495 */
496 function _error_number()
497 {
498 return $this->conn_id->errorCode();
499 }
500
501 // --------------------------------------------------------------------
502
503 /**
504 * Escape the SQL Identifiers
505 *
506 * This function escapes column and table names
507 *
508 * @access private
509 * @param string
510 * @return string
511 */
512 function _escape_identifiers($item)
513 {
514 if ($this->_escape_char == '')
515 {
516 return $item;
517 }
518
519 foreach ($this->_reserved_identifiers as $id)
520 {
521 if (strpos($item, '.'.$id) !== FALSE)
522 {
523 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
524
525 // remove duplicates if the user already included the escape
526 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
527 }
528 }
529
530 if (strpos($item, '.') !== FALSE)
531 {
532 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
Timothy Warren9f5316e2011-09-14 12:25:14 -0400533
Timothy Warren24f325c2011-10-07 10:03:01 -0400534 }
535 else
536 {
537 $str = $this->_escape_char.$item.$this->_escape_char;
538 }
539
540 // remove duplicates if the user already included the escape
541 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
542 }
543
544 // --------------------------------------------------------------------
545
546 /**
547 * From Tables
548 *
549 * This function implicitly groups FROM tables so there is no confusion
550 * about operator precedence in harmony with SQL standards
551 *
552 * @access public
553 * @param type
554 * @return type
555 */
556 function _from_tables($tables)
557 {
558 if ( ! is_array($tables))
559 {
560 $tables = array($tables);
561 }
562
563 return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
564 }
565
566 // --------------------------------------------------------------------
567
568 /**
569 * Insert statement
570 *
571 * Generates a platform-specific insert string from the supplied data
572 *
573 * @access public
574 * @param string the table name
575 * @param array the insert keys
576 * @param array the insert values
577 * @return string
578 */
579 function _insert($table, $keys, $values)
580 {
581 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
582 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400583
Timothy Warren24f325c2011-10-07 10:03:01 -0400584 // --------------------------------------------------------------------
585
586 /**
587 * Insert_batch statement
588 *
589 * Generates a platform-specific insert string from the supplied data
590 *
591 * @access public
592 * @param string the table name
593 * @param array the insert keys
594 * @param array the insert values
595 * @return string
596 */
597 function _insert_batch($table, $keys, $values)
598 {
599 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
600 }
601
602 // --------------------------------------------------------------------
603
604 /**
605 * Update statement
606 *
607 * Generates a platform-specific update string from the supplied data
608 *
609 * @access public
610 * @param string the table name
611 * @param array the update data
612 * @param array the where clause
613 * @param array the orderby clause
614 * @param array the limit clause
615 * @return string
616 */
617 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
618 {
619 foreach ($values as $key => $val)
620 {
621 $valstr[] = $key." = ".$val;
622 }
623
624 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
625
626 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
627
628 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
629
630 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
631
632 $sql .= $orderby.$limit;
633
634 return $sql;
635 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400636
Timothy Warren24f325c2011-10-07 10:03:01 -0400637 // --------------------------------------------------------------------
638
639 /**
640 * Update_Batch statement
641 *
642 * Generates a platform-specific batch update string from the supplied data
643 *
644 * @access public
645 * @param string the table name
646 * @param array the update data
647 * @param array the where clause
648 * @return string
649 */
650 function _update_batch($table, $values, $index, $where = NULL)
651 {
652 $ids = array();
653 $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
654
655 foreach ($values as $key => $val)
656 {
657 $ids[] = $val[$index];
658
659 foreach (array_keys($val) as $field)
660 {
661 if ($field != $index)
662 {
663 $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
664 }
665 }
666 }
667
668 $sql = "UPDATE ".$table." SET ";
669 $cases = '';
670
671 foreach ($final as $k => $v)
672 {
673 $cases .= $k.' = CASE '."\n";
674 foreach ($v as $row)
675 {
676 $cases .= $row."\n";
677 }
678
679 $cases .= 'ELSE '.$k.' END, ';
680 }
681
682 $sql .= substr($cases, 0, -2);
683
684 $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
685
686 return $sql;
687 }
688
689
690 // --------------------------------------------------------------------
691
692 /**
693 * Truncate statement
694 *
695 * Generates a platform-specific truncate string from the supplied data
696 * If the database does not support the truncate() command
697 * This function maps to "DELETE FROM table"
698 *
699 * @access public
700 * @param string the table name
701 * @return string
702 */
703 function _truncate($table)
704 {
705 return $this->_delete($table);
706 }
707
708 // --------------------------------------------------------------------
709
710 /**
711 * Delete statement
712 *
713 * Generates a platform-specific delete string from the supplied data
714 *
715 * @access public
716 * @param string the table name
717 * @param array the where clause
718 * @param string the limit clause
719 * @return string
720 */
721 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
722 {
723 $conditions = '';
724
725 if (count($where) > 0 OR count($like) > 0)
726 {
727 $conditions = "\nWHERE ";
728 $conditions .= implode("\n", $this->ar_where);
729
730 if (count($where) > 0 && count($like) > 0)
731 {
732 $conditions .= " AND ";
733 }
734 $conditions .= implode("\n", $like);
735 }
736
737 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
738
739 return "DELETE FROM ".$table.$conditions.$limit;
740 }
741
742 // --------------------------------------------------------------------
743
744 /**
745 * Limit string
746 *
747 * Generates a platform-specific LIMIT clause
748 *
749 * @access public
750 * @param string the sql query string
751 * @param integer the number of rows to limit the query to
752 * @param integer the offset value
753 * @return string
754 */
755 function _limit($sql, $limit, $offset)
756 {
757 if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE)
758 {
759 if ($offset == 0)
760 {
761 $offset = '';
762 }
763 else
764 {
765 $offset .= ", ";
766 }
767
768 return $sql."LIMIT ".$offset.$limit;
769 }
770 else
771 {
772 $sql .= "LIMIT ".$limit;
773
774 if ($offset > 0)
775 {
776 $sql .= " OFFSET ".$offset;
777 }
Timothy Warren9f5316e2011-09-14 12:25:14 -0400778
Timothy Warren24f325c2011-10-07 10:03:01 -0400779 return $sql;
780 }
781 }
782
783 // --------------------------------------------------------------------
784
785 /**
786 * Close DB Connection
787 *
788 * @access public
789 * @param resource
790 * @return void
791 */
792 function _close($conn_id)
793 {
794 $this->conn_id = null;
795 }
796
797
798}
799
800
801
802/* End of file pdo_driver.php */
803/* Location: ./system/database/drivers/pdo/pdo_driver.php */