You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

425 lines
9.6 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * 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:
  17. *
  18. * 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
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.3.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * ODBC Database Adapter Class
  41. *
  42. * Note: _DB is an extender class that the app controller
  43. * creates dynamically based on whether the query builder
  44. * class is being used or not.
  45. *
  46. * @package CodeIgniter
  47. * @subpackage Drivers
  48. * @category Database
  49. * @author EllisLab Dev Team
  50. * @link https://codeigniter.com/user_guide/database/
  51. */
  52. class CI_DB_odbc_driver extends CI_DB_driver {
  53. /**
  54. * Database driver
  55. *
  56. * @var string
  57. */
  58. public $dbdriver = 'odbc';
  59. /**
  60. * Database schema
  61. *
  62. * @var string
  63. */
  64. public $schema = 'public';
  65. // --------------------------------------------------------------------
  66. /**
  67. * Identifier escape character
  68. *
  69. * Must be empty for ODBC.
  70. *
  71. * @var string
  72. */
  73. protected $_escape_char = '';
  74. /**
  75. * ESCAPE statement string
  76. *
  77. * @var string
  78. */
  79. protected $_like_escape_str = " {escape '%s'} ";
  80. /**
  81. * ORDER BY random keyword
  82. *
  83. * @var array
  84. */
  85. protected $_random_keyword = array('RND()', 'RND(%d)');
  86. // --------------------------------------------------------------------
  87. /**
  88. * ODBC result ID resource returned from odbc_prepare()
  89. *
  90. * @var resource
  91. */
  92. private $odbc_result;
  93. /**
  94. * Values to use with odbc_execute() for prepared statements
  95. *
  96. * @var array
  97. */
  98. private $binds = array();
  99. // --------------------------------------------------------------------
  100. /**
  101. * Class constructor
  102. *
  103. * @param array $params
  104. * @return void
  105. */
  106. public function __construct($params)
  107. {
  108. parent::__construct($params);
  109. // Legacy support for DSN in the hostname field
  110. if (empty($this->dsn))
  111. {
  112. $this->dsn = $this->hostname;
  113. }
  114. }
  115. // --------------------------------------------------------------------
  116. /**
  117. * Non-persistent database connection
  118. *
  119. * @param bool $persistent
  120. * @return resource
  121. */
  122. public function db_connect($persistent = FALSE)
  123. {
  124. return ($persistent === TRUE)
  125. ? odbc_pconnect($this->dsn, $this->username, $this->password)
  126. : odbc_connect($this->dsn, $this->username, $this->password);
  127. }
  128. // --------------------------------------------------------------------
  129. /**
  130. * Compile Bindings
  131. *
  132. * @param string $sql SQL statement
  133. * @param array $binds An array of values to bind
  134. * @return string
  135. */
  136. public function compile_binds($sql, $binds)
  137. {
  138. if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
  139. {
  140. return $sql;
  141. }
  142. elseif ( ! is_array($binds))
  143. {
  144. $binds = array($binds);
  145. $bind_count = 1;
  146. }
  147. else
  148. {
  149. // Make sure we're using numeric keys
  150. $binds = array_values($binds);
  151. $bind_count = count($binds);
  152. }
  153. // We'll need the marker length later
  154. $ml = strlen($this->bind_marker);
  155. // Make sure not to replace a chunk inside a string that happens to match the bind marker
  156. if ($c = preg_match_all("/'[^']*'|\"[^\"]*\"/i", $sql, $matches))
  157. {
  158. $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i',
  159. str_replace($matches[0],
  160. str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
  161. $sql, $c),
  162. $matches, PREG_OFFSET_CAPTURE);
  163. // Bind values' count must match the count of markers in the query
  164. if ($bind_count !== $c)
  165. {
  166. return $sql;
  167. }
  168. }
  169. elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
  170. {
  171. return $sql;
  172. }
  173. if ($this->bind_marker !== '?')
  174. {
  175. do
  176. {
  177. $c--;
  178. $sql = substr_replace($sql, '?', $matches[0][$c][1], $ml);
  179. }
  180. while ($c !== 0);
  181. }
  182. if (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql)))
  183. {
  184. $this->binds = array_values($binds);
  185. }
  186. return $sql;
  187. }
  188. // --------------------------------------------------------------------
  189. /**
  190. * Execute the query
  191. *
  192. * @param string $sql an SQL query
  193. * @return resource
  194. */
  195. protected function _execute($sql)
  196. {
  197. if ( ! isset($this->odbc_result))
  198. {
  199. return odbc_exec($this->conn_id, $sql);
  200. }
  201. elseif ($this->odbc_result === FALSE)
  202. {
  203. return FALSE;
  204. }
  205. if (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds)))
  206. {
  207. // For queries that return result sets, return the result_id resource on success
  208. $this->is_write_type($sql) OR $success = $this->odbc_result;
  209. }
  210. $this->odbc_result = NULL;
  211. $this->binds = array();
  212. return $success;
  213. }
  214. // --------------------------------------------------------------------
  215. /**
  216. * Begin Transaction
  217. *
  218. * @return bool
  219. */
  220. protected function _trans_begin()
  221. {
  222. return odbc_autocommit($this->conn_id, FALSE);
  223. }
  224. // --------------------------------------------------------------------
  225. /**
  226. * Commit Transaction
  227. *
  228. * @return bool
  229. */
  230. protected function _trans_commit()
  231. {
  232. if (odbc_commit($this->conn_id))
  233. {
  234. odbc_autocommit($this->conn_id, TRUE);
  235. return TRUE;
  236. }
  237. return FALSE;
  238. }
  239. // --------------------------------------------------------------------
  240. /**
  241. * Rollback Transaction
  242. *
  243. * @return bool
  244. */
  245. protected function _trans_rollback()
  246. {
  247. if (odbc_rollback($this->conn_id))
  248. {
  249. odbc_autocommit($this->conn_id, TRUE);
  250. return TRUE;
  251. }
  252. return FALSE;
  253. }
  254. // --------------------------------------------------------------------
  255. /**
  256. * Determines if a query is a "write" type.
  257. *
  258. * @param string An SQL query string
  259. * @return bool
  260. */
  261. public function is_write_type($sql)
  262. {
  263. if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
  264. {
  265. return FALSE;
  266. }
  267. return parent::is_write_type($sql);
  268. }
  269. // --------------------------------------------------------------------
  270. /**
  271. * Platform-dependent string escape
  272. *
  273. * @param string
  274. * @return string
  275. */
  276. protected function _escape_str($str)
  277. {
  278. $this->display_error('db_unsupported_feature');
  279. }
  280. // --------------------------------------------------------------------
  281. /**
  282. * Affected Rows
  283. *
  284. * @return int
  285. */
  286. public function affected_rows()
  287. {
  288. return odbc_num_rows($this->result_id);
  289. }
  290. // --------------------------------------------------------------------
  291. /**
  292. * Insert ID
  293. *
  294. * @return bool
  295. */
  296. public function insert_id()
  297. {
  298. return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
  299. }
  300. // --------------------------------------------------------------------
  301. /**
  302. * Show table query
  303. *
  304. * Generates a platform-specific query string so that the table names can be fetched
  305. *
  306. * @param bool $prefix_limit
  307. * @return string
  308. */
  309. protected function _list_tables($prefix_limit = FALSE)
  310. {
  311. $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'";
  312. if ($prefix_limit !== FALSE && $this->dbprefix !== '')
  313. {
  314. return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
  315. .sprintf($this->_like_escape_str, $this->_like_escape_chr);
  316. }
  317. return $sql;
  318. }
  319. // --------------------------------------------------------------------
  320. /**
  321. * Show column query
  322. *
  323. * Generates a platform-specific query string so that the column names can be fetched
  324. *
  325. * @param string $table
  326. * @return string
  327. */
  328. protected function _list_columns($table = '')
  329. {
  330. return 'SHOW COLUMNS FROM '.$table;
  331. }
  332. // --------------------------------------------------------------------
  333. /**
  334. * Field data query
  335. *
  336. * Generates a platform-specific query so that the column data can be retrieved
  337. *
  338. * @param string $table
  339. * @return string
  340. */
  341. protected function _field_data($table)
  342. {
  343. return 'SELECT TOP 1 FROM '.$table;
  344. }
  345. // --------------------------------------------------------------------
  346. /**
  347. * Error
  348. *
  349. * Returns an array containing code and message of the last
  350. * database error that has occurred.
  351. *
  352. * @return array
  353. */
  354. public function error()
  355. {
  356. return array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id));
  357. }
  358. // --------------------------------------------------------------------
  359. /**
  360. * Close DB Connection
  361. *
  362. * @return void
  363. */
  364. protected function _close()
  365. {
  366. odbc_close($this->conn_id);
  367. }
  368. }