blob: 5299f1a132603a043c839f78ccf2720975636f84 [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 = '';
37
38 // clause and character used for LIKE escape sequences
39 var $_like_escape_str = " {escape '%s'} ";
40 var $_like_escape_chr = '!';
41
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;
49
50
Timothy Warren51b0e642011-09-13 13:30:27 -040051 function __construct($params)
Timothy Warren80ab8162011-08-22 18:26:12 -040052 {
53 parent::CI_DB($params);
Timothy Warrenab347582011-08-23 12:29:29 -040054
55 $this->hostname = $this->hostname . ";dbname=".$this->database;
56 $this->trans_enabled = FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -040057
58 $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
59 }
60
61 /**
62 * Non-persistent database connection
63 *
64 * @access private called by the base class
65 * @return resource
66 */
67 function db_connect()
68 {
Timothy Warrenab347582011-08-23 12:29:29 -040069 return new PDO($this->hostname,$this->username,$this->password, array(
70 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT
Timothy Warren80ab8162011-08-22 18:26:12 -040071 ));
72 }
73
74 // --------------------------------------------------------------------
75
76 /**
77 * Persistent database connection
78 *
79 * @access private called by the base class
80 * @return resource
81 */
82 function db_pconnect()
83 {
Timothy Warrenab347582011-08-23 12:29:29 -040084 return new PDO($this->hostname,$this->username,$this->password, array(
85 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
86 PDO::ATTR_PERSISTENT => true
Timothy Warren80ab8162011-08-22 18:26:12 -040087 ));
88 }
89
90 // --------------------------------------------------------------------
91
92 /**
93 * Reconnect
94 *
95 * Keep / reestablish the db connection if no queries have been
96 * sent for a length of time exceeding the server's idle timeout
97 *
98 * @access public
99 * @return void
100 */
101 function reconnect()
102 {
Timothy Warren02615962011-08-24 08:21:36 -0400103 if ($this->db->db_debug)
104 {
105 return $this->db->display_error('db_unsuported_feature');
106 }
107 return FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -0400108 }
109
110 // --------------------------------------------------------------------
111
112 /**
113 * Select the database
114 *
115 * @access private called by the base class
116 * @return resource
117 */
118 function db_select()
119 {
120 // Not needed for PDO
121 return TRUE;
122 }
123
124 // --------------------------------------------------------------------
125
126 /**
127 * Set client character set
128 *
129 * @access public
130 * @param string
131 * @param string
132 * @return resource
133 */
134 function db_set_charset($charset, $collation)
135 {
136 // @todo - add support if needed
137 return TRUE;
138 }
139
140 // --------------------------------------------------------------------
141
142 /**
143 * Version number query string
144 *
145 * @access public
146 * @return string
147 */
148 function _version()
149 {
Timothy Warren36fb8de2011-08-24 08:29:05 -0400150 return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
Timothy Warren80ab8162011-08-22 18:26:12 -0400151 }
152
153 // --------------------------------------------------------------------
154
155 /**
156 * Execute the query
157 *
158 * @access private called by the base class
159 * @param string an SQL query
160 * @return resource
161 */
162 function _execute($sql)
163 {
164 $sql = $this->_prep_query($sql);
Timothy Warrenab347582011-08-23 12:29:29 -0400165 return $this->conn_id->query($sql);
Timothy Warren80ab8162011-08-22 18:26:12 -0400166 }
167
168 // --------------------------------------------------------------------
169
170 /**
171 * Prep the query
172 *
173 * If needed, each database adapter can prep the query string
174 *
175 * @access private called by execute()
176 * @param string an SQL query
177 * @return string
178 */
179 function _prep_query($sql)
180 {
181 return $sql;
182 }
183
184 // --------------------------------------------------------------------
185
186 /**
187 * Begin Transaction
188 *
189 * @access public
190 * @return bool
191 */
192 function trans_begin($test_mode = FALSE)
193 {
194 if ( ! $this->trans_enabled)
195 {
196 return TRUE;
197 }
198
199 // When transactions are nested we only begin/commit/rollback the outermost ones
200 if ($this->_trans_depth > 0)
201 {
202 return TRUE;
203 }
204
205 // Reset the transaction failure flag.
206 // If the $test_mode flag is set to TRUE transactions will be rolled back
207 // even if the queries produce a successful result.
208 $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
209
Timothy Warrenab347582011-08-23 12:29:29 -0400210 return $this->conn_id->beginTransaction();
Timothy Warren80ab8162011-08-22 18:26:12 -0400211 }
212
213 // --------------------------------------------------------------------
214
215 /**
216 * Commit Transaction
217 *
218 * @access public
219 * @return bool
220 */
221 function trans_commit()
222 {
223 if ( ! $this->trans_enabled)
224 {
225 return TRUE;
226 }
227
228 // When transactions are nested we only begin/commit/rollback the outermost ones
229 if ($this->_trans_depth > 0)
230 {
231 return TRUE;
232 }
233
Timothy Warrenab347582011-08-23 12:29:29 -0400234 $ret = $this->conn->commit();
Timothy Warren80ab8162011-08-22 18:26:12 -0400235 return $ret;
236 }
237
238 // --------------------------------------------------------------------
239
240 /**
241 * Rollback Transaction
242 *
243 * @access public
244 * @return bool
245 */
246 function trans_rollback()
247 {
248 if ( ! $this->trans_enabled)
249 {
250 return TRUE;
251 }
252
253 // When transactions are nested we only begin/commit/rollback the outermost ones
254 if ($this->_trans_depth > 0)
255 {
256 return TRUE;
257 }
258
Timothy Warrenab347582011-08-23 12:29:29 -0400259 $ret = $this->conn_id->rollBack();
Timothy Warren80ab8162011-08-22 18:26:12 -0400260 return $ret;
261 }
262
263 // --------------------------------------------------------------------
264
265 /**
266 * Escape String
267 *
268 * @access public
269 * @param string
270 * @param bool whether or not the string will be used in a LIKE condition
271 * @return string
272 */
273 function escape_str($str, $like = FALSE)
274 {
275 if (is_array($str))
276 {
277 foreach ($str as $key => $val)
278 {
279 $str[$key] = $this->escape_str($val, $like);
280 }
281
282 return $str;
283 }
284
285 // PDO doesn't require escaping
286 $str = remove_invisible_characters($str);
287
288 // escape LIKE condition wildcards
289 if ($like === TRUE)
290 {
291 $str = str_replace( array('%', '_', $this->_like_escape_chr),
292 array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
293 $str);
294 }
295
296 return $str;
297 }
298
299 // --------------------------------------------------------------------
300
301 /**
302 * Affected Rows
303 *
304 * @access public
305 * @return integer
306 */
307 function affected_rows()
308 {
Timothy Warrena6c65332011-09-14 12:03:27 -0400309 if ($this->db->db_debug)
310 {
311 return $this->db->display_error('db_unsuported_feature');
312 }
313 return FALSE;
Timothy Warren80ab8162011-08-22 18:26:12 -0400314 }
315
316 // --------------------------------------------------------------------
317
318 /**
319 * Insert ID
320 *
321 * @access public
322 * @return integer
323 */
324 function insert_id()
325 {
Timothy Warrenab347582011-08-23 12:29:29 -0400326 return $this->conn_id->lastInsertId();
Timothy Warren80ab8162011-08-22 18:26:12 -0400327 }
328
329 // --------------------------------------------------------------------
330
331 /**
332 * "Count All" query
333 *
334 * Generates a platform-specific query string that counts all records in
335 * the specified database
336 *
337 * @access public
338 * @param string
339 * @return string
340 */
341 function count_all($table = '')
342 {
343 if ($table == '')
344 {
345 return 0;
346 }
347
348 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
349
350 if ($query->num_rows() == 0)
351 {
352 return 0;
353 }
354
355 $row = $query->row();
356 $this->_reset_select();
357 return (int) $row->numrows;
358 }
359
360 // --------------------------------------------------------------------
361
362 /**
363 * Show table query
364 *
365 * Generates a platform-specific query string so that the table names can be fetched
366 *
367 * @access private
368 * @param boolean
369 * @return string
370 */
371 function _list_tables($prefix_limit = FALSE)
372 {
373 $sql = "SHOW TABLES FROM `".$this->database."`";
374
375 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
376 {
377 //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
378 return FALSE; // not currently supported
379 }
380
381 return $sql;
382 }
383
384 // --------------------------------------------------------------------
385
386 /**
387 * Show column query
388 *
389 * Generates a platform-specific query string so that the column names can be fetched
390 *
391 * @access public
392 * @param string the table name
393 * @return string
394 */
395 function _list_columns($table = '')
396 {
397 return "SHOW COLUMNS FROM ".$table;
398 }
399
400 // --------------------------------------------------------------------
401
402 /**
403 * Field data query
404 *
405 * Generates a platform-specific query so that the column data can be retrieved
406 *
407 * @access public
408 * @param string the table name
409 * @return object
410 */
411 function _field_data($table)
412 {
413 return "SELECT TOP 1 FROM ".$table;
414 }
415
416 // --------------------------------------------------------------------
417
418 /**
419 * The error message string
420 *
421 * @access private
422 * @return string
423 */
424 function _error_message()
425 {
Timothy Warrenab347582011-08-23 12:29:29 -0400426 $error_array = $this->conn_id->errorInfo();
427 return $error_array[2];
Timothy Warren80ab8162011-08-22 18:26:12 -0400428 }
429
430 // --------------------------------------------------------------------
431
432 /**
433 * The error message number
434 *
435 * @access private
436 * @return integer
437 */
438 function _error_number()
439 {
Timothy Warrenab347582011-08-23 12:29:29 -0400440 return $this->conn_id->errorCode();
Timothy Warren80ab8162011-08-22 18:26:12 -0400441 }
442
443 // --------------------------------------------------------------------
444
445 /**
446 * Escape the SQL Identifiers
447 *
448 * This function escapes column and table names
449 *
450 * @access private
451 * @param string
452 * @return string
453 */
454 function _escape_identifiers($item)
455 {
456 if ($this->_escape_char == '')
457 {
458 return $item;
459 }
460
461 foreach ($this->_reserved_identifiers as $id)
462 {
463 if (strpos($item, '.'.$id) !== FALSE)
464 {
465 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
466
467 // remove duplicates if the user already included the escape
468 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
469 }
470 }
471
472 if (strpos($item, '.') !== FALSE)
473 {
474 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
Timothy Warren018af7a2011-09-07 12:07:35 -0400475
Timothy Warren80ab8162011-08-22 18:26:12 -0400476 }
477 else
478 {
479 $str = $this->_escape_char.$item.$this->_escape_char;
480 }
481
482 // remove duplicates if the user already included the escape
483 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
484 }
485
486 // --------------------------------------------------------------------
487
488 /**
489 * From Tables
490 *
491 * This function implicitly groups FROM tables so there is no confusion
492 * about operator precedence in harmony with SQL standards
493 *
494 * @access public
495 * @param type
496 * @return type
497 */
498 function _from_tables($tables)
499 {
500 if ( ! is_array($tables))
501 {
502 $tables = array($tables);
503 }
504
Timothy Warrenab347582011-08-23 12:29:29 -0400505 return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
Timothy Warren80ab8162011-08-22 18:26:12 -0400506 }
507
508 // --------------------------------------------------------------------
509
510 /**
511 * Insert statement
512 *
513 * Generates a platform-specific insert string from the supplied data
514 *
515 * @access public
516 * @param string the table name
517 * @param array the insert keys
518 * @param array the insert values
519 * @return string
520 */
521 function _insert($table, $keys, $values)
522 {
523 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
524 }
525
526 // --------------------------------------------------------------------
527
528 /**
529 * Update statement
530 *
531 * Generates a platform-specific update string from the supplied data
532 *
533 * @access public
534 * @param string the table name
535 * @param array the update data
536 * @param array the where clause
537 * @param array the orderby clause
538 * @param array the limit clause
539 * @return string
540 */
541 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
542 {
543 foreach ($values as $key => $val)
544 {
545 $valstr[] = $key." = ".$val;
546 }
547
548 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
549
550 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
551
552 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
553
554 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
555
556 $sql .= $orderby.$limit;
557
558 return $sql;
559 }
560
561
562 // --------------------------------------------------------------------
563
564 /**
565 * Truncate statement
566 *
567 * Generates a platform-specific truncate string from the supplied data
568 * If the database does not support the truncate() command
569 * This function maps to "DELETE FROM table"
570 *
571 * @access public
572 * @param string the table name
573 * @return string
574 */
575 function _truncate($table)
576 {
577 return $this->_delete($table);
578 }
579
580 // --------------------------------------------------------------------
581
582 /**
583 * Delete statement
584 *
585 * Generates a platform-specific delete string from the supplied data
586 *
587 * @access public
588 * @param string the table name
589 * @param array the where clause
590 * @param string the limit clause
591 * @return string
592 */
593 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
594 {
595 $conditions = '';
596
597 if (count($where) > 0 OR count($like) > 0)
598 {
599 $conditions = "\nWHERE ";
600 $conditions .= implode("\n", $this->ar_where);
601
602 if (count($where) > 0 && count($like) > 0)
603 {
604 $conditions .= " AND ";
605 }
606 $conditions .= implode("\n", $like);
607 }
608
609 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
610
611 return "DELETE FROM ".$table.$conditions.$limit;
612 }
613
614 // --------------------------------------------------------------------
615
616 /**
617 * Limit string
618 *
619 * Generates a platform-specific LIMIT clause
620 *
621 * @access public
622 * @param string the sql query string
623 * @param integer the number of rows to limit the query to
624 * @param integer the offset value
625 * @return string
626 */
627 function _limit($sql, $limit, $offset)
628 {
629 // Does PDO doesn't use the LIMIT clause?
630 return $sql;
631 }
632
633 // --------------------------------------------------------------------
634
635 /**
636 * Close DB Connection
637 *
638 * @access public
639 * @param resource
640 * @return void
641 */
642 function _close($conn_id)
643 {
Timothy Warren6a450cf2011-08-23 12:46:11 -0400644 $this->conn_id = null;
Timothy Warren80ab8162011-08-22 18:26:12 -0400645 }
646
647
648}
649
650
651
652/* End of file pdo_driver.php */
653/* Location: ./system/database/drivers/pdo/pdo_driver.php */