PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/include/dblayer/pgsql.php

https://github.com/Dratone/EveBB
PHP | 521 lines | 358 code | 140 blank | 23 comment | 62 complexity | e62ddbf01e2a6e9a675525bed46ea7eb MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Copyright (C) 2008-2010 FluxBB
  4. * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  5. * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  6. */
  7. // Make sure we have built in support for PostgreSQL
  8. if (!function_exists('pg_connect'))
  9. exit('This PHP environment doesn\'t have PostgreSQL support built in. PostgreSQL support is required if you want to use a PostgreSQL database to run this forum. Consult the PHP documentation for further assistance.');
  10. class DBLayer
  11. {
  12. var $prefix;
  13. var $link_id;
  14. var $query_result;
  15. var $last_query_text = array();
  16. var $in_transaction = 0;
  17. var $saved_queries = array();
  18. var $num_queries = 0;
  19. var $error_no = false;
  20. var $error_msg = 'Unknown';
  21. var $datatype_transformations = array(
  22. '/^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i' => 'SMALLINT',
  23. '/^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i' => 'INTEGER',
  24. '/^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i' => 'BIGINT',
  25. '/^(TINY|MEDIUM|LONG)?TEXT$/i' => 'TEXT',
  26. '/^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$/i' => 'DOUBLE PRECISION',
  27. '/^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i' => 'REAL'
  28. );
  29. function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
  30. {
  31. $this->prefix = $db_prefix;
  32. if ($db_host)
  33. {
  34. if (strpos($db_host, ':') !== false)
  35. {
  36. list($db_host, $dbport) = explode(':', $db_host);
  37. $connect_str[] = 'host='.$db_host.' port='.$dbport;
  38. }
  39. else
  40. $connect_str[] = 'host='.$db_host;
  41. }
  42. if ($db_name)
  43. $connect_str[] = 'dbname='.$db_name;
  44. if ($db_username)
  45. $connect_str[] = 'user='.$db_username;
  46. if ($db_password)
  47. $connect_str[] = 'password='.$db_password;
  48. if ($p_connect)
  49. $this->link_id = @pg_pconnect(implode(' ', $connect_str));
  50. else
  51. $this->link_id = @pg_connect(implode(' ', $connect_str));
  52. if (!$this->link_id)
  53. error('Unable to connect to PostgreSQL server', __FILE__, __LINE__);
  54. // Setup the client-server character set (UTF-8)
  55. if (!defined('FORUM_NO_SET_NAMES'))
  56. $this->set_names('utf8');
  57. return $this->link_id;
  58. }
  59. function start_transaction()
  60. {
  61. ++$this->in_transaction;
  62. return (@pg_query($this->link_id, 'BEGIN')) ? true : false;
  63. }
  64. function end_transaction()
  65. {
  66. --$this->in_transaction;
  67. if (@pg_query($this->link_id, 'COMMIT'))
  68. return true;
  69. else
  70. {
  71. @pg_query($this->link_id, 'ROLLBACK');
  72. return false;
  73. }
  74. }
  75. function query($sql, $unbuffered = false) // $unbuffered is ignored since there is no pgsql_unbuffered_query()
  76. {
  77. if (strrpos($sql, 'LIMIT') !== false)
  78. $sql = preg_replace('#LIMIT ([0-9]+),([ 0-9]+)#', 'LIMIT \\2 OFFSET \\1', $sql);
  79. if (defined('PUN_SHOW_QUERIES'))
  80. $q_start = get_microtime();
  81. @pg_send_query($this->link_id, $sql);
  82. $this->query_result = @pg_get_result($this->link_id);
  83. if (pg_result_status($this->query_result) != PGSQL_FATAL_ERROR)
  84. {
  85. if (defined('PUN_SHOW_QUERIES'))
  86. $this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
  87. ++$this->num_queries;
  88. $this->last_query_text[$this->query_result] = $sql;
  89. return $this->query_result;
  90. }
  91. else
  92. {
  93. if (defined('PUN_SHOW_QUERIES'))
  94. $this->saved_queries[] = array($sql, 0);
  95. $this->error_no = false;
  96. $this->error_msg = @pg_result_error($this->query_result);
  97. if ($this->in_transaction)
  98. @pg_query($this->link_id, 'ROLLBACK');
  99. --$this->in_transaction;
  100. return false;
  101. }
  102. }
  103. /**
  104. * This function mimics the functionality for the 'ON DUPLICATE KEY UPDATE' command in MySQL5.
  105. * With PostgreSQL, we will be taking the long way around of checking to see if it exists.
  106. * This function now excepts an array value for $primaryKey, allowing more complex inserts or updates.
  107. */
  108. function insert_or_update($fields, $primaryKey, $table, $fields_to_update = array()) {
  109. $keyList = '';
  110. if (is_array($primaryKey)) {
  111. if (count($primaryKey) == 0) {
  112. $this->error_msg = 'No Primary keys passed.';
  113. return false;
  114. } //End if.
  115. $sql = "SELECT ".$primaryKey[0]." FROM ".$table." WHERE ";
  116. foreach ($primaryKey as $key) {
  117. if (!isset($fields[$key])) {
  118. $this->error_msg = 'Primary key value missing.';
  119. return false;
  120. } //End if.
  121. $keyList .= $key."=".(is_string($fields[$key]) ? "'".$fields[$key]."'": $fields[$key])." AND ";
  122. } //End foreach().
  123. //Get rid fo the last " AND " bit.
  124. $keyList = substr($keyList, 0, -4);
  125. $sql .= $keyList;
  126. } else {
  127. if (!isset($fields[$primaryKey])) {
  128. $this->error_msg = 'Primary key value missing.';
  129. return false;
  130. } //End if.
  131. $sql = "SELECT ".$primaryKey." FROM ".$table." WHERE ".$primaryKey."=".(is_string($fields[$primaryKey]) ? "'".$fields[$primaryKey]."'": $fields[$primaryKey]);
  132. } //End if - else.
  133. if (!$result = $this->query($sql)) {
  134. return false;
  135. } //End if.
  136. if ($this->num_rows($result) == 1) {
  137. //Lets do an update.
  138. $update_fields = "";
  139. if (empty($fields_to_update)) {
  140. foreach($fields as $key => $value) {
  141. $update_fields .= $key."=".(is_string($value) ? "'".$value."'": $value).",";
  142. } //End foreach().
  143. } else {
  144. foreach($fields_to_update as $key => $value) {
  145. $update_fields .= $key."=".(is_string($value) ? "'".$value."'": $value).",";
  146. } //End foreach().
  147. } //End if - else.
  148. $update_fields = substr($update_fields, 0, -1);
  149. if (is_array($primaryKey)) {
  150. $sql = "UPDATE ".$table." SET ".$update_fields." WHERE ".$keyList;
  151. } else {
  152. $sql = "UPDATE ".$table." SET ".$update_fields." WHERE ".$primaryKey."=".(is_string($value) ? "'".$fields[$primaryKey]."'": $fields[$primaryKey]);
  153. } //End if - else.
  154. } else {
  155. //Insert that data, baby.
  156. $table_fields = $table."(";
  157. $insert_fields = "VALUES(";
  158. foreach($fields as $key => $value) {
  159. $insert_fields .= (is_string($value) ? "'".$value."'": $value).",";
  160. $table_fields .= $key.',';
  161. } //End foreach().
  162. $insert_fields = substr($insert_fields, 0, -1);
  163. $table_fields = substr($table_fields, 0, -1);
  164. $table_fields .= ")";
  165. $insert_fields .= ")";
  166. $sql = "INSERT INTO ".$table_fields." ".$insert_fields.";";
  167. } //End if - else.
  168. return $this->query($sql);
  169. } //End insert_or_update().
  170. function result($query_id = 0, $row = 0, $col = 0)
  171. {
  172. return ($query_id) ? @pg_fetch_result($query_id, $row, $col) : false;
  173. }
  174. function fetch_assoc($query_id = 0)
  175. {
  176. return ($query_id) ? @pg_fetch_assoc($query_id) : false;
  177. }
  178. function fetch_row($query_id = 0)
  179. {
  180. return ($query_id) ? @pg_fetch_row($query_id) : false;
  181. }
  182. function num_rows($query_id = 0)
  183. {
  184. return ($query_id) ? @pg_num_rows($query_id) : false;
  185. }
  186. function affected_rows()
  187. {
  188. return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
  189. }
  190. function insert_id()
  191. {
  192. $query_id = $this->query_result;
  193. if ($query_id && $this->last_query_text[$query_id] != '')
  194. {
  195. if (preg_match('/^INSERT INTO ([a-z0-9\_\-]+)/is', $this->last_query_text[$query_id], $table_name))
  196. {
  197. // Hack (don't ask)
  198. if (substr($table_name[1], -6) == 'groups')
  199. $table_name[1] .= '_g';
  200. $temp_q_id = @pg_query($this->link_id, 'SELECT currval(\''.$table_name[1].'_id_seq\')');
  201. return ($temp_q_id) ? intval(@pg_fetch_result($temp_q_id, 0)) : false;
  202. }
  203. }
  204. return false;
  205. }
  206. function get_num_queries()
  207. {
  208. return $this->num_queries;
  209. }
  210. function get_saved_queries()
  211. {
  212. return $this->saved_queries;
  213. }
  214. function free_result($query_id = false)
  215. {
  216. if (!$query_id)
  217. $query_id = $this->query_result;
  218. return ($query_id) ? @pg_free_result($query_id) : false;
  219. }
  220. function escape($str)
  221. {
  222. return is_array($str) ? '' : pg_escape_string($str);
  223. }
  224. function error()
  225. {
  226. $result['error_sql'] = @current(@end($this->saved_queries));
  227. $result['error_no'] = $this->error_no;
  228. $result['error_msg'] = $this->error_msg;
  229. return $result;
  230. }
  231. function close()
  232. {
  233. if ($this->link_id)
  234. {
  235. if ($this->in_transaction)
  236. {
  237. if (defined('PUN_SHOW_QUERIES'))
  238. $this->saved_queries[] = array('COMMIT', 0);
  239. @pg_query($this->link_id, 'COMMIT');
  240. }
  241. if ($this->query_result)
  242. @pg_free_result($this->query_result);
  243. return @pg_close($this->link_id);
  244. }
  245. else
  246. return false;
  247. }
  248. function get_names()
  249. {
  250. $result = $this->query('SHOW client_encoding');
  251. return strtolower($this->result($result)); // MySQL returns lowercase so lets be consistent
  252. }
  253. function set_names($names)
  254. {
  255. return $this->query('SET NAMES \''.$this->escape($names).'\'');
  256. }
  257. function get_version()
  258. {
  259. $result = $this->query('SELECT VERSION()');
  260. return array(
  261. 'name' => 'PostgreSQL',
  262. 'version' => preg_replace('/^[^0-9]+([^\s,-]+).*$/', '\\1', $this->result($result))
  263. );
  264. }
  265. function table_exists($table_name, $no_prefix = false)
  266. {
  267. $result = $this->query('SELECT 1 FROM pg_class WHERE relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
  268. return $this->num_rows($result) > 0;
  269. }
  270. function field_exists($table_name, $field_name, $no_prefix = false)
  271. {
  272. $result = $this->query('SELECT 1 FROM pg_class c INNER JOIN pg_attribute a ON a.attrelid = c.oid WHERE c.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND a.attname = \''.$this->escape($field_name).'\'');
  273. return $this->num_rows($result) > 0;
  274. }
  275. function index_exists($table_name, $index_name, $no_prefix = false)
  276. {
  277. $result = $this->query('SELECT 1 FROM pg_index i INNER JOIN pg_class c1 ON c1.oid = i.indrelid INNER JOIN pg_class c2 ON c2.oid = i.indexrelid WHERE c1.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND c2.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\'');
  278. return $this->num_rows($result) > 0;
  279. }
  280. function create_table($table_name, $schema, $no_prefix = false)
  281. {
  282. if ($this->table_exists($table_name, $no_prefix))
  283. return true;
  284. $query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
  285. // Go through every schema element and add it to the query
  286. foreach ($schema['FIELDS'] as $field_name => $field_data)
  287. {
  288. $field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
  289. $query .= $field_name.' '.$field_data['datatype'];
  290. (!isset($field_data['allow_null'])) ? $field_data['allow_null'] = false : '';
  291. // The SERIAL datatype is a special case where we don't need to say not null
  292. if (!$field_data['allow_null'] && $field_data['datatype'] != 'SERIAL')
  293. $query .= ' NOT NULL';
  294. if (isset($field_data['default']))
  295. $query .= ' DEFAULT '.$field_data['default'];
  296. $query .= ",\n";
  297. }
  298. // If we have a primary key, add it
  299. if (isset($schema['PRIMARY KEY']))
  300. $query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
  301. // Add unique keys
  302. if (isset($schema['UNIQUE KEYS']))
  303. {
  304. foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
  305. $query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
  306. }
  307. // We remove the last two characters (a newline and a comma) and add on the ending
  308. $query = substr($query, 0, strlen($query) - 2)."\n".')';
  309. $result = $this->query($query) ? true : false;
  310. // Add indexes
  311. if (isset($schema['INDEXES']))
  312. {
  313. foreach ($schema['INDEXES'] as $index_name => $index_fields)
  314. $result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
  315. }
  316. return $result;
  317. }
  318. function drop_table($table_name, $no_prefix = false)
  319. {
  320. if (!$this->table_exists($table_name, $no_prefix))
  321. return true;
  322. return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
  323. }
  324. function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
  325. {
  326. if ($this->field_exists($table_name, $field_name, $no_prefix))
  327. return true;
  328. $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
  329. $result = $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type) ? true : false;
  330. if ($default_value !== null)
  331. {
  332. if (!is_int($default_value) && !is_float($default_value))
  333. $default_value = '\''.$this->escape($default_value).'\'';
  334. $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET DEFAULT '.$default_value) ? true : false;
  335. $result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET '.$field_name.'='.$default_value) ? true : false;
  336. }
  337. if (!$allow_null)
  338. $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET NOT NULL') ? true : false;
  339. return $result;
  340. }
  341. function rename_table($old_table, $new_table, $no_prefix = false)
  342. {
  343. // If there new table exists and the old one doesn't, then we're happy
  344. if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
  345. return true;
  346. return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
  347. }
  348. function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
  349. {
  350. if (!$this->field_exists($table_name, $field_name, $no_prefix))
  351. return true;
  352. $field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
  353. $result = $this->add_field($table_name, 'tmp_'.$field_name, $field_type, $allow_null, $default_value, $after_field, $no_prefix);
  354. $result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET tmp_'.$field_name.' = '.$field_name) ? true : false;
  355. $result &= $this->drop_field($table_name, $field_name, $no_prefix);
  356. $result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' RENAME COLUMN tmp_'.$field_name.' TO '.$field_name) ? true : false;
  357. return $result;
  358. }
  359. function drop_field($table_name, $field_name, $no_prefix = false)
  360. {
  361. if (!$this->field_exists($table_name, $field_name, $no_prefix))
  362. return true;
  363. return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
  364. }
  365. function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
  366. {
  367. if ($this->index_exists($table_name, $index_name, $no_prefix))
  368. return true;
  369. return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
  370. }
  371. function drop_index($table_name, $index_name, $no_prefix = false)
  372. {
  373. if (!$this->index_exists($table_name, $index_name, $no_prefix))
  374. return true;
  375. return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
  376. }
  377. function truncate_table($table_name, $no_prefix = false)
  378. {
  379. return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
  380. }
  381. }