PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/include/DbSimple/Postgresql.php

https://repo.or.cz/cswow.git
PHP | 312 lines | 279 code | 8 blank | 25 comment | 3 complexity | 379833c8d457f878e73fa5ed737fefd9 MD5 | raw file
  1. <?php
  2. /**
  3. * DbSimple_Postgreql: PostgreSQL database.
  4. * (C) Dk Lab, http://en.dklab.ru
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. * See http://www.gnu.org/copyleft/lesser.html
  11. *
  12. * Placeholders are emulated because of logging purposes.
  13. *
  14. * @author Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
  15. * @author Konstantin Zhinko, http://forum.dklab.ru/users/KonstantinGinkoTit/
  16. *
  17. * @version 2.x $Id: Postgresql.php 167 2007-01-22 10:12:09Z tit $
  18. */
  19. require_once dirname(__FILE__) . '/Generic.php';
  20. /**
  21. * Database class for PostgreSQL.
  22. */
  23. class DbSimple_Postgresql extends DbSimple_Generic_Database
  24. {
  25. var $DbSimple_Postgresql_USE_NATIVE_PHOLDERS = null;
  26. var $prepareCache = array();
  27. var $link;
  28. /**
  29. * constructor(string $dsn)
  30. * Connect to PostgresSQL.
  31. */
  32. function DbSimple_Postgresql($dsn)
  33. {
  34. $p = DbSimple_Generic::parseDSN($dsn);
  35. if (!is_callable('pg_connect')) {
  36. return $this->_setLastError("-1", "PostgreSQL extension is not loaded", "pg_connect");
  37. }
  38. // Prepare+execute works only in PHP 5.1+.
  39. $this->DbSimple_Postgresql_USE_NATIVE_PHOLDERS = function_exists('pg_prepare');
  40. $ok = $this->link = @pg_connect(
  41. $t = (!empty($p['host']) ? 'host='.$p['host'].' ' : '').
  42. (!empty($p['port']) ? 'port='.$p['port'].' ' : '').
  43. 'dbname='.preg_replace('{^/}s', '', $p['path']).' '.
  44. (!empty($p['user']) ? 'user='.$p['user'].' ' : '').
  45. (!empty($p['pass']) ? 'password='.$p['pass'].' ' : '')
  46. );
  47. $this->_resetLastError();
  48. if (!$ok) return $this->_setDbError('pg_connect()');
  49. }
  50. function _performEscape($s, $isIdent=false)
  51. {
  52. if (!$isIdent)
  53. return "'" . str_replace("'", "''", $s) . "'";
  54. else
  55. return '"' . str_replace('"', '_', $s) . '"';
  56. }
  57. function _performTransaction($parameters=null)
  58. {
  59. return $this->query('BEGIN');
  60. }
  61. function& _performNewBlob($blobid=null)
  62. {
  63. $obj = new DbSimple_Postgresql_Blob($this, $blobid);
  64. return $obj;
  65. }
  66. function _performGetBlobFieldNames($result)
  67. {
  68. $blobFields = array();
  69. for ($i=pg_num_fields($result)-1; $i>=0; $i--) {
  70. $type = pg_field_type($result, $i);
  71. if (strpos($type, "BLOB") !== false) $blobFields[] = pg_field_name($result, $i);
  72. }
  73. return $blobFields;
  74. }
  75. // TODO: Real PostgreSQL escape
  76. function _performGetPlaceholderIgnoreRe()
  77. {
  78. return '
  79. " (?> [^"\\\\]+|\\\\"|\\\\)* " |
  80. \' (?> [^\'\\\\]+|\\\\\'|\\\\)* \' |
  81. /\* .*? \*/ # comments
  82. ';
  83. }
  84. function _performGetNativePlaceholderMarker($n)
  85. {
  86. // PostgreSQL uses specific placeholders such as $1, $2, etc.
  87. return '$' . ($n + 1);
  88. }
  89. function _performCommit()
  90. {
  91. return $this->query('COMMIT');
  92. }
  93. function _performRollback()
  94. {
  95. return $this->query('ROLLBACK');
  96. }
  97. function _performTransformQuery(&$queryMain, $how)
  98. {
  99. // If we also need to calculate total number of found rows...
  100. switch ($how) {
  101. // Prepare total calculation (if possible)
  102. case 'CALC_TOTAL':
  103. // Not possible
  104. return true;
  105. // Perform total calculation.
  106. case 'GET_TOTAL':
  107. // TODO: GROUP BY ... -> COUNT(DISTINCT ...)
  108. $re = '/^
  109. (?> -- [^\r\n]* | \s+)*
  110. (\s* SELECT \s+) #1
  111. (.*?) #2
  112. (\s+ FROM \s+ .*?) #3
  113. ((?:\s+ ORDER \s+ BY \s+ .*?)?) #4
  114. ((?:\s+ LIMIT \s+ \S+ \s* (?: OFFSET \s* \S+ \s*)? )?) #5
  115. $/six';
  116. $m = null;
  117. if (preg_match($re, $queryMain[0], $m)) {
  118. $queryMain[0] = $m[1] . $this->_fieldList2Count($m[2]) . " AS C" . $m[3];
  119. $skipTail = substr_count($m[4] . $m[5], '?');
  120. if ($skipTail) array_splice($queryMain, -$skipTail);
  121. }
  122. return true;
  123. }
  124. return false;
  125. }
  126. function _performQuery($queryMain)
  127. {
  128. $this->_lastQuery = $queryMain;
  129. $isInsert = preg_match('/^\s* INSERT \s+/six', $queryMain[0]);
  130. //
  131. // Note that in case of INSERT query we CANNOT work with prepare...execute
  132. // cache, because RULEs do not work after pg_execute(). This is a very strange
  133. // bug... To reproduce:
  134. // $DB->query("CREATE TABLE test(id SERIAL, str VARCHAR(10)) WITH OIDS");
  135. // $DB->query("CREATE RULE test_r AS ON INSERT TO test DO (SELECT 111 AS id)");
  136. // print_r($DB->query("INSERT INTO test(str) VALUES ('test')"));
  137. // In case INSERT + pg_execute() it returns new row OID (numeric) instead
  138. // of result of RULE query. Strange, very strange...
  139. //
  140. if ($this->DbSimple_Postgresql_USE_NATIVE_PHOLDERS && !$isInsert) {
  141. // Use native placeholders only if PG supports them.
  142. $this->_expandPlaceholders($queryMain, true);
  143. $hash = md5($queryMain[0]);
  144. if (!isset($this->prepareCache[$hash])) {
  145. $this->prepareCache[$hash] = true;
  146. $prepared = @pg_prepare($this->link, $hash, $queryMain[0]);
  147. if ($prepared === false) return $this->_setDbError($queryMain[0]);
  148. } else {
  149. // Prepare cache hit!
  150. }
  151. $result = pg_execute($this->link, $hash, array_slice($queryMain, 1));
  152. } else {
  153. // No support for native placeholders or INSERT query.
  154. $this->_expandPlaceholders($queryMain, false);
  155. $result = @pg_query($this->link, $queryMain[0]);
  156. }
  157. if ($result === false) return $this->_setDbError($queryMain);
  158. if (!pg_num_fields($result)) {
  159. if ($isInsert) {
  160. // INSERT queries return generated OID (if table is WITH OIDs).
  161. //
  162. // Please note that unfortunately we cannot use lastval() PostgreSQL
  163. // stored function because it generates fatal error if INSERT query
  164. // does not contain sequence-based field at all. This error terminates
  165. // the current transaction, and we cannot continue to work nor know
  166. // if table contains sequence-updateable field or not.
  167. //
  168. // To use auto-increment functionality you must invoke
  169. // $insertedId = $DB->query("SELECT lastval()")
  170. // manually where it is really needed.
  171. //
  172. return @pg_last_oid($result);
  173. }
  174. // Non-SELECT queries return number of affected rows, SELECT - resource.
  175. return @pg_affected_rows($result);
  176. }
  177. return $result;
  178. }
  179. function _performFetch($result)
  180. {
  181. $row = @pg_fetch_assoc($result);
  182. if (pg_last_error($this->link)) return $this->_setDbError($this->_lastQuery);
  183. if ($row === false) return null;
  184. return $row;
  185. }
  186. function _setDbError($query)
  187. {
  188. return $this->_setLastError(null, pg_last_error($this->link), $query);
  189. }
  190. function _getVersion()
  191. {
  192. }
  193. }
  194. class DbSimple_Postgresql_Blob extends DbSimple_Generic_Blob
  195. {
  196. var $blob; // resourse link
  197. var $id;
  198. var $database;
  199. function DbSimple_Postgresql_Blob(&$database, $id=null)
  200. {
  201. $this->database =& $database;
  202. $this->database->transaction();
  203. $this->id = $id;
  204. $this->blob = null;
  205. }
  206. function read($len)
  207. {
  208. if ($this->id === false) return ''; // wr-only blob
  209. if (!($e=$this->_firstUse())) return $e;
  210. $data = @pg_lo_read($this->blob, $len);
  211. if ($data === false) return $this->_setDbError('read');
  212. return $data;
  213. }
  214. function write($data)
  215. {
  216. if (!($e=$this->_firstUse())) return $e;
  217. $ok = @pg_lo_write($this->blob, $data);
  218. if ($ok === false) return $this->_setDbError('add data to');
  219. return true;
  220. }
  221. function close()
  222. {
  223. if (!($e=$this->_firstUse())) return $e;
  224. if ($this->blob) {
  225. $id = @pg_lo_close($this->blob);
  226. if ($id === false) return $this->_setDbError('close');
  227. $this->blob = null;
  228. } else {
  229. $id = null;
  230. }
  231. $this->database->commit();
  232. return $this->id? $this->id : $id;
  233. }
  234. function length()
  235. {
  236. if (!($e=$this->_firstUse())) return $e;
  237. @pg_lo_seek($this->blob, 0, PGSQL_SEEK_END);
  238. $len = @pg_lo_tell($this->blob);
  239. @pg_lo_seek($this->blob, 0, PGSQL_SEEK_SET);
  240. if (!$len) return $this->_setDbError('get length of');
  241. return $len;
  242. }
  243. function _setDbError($query)
  244. {
  245. $hId = $this->id === null? "null" : ($this->id === false? "false" : $this->id);
  246. $query = "-- $query BLOB $hId";
  247. $this->database->_setDbError($query);
  248. }
  249. // Called on each blob use (reading or writing).
  250. function _firstUse()
  251. {
  252. // BLOB opened - do nothing.
  253. if (is_resource($this->blob)) return true;
  254. // Open or create blob.
  255. if ($this->id !== null) {
  256. $this->blob = @pg_lo_open($this->database->link, $this->id, 'rw');
  257. if ($this->blob === false) return $this->_setDbError('open');
  258. } else {
  259. $this->id = @pg_lo_create($this->database->link);
  260. $this->blob = @pg_lo_open($this->database->link, $this->id, 'w');
  261. if ($this->blob === false) return $this->_setDbError('create');
  262. }
  263. return true;
  264. }
  265. }
  266. ?>