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.

384 lines
9.4 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 3.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * PDO PostgreSQL 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_pdo_pgsql_driver extends CI_DB_pdo_driver {
  53. /**
  54. * Sub-driver
  55. *
  56. * @var string
  57. */
  58. public $subdriver = 'pgsql';
  59. /**
  60. * Database schema
  61. *
  62. * @var string
  63. */
  64. public $schema = 'public';
  65. // --------------------------------------------------------------------
  66. /**
  67. * ORDER BY random keyword
  68. *
  69. * @var array
  70. */
  71. protected $_random_keyword = array('RANDOM()', 'RANDOM()');
  72. // --------------------------------------------------------------------
  73. /**
  74. * Class constructor
  75. *
  76. * Builds the DSN if not already set.
  77. *
  78. * @param array $params
  79. * @return void
  80. */
  81. public function __construct($params)
  82. {
  83. parent::__construct($params);
  84. if (empty($this->dsn))
  85. {
  86. $this->dsn = 'pgsql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
  87. empty($this->port) OR $this->dsn .= ';port='.$this->port;
  88. empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
  89. if ( ! empty($this->username))
  90. {
  91. $this->dsn .= ';username='.$this->username;
  92. empty($this->password) OR $this->dsn .= ';password='.$this->password;
  93. }
  94. }
  95. }
  96. // --------------------------------------------------------------------
  97. /**
  98. * Database connection
  99. *
  100. * @param bool $persistent
  101. * @return object
  102. */
  103. public function db_connect($persistent = FALSE)
  104. {
  105. $this->conn_id = parent::db_connect($persistent);
  106. if (is_object($this->conn_id) && ! empty($this->schema))
  107. {
  108. $this->simple_query('SET search_path TO '.$this->schema.',public');
  109. }
  110. return $this->conn_id;
  111. }
  112. // --------------------------------------------------------------------
  113. /**
  114. * Insert ID
  115. *
  116. * @param string $name
  117. * @return int
  118. */
  119. public function insert_id($name = NULL)
  120. {
  121. if ($name === NULL && version_compare($this->version(), '8.1', '>='))
  122. {
  123. $query = $this->query('SELECT LASTVAL() AS ins_id');
  124. $query = $query->row();
  125. return $query->ins_id;
  126. }
  127. return $this->conn_id->lastInsertId($name);
  128. }
  129. // --------------------------------------------------------------------
  130. /**
  131. * Determines if a query is a "write" type.
  132. *
  133. * @param string An SQL query string
  134. * @return bool
  135. */
  136. public function is_write_type($sql)
  137. {
  138. if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
  139. {
  140. return FALSE;
  141. }
  142. return parent::is_write_type($sql);
  143. }
  144. // --------------------------------------------------------------------
  145. /**
  146. * "Smart" Escape String
  147. *
  148. * Escapes data based on type
  149. *
  150. * @param string $str
  151. * @return mixed
  152. */
  153. public function escape($str)
  154. {
  155. if (is_bool($str))
  156. {
  157. return ($str) ? 'TRUE' : 'FALSE';
  158. }
  159. return parent::escape($str);
  160. }
  161. // --------------------------------------------------------------------
  162. /**
  163. * ORDER BY
  164. *
  165. * @param string $orderby
  166. * @param string $direction ASC, DESC or RANDOM
  167. * @param bool $escape
  168. * @return object
  169. */
  170. public function order_by($orderby, $direction = '', $escape = NULL)
  171. {
  172. $direction = strtoupper(trim($direction));
  173. if ($direction === 'RANDOM')
  174. {
  175. if ( ! is_float($orderby) && ctype_digit((string) $orderby))
  176. {
  177. $orderby = ($orderby > 1)
  178. ? (float) '0.'.$orderby
  179. : (float) $orderby;
  180. }
  181. if (is_float($orderby))
  182. {
  183. $this->simple_query('SET SEED '.$orderby);
  184. }
  185. $orderby = $this->_random_keyword[0];
  186. $direction = '';
  187. $escape = FALSE;
  188. }
  189. return parent::order_by($orderby, $direction, $escape);
  190. }
  191. // --------------------------------------------------------------------
  192. /**
  193. * Show table query
  194. *
  195. * Generates a platform-specific query string so that the table names can be fetched
  196. *
  197. * @param bool $prefix_limit
  198. * @return string
  199. */
  200. protected function _list_tables($prefix_limit = FALSE)
  201. {
  202. $sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'";
  203. if ($prefix_limit === TRUE && $this->dbprefix !== '')
  204. {
  205. return $sql.' AND "table_name" LIKE \''
  206. .$this->escape_like_str($this->dbprefix)."%' "
  207. .sprintf($this->_like_escape_str, $this->_like_escape_chr);
  208. }
  209. return $sql;
  210. }
  211. // --------------------------------------------------------------------
  212. /**
  213. * List column query
  214. *
  215. * Generates a platform-specific query string so that the column names can be fetched
  216. *
  217. * @param string $table
  218. * @return string
  219. */
  220. protected function _list_columns($table = '')
  221. {
  222. return 'SELECT "column_name"
  223. FROM "information_schema"."columns"
  224. WHERE LOWER("table_name") = '.$this->escape(strtolower($table));
  225. }
  226. // --------------------------------------------------------------------
  227. /**
  228. * Returns an object with field data
  229. *
  230. * @param string $table
  231. * @return array
  232. */
  233. public function field_data($table)
  234. {
  235. $sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
  236. FROM "information_schema"."columns"
  237. WHERE LOWER("table_name") = '.$this->escape(strtolower($table));
  238. if (($query = $this->query($sql)) === FALSE)
  239. {
  240. return FALSE;
  241. }
  242. $query = $query->result_object();
  243. $retval = array();
  244. for ($i = 0, $c = count($query); $i < $c; $i++)
  245. {
  246. $retval[$i] = new stdClass();
  247. $retval[$i]->name = $query[$i]->column_name;
  248. $retval[$i]->type = $query[$i]->data_type;
  249. $retval[$i]->max_length = ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
  250. $retval[$i]->default = $query[$i]->column_default;
  251. }
  252. return $retval;
  253. }
  254. // --------------------------------------------------------------------
  255. /**
  256. * Update statement
  257. *
  258. * Generates a platform-specific update string from the supplied data
  259. *
  260. * @param string $table
  261. * @param array $values
  262. * @return string
  263. */
  264. protected function _update($table, $values)
  265. {
  266. $this->qb_limit = FALSE;
  267. $this->qb_orderby = array();
  268. return parent::_update($table, $values);
  269. }
  270. // --------------------------------------------------------------------
  271. /**
  272. * Update_Batch statement
  273. *
  274. * Generates a platform-specific batch update string from the supplied data
  275. *
  276. * @param string $table Table name
  277. * @param array $values Update data
  278. * @param string $index WHERE key
  279. * @return string
  280. */
  281. protected function _update_batch($table, $values, $index)
  282. {
  283. $ids = array();
  284. foreach ($values as $key => $val)
  285. {
  286. $ids[] = $val[$index]['value'];
  287. foreach (array_keys($val) as $field)
  288. {
  289. if ($field !== $index)
  290. {
  291. $final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];
  292. }
  293. }
  294. }
  295. $cases = '';
  296. foreach ($final as $k => $v)
  297. {
  298. $cases .= $k.' = (CASE '.$val[$index]['field']."\n"
  299. .implode("\n", $v)."\n"
  300. .'ELSE '.$k.' END), ';
  301. }
  302. $this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
  303. return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
  304. }
  305. // --------------------------------------------------------------------
  306. /**
  307. * Delete statement
  308. *
  309. * Generates a platform-specific delete string from the supplied data
  310. *
  311. * @param string $table
  312. * @return string
  313. */
  314. protected function _delete($table)
  315. {
  316. $this->qb_limit = FALSE;
  317. return parent::_delete($table);
  318. }
  319. // --------------------------------------------------------------------
  320. /**
  321. * LIMIT
  322. *
  323. * Generates a platform-specific LIMIT clause
  324. *
  325. * @param string $sql SQL Query
  326. * @return string
  327. */
  328. protected function _limit($sql)
  329. {
  330. return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');
  331. }
  332. }