blob: c85390352e4efd9d31da8e49d459e3b7a10abfad [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02005 * An open source application development framework for PHP
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Andrey Andreevb76029d2012-01-26 15:13:19 +02008 *
Andrey Andreevcce6bd12018-01-09 11:32:02 +02009 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
Andrey Andreevb76029d2012-01-26 15:13:19 +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:
Derek Jonesf4a4bd82011-10-20 12:18:42 -050017 *
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
Andrey Andreev1924e872016-01-11 12:55:34 +020031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
Andrey Andreevcce6bd12018-01-09 11:32:02 +020032 * @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
Andrey Andreevbd202c92016-01-11 12:50:18 +020034 * @link https://codeigniter.com
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020035 * @since Version 1.3.0
Derek Allard2067d1a2008-11-13 22:59:24 +000036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +000039
Derek Allard2067d1a2008-11-13 22:59:24 +000040/**
41 * ODBC Database Adapter Class
42 *
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
Derek Allard2067d1a2008-11-13 22:59:24 +000045 * class is being used or not.
46 *
47 * @package CodeIgniter
48 * @subpackage Drivers
49 * @category Database
Derek Jonesf4a4bd82011-10-20 12:18:42 -050050 * @author EllisLab Dev Team
Andrey Andreevbd202c92016-01-11 12:50:18 +020051 * @link https://codeigniter.com/user_guide/database/
Derek Allard2067d1a2008-11-13 22:59:24 +000052 */
Andrey Andreev3d10ffa2016-07-26 19:42:05 +030053class CI_DB_odbc_driver extends CI_DB_driver {
Derek Allard2067d1a2008-11-13 22:59:24 +000054
Andrey Andreeva24e52e2012-11-02 03:54:12 +020055 /**
56 * Database driver
57 *
58 * @var string
59 */
Andrey Andreevb76029d2012-01-26 15:13:19 +020060 public $dbdriver = 'odbc';
Barry Mienydd671972010-10-04 16:33:58 +020061
Andrey Andreev5fd3ae82012-10-24 14:55:35 +030062 /**
Andrey Andreev3e9d2b82012-10-27 14:28:51 +030063 * Database schema
64 *
65 * @var string
Andrey Andreev485a3482012-10-27 03:22:43 +030066 */
67 public $schema = 'public';
68
Andrey Andreeva24e52e2012-11-02 03:54:12 +020069 // --------------------------------------------------------------------
70
Andrey Andreev485a3482012-10-27 03:22:43 +030071 /**
Andrey Andreeva24e52e2012-11-02 03:54:12 +020072 * Identifier escape character
73 *
74 * Must be empty for ODBC.
75 *
76 * @var string
77 */
78 protected $_escape_char = '';
79
80 /**
81 * ESCAPE statement string
82 *
83 * @var string
84 */
85 protected $_like_escape_str = " {escape '%s'} ";
86
Andrey Andreev98e46cf2012-11-13 03:01:42 +020087 /**
88 * ORDER BY random keyword
89 *
90 * @var array
91 */
92 protected $_random_keyword = array('RND()', 'RND(%d)');
93
Andrey Andreeva24e52e2012-11-02 03:54:12 +020094 // --------------------------------------------------------------------
95
96 /**
Andrey Andreev3d10ffa2016-07-26 19:42:05 +030097 * ODBC result ID resource returned from odbc_prepare()
98 *
99 * @var resource
100 */
101 private $odbc_result;
102
103 /**
104 * Values to use with odbc_execute() for prepared statements
105 *
106 * @var array
107 */
108 private $binds = array();
109
110 // --------------------------------------------------------------------
111
112 /**
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200113 * Class constructor
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300114 *
115 * @param array $params
116 * @return void
117 */
Andrey Andreev5f356bf2012-03-20 14:32:27 +0200118 public function __construct($params)
Derek Allard2067d1a2008-11-13 22:59:24 +0000119 {
Timothy Warrena2097a02011-10-10 10:10:46 -0400120 parent::__construct($params);
Barry Mienydd671972010-10-04 16:33:58 +0200121
Andrey Andreev46f6dbc2012-02-13 01:39:53 +0200122 // Legacy support for DSN in the hostname field
Andrey Andreeve4c30192012-06-04 15:08:24 +0300123 if (empty($this->dsn))
Andrey Andreev46f6dbc2012-02-13 01:39:53 +0200124 {
125 $this->dsn = $this->hostname;
126 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000127 }
128
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300129 // --------------------------------------------------------------------
130
Derek Allard2067d1a2008-11-13 22:59:24 +0000131 /**
132 * Non-persistent database connection
133 *
Andrey Andreev2e171022014-02-25 15:21:41 +0200134 * @param bool $persistent
Derek Allard2067d1a2008-11-13 22:59:24 +0000135 * @return resource
Barry Mienydd671972010-10-04 16:33:58 +0200136 */
Andrey Andreev2e171022014-02-25 15:21:41 +0200137 public function db_connect($persistent = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 {
Andrey Andreev2e171022014-02-25 15:21:41 +0200139 return ($persistent === TRUE)
140 ? odbc_pconnect($this->dsn, $this->username, $this->password)
141 : odbc_connect($this->dsn, $this->username, $this->password);
Derek Allard2067d1a2008-11-13 22:59:24 +0000142 }
Barry Mienydd671972010-10-04 16:33:58 +0200143
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 // --------------------------------------------------------------------
145
146 /**
Andrey Andreev3d10ffa2016-07-26 19:42:05 +0300147 * Compile Bindings
148 *
149 * @param string $sql SQL statement
150 * @param array $binds An array of values to bind
151 * @return string
152 */
153 public function compile_binds($sql, $binds)
154 {
155 if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
156 {
157 return $sql;
158 }
159 elseif ( ! is_array($binds))
160 {
161 $binds = array($binds);
162 $bind_count = 1;
163 }
164 else
165 {
166 // Make sure we're using numeric keys
167 $binds = array_values($binds);
168 $bind_count = count($binds);
169 }
170
171 // We'll need the marker length later
172 $ml = strlen($this->bind_marker);
173
174 // Make sure not to replace a chunk inside a string that happens to match the bind marker
Andrey Andreev6b5464c2016-10-31 13:09:33 +0200175 if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches))
Andrey Andreev3d10ffa2016-07-26 19:42:05 +0300176 {
177 $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
178 str_replace($matches[0],
179 str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
180 $sql, $c),
181 $matches, PREG_OFFSET_CAPTURE);
182
183 // Bind values' count must match the count of markers in the query
184 if ($bind_count !== $c)
185 {
186 return $sql;
187 }
188 }
189 elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
190 {
191 return $sql;
192 }
193
194 if ($this->bind_marker !== '?')
195 {
196 do
197 {
198 $c--;
199 $sql = substr_replace($sql, '?', $matches[0][$c][1], $ml);
200 }
201 while ($c !== 0);
202 }
203
204 if (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql)))
205 {
206 $this->binds = array_values($binds);
207 }
208
209 return $sql;
210 }
211
212 // --------------------------------------------------------------------
213
214 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000215 * Execute the query
216 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200217 * @param string $sql an SQL query
Derek Allard2067d1a2008-11-13 22:59:24 +0000218 * @return resource
Barry Mienydd671972010-10-04 16:33:58 +0200219 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200220 protected function _execute($sql)
Derek Allard2067d1a2008-11-13 22:59:24 +0000221 {
Andrey Andreev3d10ffa2016-07-26 19:42:05 +0300222 if ( ! isset($this->odbc_result))
223 {
224 return odbc_exec($this->conn_id, $sql);
225 }
226 elseif ($this->odbc_result === FALSE)
227 {
228 return FALSE;
229 }
230
231 if (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds)))
232 {
233 // For queries that return result sets, return the result_id resource on success
234 $this->is_write_type($sql) OR $success = $this->odbc_result;
235 }
236
237 $this->odbc_result = NULL;
238 $this->binds = array();
239
240 return $success;
Derek Allard2067d1a2008-11-13 22:59:24 +0000241 }
242
243 // --------------------------------------------------------------------
244
245 /**
246 * Begin Transaction
247 *
Barry Mienydd671972010-10-04 16:33:58 +0200248 * @return bool
249 */
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300250 protected function _trans_begin()
Derek Allard2067d1a2008-11-13 22:59:24 +0000251 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000252 return odbc_autocommit($this->conn_id, FALSE);
253 }
254
255 // --------------------------------------------------------------------
256
257 /**
258 * Commit Transaction
259 *
Barry Mienydd671972010-10-04 16:33:58 +0200260 * @return bool
261 */
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300262 protected function _trans_commit()
Derek Allard2067d1a2008-11-13 22:59:24 +0000263 {
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300264 if (odbc_commit($this->conn_id))
Derek Allard2067d1a2008-11-13 22:59:24 +0000265 {
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300266 odbc_autocommit($this->conn_id, TRUE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000267 return TRUE;
268 }
269
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300270 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000271 }
272
273 // --------------------------------------------------------------------
274
275 /**
276 * Rollback Transaction
277 *
Barry Mienydd671972010-10-04 16:33:58 +0200278 * @return bool
279 */
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300280 protected function _trans_rollback()
Derek Allard2067d1a2008-11-13 22:59:24 +0000281 {
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300282 if (odbc_rollback($this->conn_id))
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 {
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300284 odbc_autocommit($this->conn_id, TRUE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000285 return TRUE;
286 }
287
Andrey Andreeva7d4aba2015-10-19 14:39:44 +0300288 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000289 }
290
291 // --------------------------------------------------------------------
292
293 /**
Andrey Andreev0ca9ae62016-01-06 14:51:27 +0200294 * Determines if a query is a "write" type.
295 *
296 * @param string An SQL query string
297 * @return bool
298 */
299 public function is_write_type($sql)
300 {
Andrey Andreev442ea682016-09-16 11:51:25 +0300301 if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
Andrey Andreev0ca9ae62016-01-06 14:51:27 +0200302 {
303 return FALSE;
304 }
305
306 return parent::is_write_type($sql);
307 }
308
309 // --------------------------------------------------------------------
310
311 /**
Andrey Andreev71d8f722017-01-17 12:01:00 +0200312 * Platform-dependent string escape
Derek Allard2067d1a2008-11-13 22:59:24 +0000313 *
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200314 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000315 * @return string
316 */
Andrey Andreev0b6a4922013-01-10 16:53:44 +0200317 protected function _escape_str($str)
Derek Allard2067d1a2008-11-13 22:59:24 +0000318 {
Andrey Andreevea073522017-03-14 18:42:12 +0200319 $this->display_error('db_unsupported_feature');
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 }
Barry Mienydd671972010-10-04 16:33:58 +0200321
Derek Allard2067d1a2008-11-13 22:59:24 +0000322 // --------------------------------------------------------------------
323
324 /**
325 * Affected Rows
326 *
Andrey Andreevb76029d2012-01-26 15:13:19 +0200327 * @return int
Derek Allard2067d1a2008-11-13 22:59:24 +0000328 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200329 public function affected_rows()
Derek Allard2067d1a2008-11-13 22:59:24 +0000330 {
Andrey Andreev2bbbd1a2014-05-09 10:24:14 +0300331 return odbc_num_rows($this->result_id);
Derek Allard2067d1a2008-11-13 22:59:24 +0000332 }
Barry Mienydd671972010-10-04 16:33:58 +0200333
Derek Allard2067d1a2008-11-13 22:59:24 +0000334 // --------------------------------------------------------------------
335
336 /**
337 * Insert ID
338 *
Andrey Andreev8af76662012-03-05 14:33:41 +0200339 * @return bool
Derek Allard2067d1a2008-11-13 22:59:24 +0000340 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200341 public function insert_id()
Derek Allard2067d1a2008-11-13 22:59:24 +0000342 {
Andrey Andreevea073522017-03-14 18:42:12 +0200343 return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 }
345
346 // --------------------------------------------------------------------
347
348 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000349 * Show table query
350 *
351 * Generates a platform-specific query string so that the table names can be fetched
352 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200353 * @param bool $prefix_limit
Derek Allard2067d1a2008-11-13 22:59:24 +0000354 * @return string
355 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200356 protected function _list_tables($prefix_limit = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000357 {
Andrey Andreev485a3482012-10-27 03:22:43 +0300358 $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'";
Derek Allard2067d1a2008-11-13 22:59:24 +0000359
Andrey Andreeve4c30192012-06-04 15:08:24 +0300360 if ($prefix_limit !== FALSE && $this->dbprefix !== '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000361 {
Andrey Andreev485a3482012-10-27 03:22:43 +0300362 return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
363 .sprintf($this->_like_escape_str, $this->_like_escape_chr);
Derek Allard2067d1a2008-11-13 22:59:24 +0000364 }
Barry Mienydd671972010-10-04 16:33:58 +0200365
Derek Allard2067d1a2008-11-13 22:59:24 +0000366 return $sql;
367 }
Barry Mienydd671972010-10-04 16:33:58 +0200368
Derek Allard2067d1a2008-11-13 22:59:24 +0000369 // --------------------------------------------------------------------
370
371 /**
372 * Show column query
373 *
374 * Generates a platform-specific query string so that the column names can be fetched
375 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200376 * @param string $table
Derek Allard2067d1a2008-11-13 22:59:24 +0000377 * @return string
378 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200379 protected function _list_columns($table = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000380 {
Andrey Andreevb76029d2012-01-26 15:13:19 +0200381 return 'SHOW COLUMNS FROM '.$table;
Derek Allard2067d1a2008-11-13 22:59:24 +0000382 }
383
384 // --------------------------------------------------------------------
385
386 /**
387 * Field data query
388 *
389 * Generates a platform-specific query so that the column data can be retrieved
390 *
Andrey Andreeva24e52e2012-11-02 03:54:12 +0200391 * @param string $table
Andrey Andreevb76029d2012-01-26 15:13:19 +0200392 * @return string
Derek Allard2067d1a2008-11-13 22:59:24 +0000393 */
Andrey Andreevb76029d2012-01-26 15:13:19 +0200394 protected function _field_data($table)
Derek Allard2067d1a2008-11-13 22:59:24 +0000395 {
Andrey Andreevb76029d2012-01-26 15:13:19 +0200396 return 'SELECT TOP 1 FROM '.$table;
Derek Allard2067d1a2008-11-13 22:59:24 +0000397 }
398
399 // --------------------------------------------------------------------
400
401 /**
Andrey Andreev4be5de12012-03-02 15:45:41 +0200402 * Error
Derek Allard2067d1a2008-11-13 22:59:24 +0000403 *
Andrey Andreev4be5de12012-03-02 15:45:41 +0200404 * Returns an array containing code and message of the last
Andrey Andreev71d8f722017-01-17 12:01:00 +0200405 * database error that has occurred.
Derek Allard2067d1a2008-11-13 22:59:24 +0000406 *
Andrey Andreev4be5de12012-03-02 15:45:41 +0200407 * @return array
Derek Allard2067d1a2008-11-13 22:59:24 +0000408 */
Andrey Andreev4be5de12012-03-02 15:45:41 +0200409 public function error()
Derek Allard2067d1a2008-11-13 22:59:24 +0000410 {
Andrey Andreev4be5de12012-03-02 15:45:41 +0200411 return array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id));
Derek Allard2067d1a2008-11-13 22:59:24 +0000412 }
413
414 // --------------------------------------------------------------------
415
416 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000417 * Close DB Connection
418 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 * @return void
420 */
Andrey Andreev79922c02012-05-23 12:27:17 +0300421 protected function _close()
Derek Allard2067d1a2008-11-13 22:59:24 +0000422 {
Andrey Andreev2bbbd1a2014-05-09 10:24:14 +0300423 odbc_close($this->conn_id);
Derek Allard2067d1a2008-11-13 22:59:24 +0000424 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000425}