blob: cc77e95681081f8286ac5f62e68004ebc8175649 [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Timothy Warren80ab8162011-08-22 18:26:12 -04002/**
3 * CodeIgniter
4 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02005 * An open source application development framework for PHP
Timothy Warren80ab8162011-08-22 18:26:12 -04006 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Andrey Andreevbd44d5a2012-03-20 22:59:29 +02008 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02009 * Copyright (c) 2014 - 2015, British Columbia Institute of Technology
Andrey Andreevbd44d5a2012-03-20 22:59:29 +020010 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020011 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
Timothy Warrend1a5ba22011-10-21 04:45:28 -040017 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020018 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 * @package CodeIgniter
30 * @author EllisLab Dev Team
darwinel871754a2014-02-11 17:34:57 +010031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
Andrey Andreevfe9309d2015-01-09 17:48:58 +020032 * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
34 * @link http://codeigniter.com
35 * @since Version 2.1.0
Timothy Warren80ab8162011-08-22 18:26:12 -040036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Timothy Warren80ab8162011-08-22 18:26:12 -040039
Timothy Warren80ab8162011-08-22 18:26:12 -040040/**
Timothy Warren02615962011-08-24 08:21:36 -040041 * PDO Database Adapter Class
Timothy Warren80ab8162011-08-22 18:26:12 -040042 *
43 * Note: _DB is an extender class that the app controller
Jamie Rumbelow7efad202012-02-19 12:37:00 +000044 * creates dynamically based on whether the query builder
Timothy Warren80ab8162011-08-22 18:26:12 -040045 * class is being used or not.
46 *
47 * @package CodeIgniter
48 * @subpackage Drivers
49 * @category Database
Timothy Warrend1a5ba22011-10-21 04:45:28 -040050 * @author EllisLab Dev Team
Timothy Warren80ab8162011-08-22 18:26:12 -040051 * @link http://codeigniter.com/user_guide/database/
52 */
53class CI_DB_pdo_driver extends CI_DB {
54
Andrey Andreeva24e52e2012-11-02 03:54:12 +020055 /**
56 * Database driver
57 *
58 * @var string
59 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +020060 public $dbdriver = 'pdo';
Timothy Warren80ab8162011-08-22 18:26:12 -040061
Andrey Andreeva24e52e2012-11-02 03:54:12 +020062 /**
Andrey Andreeva24e52e2012-11-02 03:54:12 +020063 * PDO Options
64 *
65 * @var array
66 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +020067 public $options = array();
Timothy Warren80ab8162011-08-22 18:26:12 -040068
Andrey Andreeva24e52e2012-11-02 03:54:12 +020069 // --------------------------------------------------------------------
70
Andrey Andreev50293052012-06-25 17:48:49 +030071 /**
Andrey Andreeva24e52e2012-11-02 03:54:12 +020072 * Class constructor
Andrey Andreev50293052012-06-25 17:48:49 +030073 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +020074 * Validates the DSN string and/or detects the subdriver.
Andrey Andreev50293052012-06-25 17:48:49 +030075 *
Andrey Andreev5fd3ae82012-10-24 14:55:35 +030076 * @param array $params
Andrey Andreev50293052012-06-25 17:48:49 +030077 * @return void
78 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +020079 public function __construct($params)
Timothy Warren80ab8162011-08-22 18:26:12 -040080 {
Timothy Warrenb5a43b02011-10-04 17:26:04 -040081 parent::__construct($params);
Taufan Aditya18209332012-02-09 16:07:27 +070082
Andrey Andreev42113442013-07-29 09:55:29 +030083 if (preg_match('/([^:]+):/', $this->dsn, $match) && count($match) === 2)
Taufan Aditya18209332012-02-09 16:07:27 +070084 {
85 // If there is a minimum valid dsn string pattern found, we're done
Taufan Adityafdd6ad02012-02-10 13:10:27 +070086 // This is for general PDO users, who tend to have a full DSN string.
Andrey Andreev7151e802012-06-25 00:47:41 +030087 $this->subdriver = $match[1];
Andrey Andreev50293052012-06-25 17:48:49 +030088 return;
Taufan Aditya18209332012-02-09 16:07:27 +070089 }
Andrey Andreev50293052012-06-25 17:48:49 +030090 // Legacy support for DSN specified in the hostname field
Andrey Andreev42113442013-07-29 09:55:29 +030091 elseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2)
Taufan Aditya18209332012-02-09 16:07:27 +070092 {
Andrey Andreev50293052012-06-25 17:48:49 +030093 $this->dsn = $this->hostname;
94 $this->hostname = NULL;
95 $this->subdriver = $match[1];
96 return;
Taufan Aditya18209332012-02-09 16:07:27 +070097 }
Andrey Andreev50293052012-06-25 17:48:49 +030098 elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))
Andrey Andreev7151e802012-06-25 00:47:41 +030099 {
100 $this->subdriver = 'dblib';
101 }
Andrey Andreev6b4bffa2012-06-25 02:29:20 +0300102 elseif ($this->subdriver === '4D')
103 {
104 $this->subdriver = '4d';
105 }
Andrey Andreev279256f2014-01-30 13:50:00 +0200106 elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'pgsql', 'sqlite', 'sqlsrv'), TRUE))
Timothy Warrenc7ba6642011-09-14 12:25:14 -0400107 {
Andrey Andreev50293052012-06-25 17:48:49 +0300108 log_message('error', 'PDO: Invalid or non-existent subdriver');
Timothy Warren80ab8162011-08-22 18:26:12 -0400109
Andrey Andreev50293052012-06-25 17:48:49 +0300110 if ($this->db_debug)
Timothy Warren25dc7552012-02-13 14:12:35 -0500111 {
Andrey Andreev50293052012-06-25 17:48:49 +0300112 show_error('Invalid or non-existent PDO subdriver');
Taufan Aditya18209332012-02-09 16:07:27 +0700113 }
Andrey Andreevd42cc462012-06-24 23:52:40 +0300114 }
Andrey Andreev50293052012-06-25 17:48:49 +0300115
116 $this->dsn = NULL;
Taufan Aditya18209332012-02-09 16:07:27 +0700117 }
118
Andrey Andreev2387ed32012-04-07 00:11:14 +0300119 // --------------------------------------------------------------------
120
Taufan Aditya18209332012-02-09 16:07:27 +0700121 /**
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200122 * Database connection
Timothy Warren80ab8162011-08-22 18:26:12 -0400123 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200124 * @param bool $persistent
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200125 * @return object
Taufan Aditya18209332012-02-09 16:07:27 +0700126 */
Andrey Andreev3b0130d2012-06-24 21:41:42 +0300127 public function db_connect($persistent = FALSE)
Taufan Aditya18209332012-02-09 16:07:27 +0700128 {
Andrey Andreev3b0130d2012-06-24 21:41:42 +0300129 $this->options[PDO::ATTR_PERSISTENT] = $persistent;
Andrey Andreev2387ed32012-04-07 00:11:14 +0300130
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200131 try
Taufan Aditya18209332012-02-09 16:07:27 +0700132 {
Andrey Andreevf2818bd2014-02-25 15:26:20 +0200133 return new PDO($this->dsn, $this->username, $this->password, $this->options);
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200134 }
135 catch (PDOException $e)
Taufan Aditya18209332012-02-09 16:07:27 +0700136 {
137 if ($this->db_debug && empty($this->failover))
138 {
139 $this->display_error($e->getMessage(), '', TRUE);
140 }
141
142 return FALSE;
143 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400144 }
145
146 // --------------------------------------------------------------------
147
148 /**
Andrey Andreev08856b82012-03-03 03:19:28 +0200149 * Database version number
Timothy Warren80ab8162011-08-22 18:26:12 -0400150 *
Timothy Warren80ab8162011-08-22 18:26:12 -0400151 * @return string
152 */
Andrey Andreev08856b82012-03-03 03:19:28 +0200153 public function version()
Timothy Warren80ab8162011-08-22 18:26:12 -0400154 {
Andrey Andreev9e3a83a2012-07-05 21:57:41 +0300155 if (isset($this->data_cache['version']))
156 {
157 return $this->data_cache['version'];
158 }
159
160 // Not all subdrivers support the getAttribute() method
161 try
162 {
163 return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
164 }
165 catch (PDOException $e)
166 {
167 return parent::version();
168 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400169 }
170
171 // --------------------------------------------------------------------
172
173 /**
174 * Execute the query
175 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200176 * @param string $sql SQL query
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200177 * @return mixed
Timothy Warren80ab8162011-08-22 18:26:12 -0400178 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200179 protected function _execute($sql)
Timothy Warren80ab8162011-08-22 18:26:12 -0400180 {
Andrey Andreev2387ed32012-04-07 00:11:14 +0300181 return $this->conn_id->query($sql);
Timothy Warren80ab8162011-08-22 18:26:12 -0400182 }
183
184 // --------------------------------------------------------------------
185
186 /**
Timothy Warren80ab8162011-08-22 18:26:12 -0400187 * Begin Transaction
188 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200189 * @param bool $test_mode
Timothy Warren80ab8162011-08-22 18:26:12 -0400190 * @return bool
191 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200192 public function trans_begin($test_mode = FALSE)
Timothy Warren80ab8162011-08-22 18:26:12 -0400193 {
Timothy Warren80ab8162011-08-22 18:26:12 -0400194 // When transactions are nested we only begin/commit/rollback the outermost ones
Andrey Andreev80144bf2012-04-06 22:19:26 +0300195 if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
Timothy Warren80ab8162011-08-22 18:26:12 -0400196 {
197 return TRUE;
198 }
199
200 // Reset the transaction failure flag.
201 // If the $test_mode flag is set to TRUE transactions will be rolled back
202 // even if the queries produce a successful result.
Andrey Andreev2387ed32012-04-07 00:11:14 +0300203 $this->_trans_failure = ($test_mode === TRUE);
Timothy Warren80ab8162011-08-22 18:26:12 -0400204
Timothy Warrenab347582011-08-23 12:29:29 -0400205 return $this->conn_id->beginTransaction();
Timothy Warren80ab8162011-08-22 18:26:12 -0400206 }
207
208 // --------------------------------------------------------------------
209
210 /**
211 * Commit Transaction
212 *
Timothy Warren80ab8162011-08-22 18:26:12 -0400213 * @return bool
214 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200215 public function trans_commit()
Timothy Warren80ab8162011-08-22 18:26:12 -0400216 {
Timothy Warren80ab8162011-08-22 18:26:12 -0400217 // When transactions are nested we only begin/commit/rollback the outermost ones
Andrey Andreev80144bf2012-04-06 22:19:26 +0300218 if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
Timothy Warren80ab8162011-08-22 18:26:12 -0400219 {
220 return TRUE;
221 }
222
Andrey Andreev80144bf2012-04-06 22:19:26 +0300223 return $this->conn_id->commit();
Timothy Warren80ab8162011-08-22 18:26:12 -0400224 }
225
226 // --------------------------------------------------------------------
227
228 /**
229 * Rollback Transaction
230 *
Timothy Warren80ab8162011-08-22 18:26:12 -0400231 * @return bool
232 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200233 public function trans_rollback()
Timothy Warren80ab8162011-08-22 18:26:12 -0400234 {
Timothy Warren80ab8162011-08-22 18:26:12 -0400235 // When transactions are nested we only begin/commit/rollback the outermost ones
Andrey Andreev80144bf2012-04-06 22:19:26 +0300236 if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
Timothy Warren80ab8162011-08-22 18:26:12 -0400237 {
238 return TRUE;
239 }
240
Andrey Andreev80144bf2012-04-06 22:19:26 +0300241 return $this->conn_id->rollBack();
Timothy Warren80ab8162011-08-22 18:26:12 -0400242 }
243
244 // --------------------------------------------------------------------
245
246 /**
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200247 * Platform-dependant string escape
Timothy Warren80ab8162011-08-22 18:26:12 -0400248 *
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200249 * @param string
Timothy Warren80ab8162011-08-22 18:26:12 -0400250 * @return string
251 */
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200252 protected function _escape_str($str)
Timothy Warren80ab8162011-08-22 18:26:12 -0400253 {
Andrey Andreev2387ed32012-04-07 00:11:14 +0300254 // Escape the string
Timothy Warren47663972011-10-05 16:44:50 -0400255 $str = $this->conn_id->quote($str);
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200256
Andrey Andreev2387ed32012-04-07 00:11:14 +0300257 // If there are duplicated quotes, trim them away
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200258 return ($str[0] === "'")
259 ? substr($str, 1, -1)
260 : $str;
Timothy Warren80ab8162011-08-22 18:26:12 -0400261 }
262
263 // --------------------------------------------------------------------
264
265 /**
266 * Affected Rows
267 *
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200268 * @return int
Timothy Warren80ab8162011-08-22 18:26:12 -0400269 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200270 public function affected_rows()
Timothy Warren80ab8162011-08-22 18:26:12 -0400271 {
Andrey Andreev2387ed32012-04-07 00:11:14 +0300272 return is_object($this->result_id) ? $this->result_id->rowCount() : 0;
Timothy Warren80ab8162011-08-22 18:26:12 -0400273 }
274
275 // --------------------------------------------------------------------
276
277 /**
278 * Insert ID
Andrey Andreeva39d6992012-03-01 19:11:39 +0200279 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200280 * @param string $name
Andrey Andreeva39d6992012-03-01 19:11:39 +0200281 * @return int
Timothy Warren80ab8162011-08-22 18:26:12 -0400282 */
Andrey Andreeva39d6992012-03-01 19:11:39 +0200283 public function insert_id($name = NULL)
Timothy Warren80ab8162011-08-22 18:26:12 -0400284 {
Andrey Andreeva39d6992012-03-01 19:11:39 +0200285 return $this->conn_id->lastInsertId($name);
Timothy Warren80ab8162011-08-22 18:26:12 -0400286 }
287
288 // --------------------------------------------------------------------
289
290 /**
Timothy Warren80ab8162011-08-22 18:26:12 -0400291 * Field data query
292 *
293 * Generates a platform-specific query so that the column data can be retrieved
294 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200295 * @param string $table
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200296 * @return string
Timothy Warren80ab8162011-08-22 18:26:12 -0400297 */
Andrey Andreevbd44d5a2012-03-20 22:59:29 +0200298 protected function _field_data($table)
Timothy Warren80ab8162011-08-22 18:26:12 -0400299 {
Andrey Andreev50293052012-06-25 17:48:49 +0300300 return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
Timothy Warren80ab8162011-08-22 18:26:12 -0400301 }
302
303 // --------------------------------------------------------------------
304
305 /**
Andrey Andreev4be5de12012-03-02 15:45:41 +0200306 * Error
Timothy Warren80ab8162011-08-22 18:26:12 -0400307 *
Andrey Andreev4be5de12012-03-02 15:45:41 +0200308 * Returns an array containing code and message of the last
309 * database error that has occured.
Timothy Warren80ab8162011-08-22 18:26:12 -0400310 *
Andrey Andreev4be5de12012-03-02 15:45:41 +0200311 * @return array
Timothy Warren80ab8162011-08-22 18:26:12 -0400312 */
Andrey Andreev4be5de12012-03-02 15:45:41 +0200313 public function error()
Timothy Warren80ab8162011-08-22 18:26:12 -0400314 {
Andrey Andreev4be5de12012-03-02 15:45:41 +0200315 $error = array('code' => '00000', 'message' => '');
316 $pdo_error = $this->conn_id->errorInfo();
317
318 if (empty($pdo_error[0]))
319 {
320 return $error;
321 }
322
323 $error['code'] = isset($pdo_error[1]) ? $pdo_error[0].'/'.$pdo_error[1] : $pdo_error[0];
324 if (isset($pdo_error[2]))
325 {
326 $error['message'] = $pdo_error[2];
327 }
328
329 return $error;
Timothy Warren80ab8162011-08-22 18:26:12 -0400330 }
331
332 // --------------------------------------------------------------------
333
334 /**
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400335 * Update_Batch statement
336 *
337 * Generates a platform-specific batch update string from the supplied data
338 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200339 * @param string $table Table name
340 * @param array $values Update data
341 * @param string $index WHERE key
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400342 * @return string
343 */
Andrey Andreevb0478652012-07-18 15:34:46 +0300344 protected function _update_batch($table, $values, $index)
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400345 {
Andrey Andreevdf8894f2012-06-25 16:18:50 +0300346 $ids = array();
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400347 foreach ($values as $key => $val)
348 {
349 $ids[] = $val[$index];
350
351 foreach (array_keys($val) as $field)
352 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100353 if ($field !== $index)
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400354 {
Andrey Andreev838a9d62012-12-03 14:37:47 +0200355 $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400356 }
357 }
358 }
359
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400360 $cases = '';
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400361 foreach ($final as $k => $v)
362 {
363 $cases .= $k.' = CASE '."\n";
Taufan Aditya18209332012-02-09 16:07:27 +0700364
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400365 foreach ($v as $row)
366 {
367 $cases .= $row."\n";
368 }
369
370 $cases .= 'ELSE '.$k.' END, ';
371 }
372
Andrey Andreevb0478652012-07-18 15:34:46 +0300373 $this->where($index.' IN('.implode(',', $ids).')', NULL, FALSE);
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400374
Andrey Andreevd40459d2012-07-18 16:46:39 +0300375 return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
Timothy Warrenb5a43b02011-10-04 17:26:04 -0400376 }
Timothy Warren80ab8162011-08-22 18:26:12 -0400377
Andrey Andreeve61b19f2012-06-25 18:11:39 +0300378 // --------------------------------------------------------------------
379
380 /**
381 * Truncate statement
382 *
383 * Generates a platform-specific truncate string from the supplied data
384 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200385 * If the database does not support the TRUNCATE statement,
Andrey Andreeve61b19f2012-06-25 18:11:39 +0300386 * then this method maps to 'DELETE FROM table'
387 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200388 * @param string $table
Andrey Andreeve61b19f2012-06-25 18:11:39 +0300389 * @return string
390 */
391 protected function _truncate($table)
392 {
393 return 'TRUNCATE TABLE '.$table;
394 }
395
Timothy Warren80ab8162011-08-22 18:26:12 -0400396}