blob: ccf1091c950e94deaa948ce893e7932efd82f52b [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
51 function CI_DB_pdo_driver($params)
52 {
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 {
309 return @pdo_num_rows($this->conn_id);
310 }
311
312 // --------------------------------------------------------------------
313
314 /**
315 * Insert ID
316 *
317 * @access public
318 * @return integer
319 */
320 function insert_id()
321 {
Timothy Warrenab347582011-08-23 12:29:29 -0400322 return $this->conn_id->lastInsertId();
Timothy Warren80ab8162011-08-22 18:26:12 -0400323 }
324
325 // --------------------------------------------------------------------
326
327 /**
328 * "Count All" query
329 *
330 * Generates a platform-specific query string that counts all records in
331 * the specified database
332 *
333 * @access public
334 * @param string
335 * @return string
336 */
337 function count_all($table = '')
338 {
339 if ($table == '')
340 {
341 return 0;
342 }
343
344 $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
345
346 if ($query->num_rows() == 0)
347 {
348 return 0;
349 }
350
351 $row = $query->row();
352 $this->_reset_select();
353 return (int) $row->numrows;
354 }
355
356 // --------------------------------------------------------------------
357
358 /**
359 * Show table query
360 *
361 * Generates a platform-specific query string so that the table names can be fetched
362 *
363 * @access private
364 * @param boolean
365 * @return string
366 */
367 function _list_tables($prefix_limit = FALSE)
368 {
369 $sql = "SHOW TABLES FROM `".$this->database."`";
370
371 if ($prefix_limit !== FALSE AND $this->dbprefix != '')
372 {
373 //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
374 return FALSE; // not currently supported
375 }
376
377 return $sql;
378 }
379
380 // --------------------------------------------------------------------
381
382 /**
383 * Show column query
384 *
385 * Generates a platform-specific query string so that the column names can be fetched
386 *
387 * @access public
388 * @param string the table name
389 * @return string
390 */
391 function _list_columns($table = '')
392 {
393 return "SHOW COLUMNS FROM ".$table;
394 }
395
396 // --------------------------------------------------------------------
397
398 /**
399 * Field data query
400 *
401 * Generates a platform-specific query so that the column data can be retrieved
402 *
403 * @access public
404 * @param string the table name
405 * @return object
406 */
407 function _field_data($table)
408 {
409 return "SELECT TOP 1 FROM ".$table;
410 }
411
412 // --------------------------------------------------------------------
413
414 /**
415 * The error message string
416 *
417 * @access private
418 * @return string
419 */
420 function _error_message()
421 {
Timothy Warrenab347582011-08-23 12:29:29 -0400422 $error_array = $this->conn_id->errorInfo();
423 return $error_array[2];
Timothy Warren80ab8162011-08-22 18:26:12 -0400424 }
425
426 // --------------------------------------------------------------------
427
428 /**
429 * The error message number
430 *
431 * @access private
432 * @return integer
433 */
434 function _error_number()
435 {
Timothy Warrenab347582011-08-23 12:29:29 -0400436 return $this->conn_id->errorCode();
Timothy Warren80ab8162011-08-22 18:26:12 -0400437 }
438
439 // --------------------------------------------------------------------
440
441 /**
442 * Escape the SQL Identifiers
443 *
444 * This function escapes column and table names
445 *
446 * @access private
447 * @param string
448 * @return string
449 */
450 function _escape_identifiers($item)
451 {
452 if ($this->_escape_char == '')
453 {
454 return $item;
455 }
456
457 foreach ($this->_reserved_identifiers as $id)
458 {
459 if (strpos($item, '.'.$id) !== FALSE)
460 {
461 $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
462
463 // remove duplicates if the user already included the escape
464 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
465 }
466 }
467
468 if (strpos($item, '.') !== FALSE)
469 {
470 $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
Timothy Warren018af7a2011-09-07 12:07:35 -0400471
Timothy Warren80ab8162011-08-22 18:26:12 -0400472 }
473 else
474 {
475 $str = $this->_escape_char.$item.$this->_escape_char;
476 }
477
478 // remove duplicates if the user already included the escape
479 return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * From Tables
486 *
487 * This function implicitly groups FROM tables so there is no confusion
488 * about operator precedence in harmony with SQL standards
489 *
490 * @access public
491 * @param type
492 * @return type
493 */
494 function _from_tables($tables)
495 {
496 if ( ! is_array($tables))
497 {
498 $tables = array($tables);
499 }
500
Timothy Warrenab347582011-08-23 12:29:29 -0400501 return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
Timothy Warren80ab8162011-08-22 18:26:12 -0400502 }
503
504 // --------------------------------------------------------------------
505
506 /**
507 * Insert statement
508 *
509 * Generates a platform-specific insert string from the supplied data
510 *
511 * @access public
512 * @param string the table name
513 * @param array the insert keys
514 * @param array the insert values
515 * @return string
516 */
517 function _insert($table, $keys, $values)
518 {
519 return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
520 }
521
522 // --------------------------------------------------------------------
523
524 /**
525 * Update statement
526 *
527 * Generates a platform-specific update string from the supplied data
528 *
529 * @access public
530 * @param string the table name
531 * @param array the update data
532 * @param array the where clause
533 * @param array the orderby clause
534 * @param array the limit clause
535 * @return string
536 */
537 function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
538 {
539 foreach ($values as $key => $val)
540 {
541 $valstr[] = $key." = ".$val;
542 }
543
544 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
545
546 $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
547
548 $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
549
550 $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
551
552 $sql .= $orderby.$limit;
553
554 return $sql;
555 }
556
557
558 // --------------------------------------------------------------------
559
560 /**
561 * Truncate statement
562 *
563 * Generates a platform-specific truncate string from the supplied data
564 * If the database does not support the truncate() command
565 * This function maps to "DELETE FROM table"
566 *
567 * @access public
568 * @param string the table name
569 * @return string
570 */
571 function _truncate($table)
572 {
573 return $this->_delete($table);
574 }
575
576 // --------------------------------------------------------------------
577
578 /**
579 * Delete statement
580 *
581 * Generates a platform-specific delete string from the supplied data
582 *
583 * @access public
584 * @param string the table name
585 * @param array the where clause
586 * @param string the limit clause
587 * @return string
588 */
589 function _delete($table, $where = array(), $like = array(), $limit = FALSE)
590 {
591 $conditions = '';
592
593 if (count($where) > 0 OR count($like) > 0)
594 {
595 $conditions = "\nWHERE ";
596 $conditions .= implode("\n", $this->ar_where);
597
598 if (count($where) > 0 && count($like) > 0)
599 {
600 $conditions .= " AND ";
601 }
602 $conditions .= implode("\n", $like);
603 }
604
605 $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
606
607 return "DELETE FROM ".$table.$conditions.$limit;
608 }
609
610 // --------------------------------------------------------------------
611
612 /**
613 * Limit string
614 *
615 * Generates a platform-specific LIMIT clause
616 *
617 * @access public
618 * @param string the sql query string
619 * @param integer the number of rows to limit the query to
620 * @param integer the offset value
621 * @return string
622 */
623 function _limit($sql, $limit, $offset)
624 {
625 // Does PDO doesn't use the LIMIT clause?
626 return $sql;
627 }
628
629 // --------------------------------------------------------------------
630
631 /**
632 * Close DB Connection
633 *
634 * @access public
635 * @param resource
636 * @return void
637 */
638 function _close($conn_id)
639 {
Timothy Warren6a450cf2011-08-23 12:46:11 -0400640 $this->conn_id = null;
Timothy Warren80ab8162011-08-22 18:26:12 -0400641 }
642
643
644}
645
646
647
648/* End of file pdo_driver.php */
649/* Location: ./system/database/drivers/pdo/pdo_driver.php */