PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/system/libraries/drivers/Database/Pgsql.php

https://github.com/Toushi/flow
PHP | 539 lines | 374 code | 97 blank | 68 comment | 46 complexity | 29d0a093d64980042339d506401ec07f MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * PostgreSQL 8.1+ Database Driver
  4. *
  5. * $Id: Pgsql.php 3160 2008-07-20 16:03:48Z Shadowhand $
  6. *
  7. * @package Core
  8. * @author Kohana Team
  9. * @copyright (c) 2007-2008 Kohana Team
  10. * @license http://kohanaphp.com/license.html
  11. */
  12. class Database_Pgsql_Driver extends Database_Driver {
  13. // Database connection link
  14. protected $link;
  15. protected $db_config;
  16. /**
  17. * Sets the config for the class.
  18. *
  19. * @param array database configuration
  20. */
  21. public function __construct($config)
  22. {
  23. $this->db_config = $config;
  24. Kohana::log('debug', 'PgSQL Database Driver Initialized');
  25. }
  26. public function connect()
  27. {
  28. // Check if link already exists
  29. if (is_resource($this->link))
  30. return $this->link;
  31. // Import the connect variables
  32. extract($this->db_config['connection']);
  33. // Persistent connections enabled?
  34. $connect = ($this->db_config['persistent'] == TRUE) ? 'pg_pconnect' : 'pg_connect';
  35. // Build the connection info
  36. $port = isset($port) ? 'port=\''.$port.'\'' : '';
  37. $host = isset($host) ? 'host=\''.$host.'\' '.$port : ''; // if no host, connect with the socket
  38. $connection_string = $host.' dbname=\''.$database.'\' user=\''.$user.'\' password=\''.$pass.'\'';
  39. // Make the connection and select the database
  40. if ($this->link = $connect($connection_string))
  41. {
  42. if ($charset = $this->db_config['character_set'])
  43. {
  44. echo $this->set_charset($charset);
  45. }
  46. // Clear password after successful connect
  47. $this->config['connection']['pass'] = NULL;
  48. return $this->link;
  49. }
  50. return FALSE;
  51. }
  52. public function query($sql)
  53. {
  54. // Only cache if it's turned on, and only cache if it's not a write statement
  55. if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|SET)\b#i', $sql))
  56. {
  57. $hash = $this->query_hash($sql);
  58. if ( ! isset(self::$query_cache[$hash]))
  59. {
  60. // Set the cached object
  61. self::$query_cache[$hash] = new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
  62. }
  63. return self::$query_cache[$hash];
  64. }
  65. return new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
  66. }
  67. public function set_charset($charset)
  68. {
  69. $this->query('SET client_encoding TO '.pg_escape_string($this->link, $charset));
  70. }
  71. public function escape_table($table)
  72. {
  73. if (!$this->db_config['escape'])
  74. return $table;
  75. return '"'.str_replace('.', '"."', $table).'"';
  76. }
  77. public function escape_column($column)
  78. {
  79. if (!$this->db_config['escape'])
  80. return $column;
  81. if (strtolower($column) == 'count(*)' OR $column == '*')
  82. return $column;
  83. // This matches any modifiers we support to SELECT.
  84. if ( ! preg_match('/\b(?:all|distinct)\s/i', $column))
  85. {
  86. if (stripos($column, ' AS ') !== FALSE)
  87. {
  88. // Force 'AS' to uppercase
  89. $column = str_ireplace(' AS ', ' AS ', $column);
  90. // Runs escape_column on both sides of an AS statement
  91. $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
  92. // Re-create the AS statement
  93. return implode(' AS ', $column);
  94. }
  95. return preg_replace('/[^.*]+/', '"$0"', $column);
  96. }
  97. $parts = explode(' ', $column);
  98. $column = '';
  99. for ($i = 0, $c = count($parts); $i < $c; $i++)
  100. {
  101. // The column is always last
  102. if ($i == ($c - 1))
  103. {
  104. $column .= preg_replace('/[^.*]+/', '"$0"', $parts[$i]);
  105. }
  106. else // otherwise, it's a modifier
  107. {
  108. $column .= $parts[$i].' ';
  109. }
  110. }
  111. return $column;
  112. }
  113. public function regex($field, $match = '', $type = 'AND ', $num_regexs)
  114. {
  115. $prefix = ($num_regexs == 0) ? '' : $type;
  116. return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\'';
  117. }
  118. public function notregex($field, $match = '', $type = 'AND ', $num_regexs)
  119. {
  120. $prefix = $num_regexs == 0 ? '' : $type;
  121. return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\'';
  122. }
  123. public function limit($limit, $offset = 0)
  124. {
  125. return 'LIMIT '.$limit.' OFFSET '.$offset;
  126. }
  127. public function stmt_prepare($sql = '')
  128. {
  129. is_object($this->link) or $this->connect();
  130. return new Kohana_Mysqli_Statement($sql, $this->link);
  131. }
  132. public function compile_select($database)
  133. {
  134. $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
  135. $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
  136. if (count($database['from']) > 0)
  137. {
  138. $sql .= "\nFROM ";
  139. $sql .= implode(', ', $database['from']);
  140. }
  141. if (count($database['join']) > 0)
  142. {
  143. $sql .= ' '.$database['join']['type'].'JOIN ('.implode(', ', $database['join']['tables']).') ON '.implode(' AND ', $database['join']['conditions']);
  144. }
  145. if (count($database['where']) > 0)
  146. {
  147. $sql .= "\nWHERE ";
  148. }
  149. $sql .= implode("\n", $database['where']);
  150. if (count($database['groupby']) > 0)
  151. {
  152. $sql .= "\nGROUP BY ";
  153. $sql .= implode(', ', $database['groupby']);
  154. }
  155. if (count($database['having']) > 0)
  156. {
  157. $sql .= "\nHAVING ";
  158. $sql .= implode("\n", $database['having']);
  159. }
  160. if (count($database['orderby']) > 0)
  161. {
  162. $sql .= "\nORDER BY ";
  163. $sql .= implode(', ', $database['orderby']);
  164. }
  165. if (is_numeric($database['limit']))
  166. {
  167. $sql .= "\n";
  168. $sql .= $this->limit($database['limit'], $database['offset']);
  169. }
  170. return $sql;
  171. }
  172. public function escape_str($str)
  173. {
  174. if (!$this->db_config['escape'])
  175. return $str;
  176. is_resource($this->link) or $this->connect();
  177. return pg_escape_string($this->link, $str);
  178. }
  179. public function list_tables()
  180. {
  181. $sql = 'SELECT table_schema || \'.\' || table_name FROM information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')';
  182. $result = $this->query($sql)->result(FALSE, PGSQL_ASSOC);
  183. $retval = array();
  184. foreach ($result as $row)
  185. {
  186. $retval[] = current($row);
  187. }
  188. return $retval;
  189. }
  190. public function show_error()
  191. {
  192. return pg_last_error($this->link);
  193. }
  194. public function list_fields($table, $query = FALSE)
  195. {
  196. static $tables;
  197. if (is_object($query))
  198. {
  199. if (empty($tables[$table]))
  200. {
  201. $tables[$table] = array();
  202. foreach ($query as $row)
  203. {
  204. $tables[$table][] = $row->Field;
  205. }
  206. }
  207. return $tables[$table];
  208. }
  209. // WOW...REALLY?!?
  210. // Taken from http://www.postgresql.org/docs/7.4/interactive/catalogs.html
  211. $query = $this->query('SELECT
  212. -- Field
  213. pg_attribute.attname AS "Field",
  214. -- Type
  215. CASE pg_type.typname
  216. WHEN \'int2\' THEN \'smallint\'
  217. WHEN \'int4\' THEN \'int\'
  218. WHEN \'int8\' THEN \'bigint\'
  219. WHEN \'varchar\' THEN \'varchar(\' || pg_attribute.atttypmod-4 || \')\'
  220. ELSE pg_type.typname
  221. END AS "Type",
  222. -- Null
  223. CASE WHEN pg_attribute.attnotnull THEN \'NO\'
  224. ELSE \'YES\'
  225. END AS "Null",
  226. -- Default
  227. CASE pg_type.typname
  228. WHEN \'varchar\' THEN substring(pg_attrdef.adsrc from \'^(.*).*$\')
  229. ELSE pg_attrdef.adsrc
  230. END AS "Default"
  231. FROM pg_class
  232. INNER JOIN pg_attribute
  233. ON (pg_class.oid=pg_attribute.attrelid)
  234. INNER JOIN pg_type
  235. ON (pg_attribute.atttypid=pg_type.oid)
  236. LEFT JOIN pg_attrdef
  237. ON (pg_class.oid=pg_attrdef.adrelid AND pg_attribute.attnum=pg_attrdef.adnum)
  238. WHERE pg_class.relname=\''.$this->escape_str($table).'\' AND pg_attribute.attnum>=1 AND NOT pg_attribute.attisdropped
  239. ORDER BY pg_attribute.attnum');
  240. $fields = array();
  241. foreach ($query as $row)
  242. {
  243. $fields[$row->Field]=$row->Type;
  244. }
  245. return $fields;
  246. }
  247. public function field_data($table)
  248. {
  249. // TODO: This whole function needs to be debugged.
  250. $query = pg_query('SELECT * FROM '.$this->escape_table($table).' LIMIT 1', $this->link);
  251. $fields = pg_num_fields($query);
  252. $table = array();
  253. for ($i=0; $i < $fields; $i++)
  254. {
  255. $table[$i]['type'] = pg_field_type($query, $i);
  256. $table[$i]['name'] = pg_field_name($query, $i);
  257. $table[$i]['len'] = pg_field_prtlen($query, $i);
  258. }
  259. return $table;
  260. }
  261. } // End Database_Pgsql_Driver Class
  262. /**
  263. * PostgreSQL Result
  264. */
  265. class Pgsql_Result extends Database_Result {
  266. // Data fetching types
  267. protected $fetch_type = 'pgsql_fetch_object';
  268. protected $return_type = PGSQL_ASSOC;
  269. /**
  270. * Sets up the result variables.
  271. *
  272. * @param resource query result
  273. * @param resource database link
  274. * @param boolean return objects or arrays
  275. * @param string SQL query that was run
  276. */
  277. public function __construct($result, $link, $object = TRUE, $sql)
  278. {
  279. $this->result = $result;
  280. // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
  281. if (is_resource($result))
  282. {
  283. // Its an DELETE, INSERT, REPLACE, or UPDATE query
  284. if (preg_match('/^(?:delete|insert|replace|update)\s+/i', trim($sql), $matches))
  285. {
  286. $this->insert_id = (strtolower($matches[0]) == 'insert') ? $this->insert_id() : FALSE;
  287. $this->total_rows = pg_affected_rows($this->result);
  288. }
  289. else
  290. {
  291. $this->current_row = 0;
  292. $this->total_rows = pg_num_rows($this->result);
  293. $this->fetch_type = ($object === TRUE) ? 'pg_fetch_object' : 'pg_fetch_array';
  294. }
  295. }
  296. else
  297. {
  298. throw new Kohana_Database_Exception('database.error', pg_last_error().' - '.$sql);
  299. }
  300. // Set result type
  301. $this->result($object);
  302. // Store the SQL
  303. $this->sql = $sql;
  304. }
  305. /**
  306. * Magic __destruct function, frees the result.
  307. */
  308. public function __destruct()
  309. {
  310. if (is_resource($this->result))
  311. {
  312. pg_free_result($this->result);
  313. }
  314. }
  315. public function result($object = TRUE, $type = PGSQL_ASSOC)
  316. {
  317. $this->fetch_type = ((bool) $object) ? 'pg_fetch_object' : 'pg_fetch_array';
  318. // This check has to be outside the previous statement, because we do not
  319. // know the state of fetch_type when $object = NULL
  320. // NOTE - The class set by $type must be defined before fetching the result,
  321. // autoloading is disabled to save a lot of stupid overhead.
  322. if ($this->fetch_type == 'pg_fetch_object')
  323. {
  324. $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  325. }
  326. else
  327. {
  328. $this->return_type = $type;
  329. }
  330. return $this;
  331. }
  332. public function as_array($object = NULL, $type = PGSQL_ASSOC)
  333. {
  334. return $this->result_array($object, $type);
  335. }
  336. public function result_array($object = NULL, $type = PGSQL_ASSOC)
  337. {
  338. $rows = array();
  339. if (is_string($object))
  340. {
  341. $fetch = $object;
  342. }
  343. elseif (is_bool($object))
  344. {
  345. if ($object === TRUE)
  346. {
  347. $fetch = 'pg_fetch_object';
  348. // NOTE - The class set by $type must be defined before fetching the result,
  349. // autoloading is disabled to save a lot of stupid overhead.
  350. $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  351. }
  352. else
  353. {
  354. $fetch = 'pg_fetch_array';
  355. }
  356. }
  357. else
  358. {
  359. // Use the default config values
  360. $fetch = $this->fetch_type;
  361. if ($fetch == 'pg_fetch_object')
  362. {
  363. $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  364. }
  365. }
  366. while ($row = $fetch($this->result, NULL, $type))
  367. {
  368. $rows[] = $row;
  369. }
  370. return $rows;
  371. }
  372. public function insert_id()
  373. {
  374. if ($this->insert_id === NULL)
  375. {
  376. $query = 'SELECT LASTVAL() AS insert_id';
  377. $result = pg_query($link, $query);
  378. $insert_id = pg_fetch_array($result, NULL, PGSQL_ASSOC);
  379. $this->insert_id = $insert_id['insert_id'];
  380. }
  381. return $this->insert_id;
  382. }
  383. public function seek($offset)
  384. {
  385. if ( ! $this->offsetExists($offset))
  386. return FALSE;
  387. return pg_result_seek($this->result, $offset);
  388. }
  389. public function list_fields()
  390. {
  391. $field_names = array();
  392. while ($field = pg_field_name($this->result))
  393. {
  394. $field_names[] = $field->name;
  395. }
  396. return $field_names;
  397. }
  398. /**
  399. * ArrayAccess: offsetGet
  400. */
  401. public function offsetGet($offset)
  402. {
  403. if ( ! $this->seek($offset))
  404. return FALSE;
  405. // Return the row by calling the defined fetching callback
  406. $fetch = $this->fetch_type;
  407. return $fetch($this->result, NULL, $this->return_type);
  408. }
  409. } // End Pgsql_Result Class
  410. /**
  411. * PostgreSQL Prepared Statement (experimental)
  412. */
  413. class Kohana_Pgsql_Statement {
  414. protected $link = NULL;
  415. protected $stmt;
  416. public function __construct($sql, $link)
  417. {
  418. $this->link = $link;
  419. $this->stmt = $this->link->prepare($sql);
  420. return $this;
  421. }
  422. public function __destruct()
  423. {
  424. $this->stmt->close();
  425. }
  426. // Sets the bind parameters
  427. public function bind_params()
  428. {
  429. $argv = func_get_args();
  430. return $this;
  431. }
  432. // sets the statement values to the bound parameters
  433. public function set_vals()
  434. {
  435. return $this;
  436. }
  437. // Runs the statement
  438. public function execute()
  439. {
  440. return $this;
  441. }
  442. }