ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
pgsql.php
Go to the documentation of this file.
1<?php
2// vim: set et ts=4 sw=4 fdm=marker:
3// +----------------------------------------------------------------------+
4// | PHP versions 4 and 5 |
5// +----------------------------------------------------------------------+
6// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, |
7// | Stig. S. Bakken, Lukas Smith |
8// | All rights reserved. |
9// +----------------------------------------------------------------------+
10// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
11// | API as well as database abstraction for PHP applications. |
12// | This LICENSE is in the BSD license style. |
13// | |
14// | Redistribution and use in source and binary forms, with or without |
15// | modification, are permitted provided that the following conditions |
16// | are met: |
17// | |
18// | Redistributions of source code must retain the above copyright |
19// | notice, this list of conditions and the following disclaimer. |
20// | |
21// | Redistributions in binary form must reproduce the above copyright |
22// | notice, this list of conditions and the following disclaimer in the |
23// | documentation and/or other materials provided with the distribution. |
24// | |
25// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
26// | Lukas Smith nor the names of his contributors may be used to endorse |
27// | or promote products derived from this software without specific prior|
28// | written permission. |
29// | |
30// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
31// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
32// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
33// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
34// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
35// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
36// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
37// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
38// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
39// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
40// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
41// | POSSIBILITY OF SUCH DAMAGE. |
42// +----------------------------------------------------------------------+
43// | Author: Paul Cooper <pgc@ucecom.com> |
44// +----------------------------------------------------------------------+
45//
46// $Id: pgsql.php,v 1.173 2007/05/02 22:00:08 quipo Exp $
47
56{
57 // {{{ properties
58 var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => '\\');
59
60 var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
61 // }}}
62 // {{{ constructor
63
67 function __construct()
68 {
69 parent::__construct();
70
71 $this->phptype = 'pgsql';
72 $this->dbsyntax = 'pgsql';
73
74 $this->supported['sequences'] = true;
75 $this->supported['indexes'] = true;
76 $this->supported['affected_rows'] = true;
77 $this->supported['summary_functions'] = true;
78 $this->supported['order_by_text'] = true;
79 $this->supported['transactions'] = true;
80 $this->supported['savepoints'] = true;
81 $this->supported['current_id'] = true;
82 $this->supported['limit_queries'] = true;
83 $this->supported['LOBs'] = true;
84 $this->supported['replace'] = 'emulated';
85 $this->supported['sub_selects'] = true;
86 $this->supported['auto_increment'] = 'emulated';
87 $this->supported['primary_key'] = true;
88 $this->supported['result_introspection'] = true;
89 $this->supported['prepared_statements'] = true;
90 $this->supported['identifier_quoting'] = true;
91 $this->supported['pattern_escaping'] = true;
92 $this->supported['new_link'] = true;
93
94 $this->options['multi_query'] = false;
95 }
96
97 // }}}
98 // {{{ errorInfo()
99
107 function errorInfo($error = null)
108 {
109 // Fall back to MDB2_ERROR if there was no mapping.
110 $error_code = MDB2_ERROR;
111
112 $native_msg = '';
113 if (is_resource($error)) {
114 $native_msg = @pg_result_error($error);
115 } elseif ($this->connection) {
116 $native_msg = @pg_last_error($this->connection);
117 if (!$native_msg && @pg_connection_status($this->connection) === PGSQL_CONNECTION_BAD) {
118 $native_msg = 'Database connection has been lost.';
119 $error_code = MDB2_ERROR_CONNECT_FAILED;
120 }
121 }
122
123 static $error_regexps;
124 if (empty($error_regexps)) {
125 $error_regexps = array(
126 '/column .* (of relation .*)?does not exist/i'
128 '/(relation|sequence|table).*does not exist|class .* not found/i'
130 '/index .* does not exist/'
132 '/relation .* already exists/i'
134 '/(divide|division) by zero$/i'
136 '/pg_atoi: error in .*: can\'t parse /i'
138 '/invalid input syntax for( type)? (integer|numeric)/i'
140 '/value .* is out of range for type \w*int/i'
142 '/integer out of range/i'
144 '/value too long for type character/i'
146 '/attribute .* not found|relation .* does not have attribute/i'
148 '/column .* specified in USING clause does not exist in (left|right) table/i'
150 '/parser: parse error at or near/i'
152 '/syntax error at/'
154 '/column reference .* is ambiguous/i'
156 '/permission denied/'
158 '/violates not-null constraint/'
160 '/violates [\w ]+ constraint/'
162 '/referential integrity violation/'
164 '/more expressions than target columns/i'
166 );
167 }
168 if (is_numeric($error) && $error < 0) {
169 $error_code = $error;
170 } else {
171 foreach ($error_regexps as $regexp => $code) {
172 if (preg_match($regexp, $native_msg)) {
173 $error_code = $code;
174 break;
175 }
176 }
177 }
178 return array($error_code, null, $native_msg);
179 }
180
181 // }}}
182 // {{{ escape()
183
195 function escape($text, $escape_wildcards = false)
196 {
197 if ($escape_wildcards) {
198 $text = $this->escapePattern($text);
199 }
200 $connection = $this->getConnection();
202 return $connection;
203 }
204 if (version_compare(PHP_VERSION, '5.2.0RC5', '>=')) {
205 $text = @pg_escape_string($connection, $text);
206 } else {
207 $text = @pg_escape_string($text);
208 }
209 return $text;
210 }
211
212 // }}}
213 // {{{ beginTransaction()
214
223 function beginTransaction($savepoint = null)
224 {
225 $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
226 if (!is_null($savepoint)) {
227 if (!$this->in_transaction) {
228 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
229 'savepoint cannot be released when changes are auto committed', __FUNCTION__);
230 }
231 $query = 'SAVEPOINT '.$savepoint;
232 return $this->_doQuery($query, true);
233 } elseif ($this->in_transaction) {
234 return MDB2_OK; //nothing to do
235 }
236 if (!$this->destructor_registered && $this->opened_persistent) {
237 $this->destructor_registered = true;
238 register_shutdown_function('MDB2_closeOpenTransactions');
239 }
240 $result =& $this->_doQuery('BEGIN', true);
241 if (PEAR::isError($result)) {
242 return $result;
243 }
244 $this->in_transaction = true;
245 return MDB2_OK;
246 }
247
248 // }}}
249 // {{{ commit()
250
262 function commit($savepoint = null)
263 {
264 $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
265 if (!$this->in_transaction) {
266 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
267 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__);
268 }
269 if (!is_null($savepoint)) {
270 $query = 'RELEASE SAVEPOINT '.$savepoint;
271 return $this->_doQuery($query, true);
272 }
273
274 $result =& $this->_doQuery('COMMIT', true);
275 if (PEAR::isError($result)) {
276 return $result;
277 }
278 $this->in_transaction = false;
279 return MDB2_OK;
280 }
281
282 // }}}
283 // {{{ rollback()
284
296 function rollback($savepoint = null)
297 {
298 $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
299 if (!$this->in_transaction) {
300 return $this->raiseError(MDB2_ERROR_INVALID, null, null,
301 'rollback cannot be done changes are auto committed', __FUNCTION__);
302 }
303 if (!is_null($savepoint)) {
304 $query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
305 return $this->_doQuery($query, true);
306 }
307
308 $query = 'ROLLBACK';
309 $result =& $this->_doQuery($query, true);
310 if (PEAR::isError($result)) {
311 return $result;
312 }
313 $this->in_transaction = false;
314 return MDB2_OK;
315 }
316
317 // }}}
318 // {{{ function setTransactionIsolation()
319
333 function setTransactionIsolation($isolation)
334 {
335 $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
336 switch ($isolation) {
337 case 'READ UNCOMMITTED':
338 case 'READ COMMITTED':
339 case 'REPEATABLE READ':
340 case 'SERIALIZABLE':
341 break;
342 default:
343 return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
344 'isolation level is not supported: '.$isolation, __FUNCTION__);
345 }
346
347 $query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL $isolation";
348 return $this->_doQuery($query, true);
349 }
350
351 // }}}
352 // {{{ _doConnect()
353
360 function _doConnect($database_name, $persistent = false)
361 {
362 if ($database_name == '') {
363 $database_name = 'template1';
364 }
365
366 $protocol = $this->dsn['protocol'] ? $this->dsn['protocol'] : 'tcp';
367
368 $params = array('');
369 if ($protocol == 'tcp') {
370 if ($this->dsn['hostspec']) {
371 $params[0].= 'host=' . $this->dsn['hostspec'];
372 }
373 if ($this->dsn['port']) {
374 $params[0].= ' port=' . $this->dsn['port'];
375 }
376 } elseif ($protocol == 'unix') {
377 // Allow for pg socket in non-standard locations.
378 if ($this->dsn['socket']) {
379 $params[0].= 'host=' . $this->dsn['socket'];
380 }
381 if ($this->dsn['port']) {
382 $params[0].= ' port=' . $this->dsn['port'];
383 }
384 }
385 if ($database_name) {
386 $params[0].= ' dbname=\'' . addslashes($database_name) . '\'';
387 }
388 if ($this->dsn['username']) {
389 $params[0].= ' user=\'' . addslashes($this->dsn['username']) . '\'';
390 }
391 if ($this->dsn['password']) {
392 $params[0].= ' password=\'' . addslashes($this->dsn['password']) . '\'';
393 }
394 if (!empty($this->dsn['options'])) {
395 $params[0].= ' options=' . $this->dsn['options'];
396 }
397 if (!empty($this->dsn['tty'])) {
398 $params[0].= ' tty=' . $this->dsn['tty'];
399 }
400 if (!empty($this->dsn['connect_timeout'])) {
401 $params[0].= ' connect_timeout=' . $this->dsn['connect_timeout'];
402 }
403 if (!empty($this->dsn['sslmode'])) {
404 $params[0].= ' sslmode=' . $this->dsn['sslmode'];
405 }
406 if (!empty($this->dsn['service'])) {
407 $params[0].= ' service=' . $this->dsn['service'];
408 }
409
410 if (!empty($this->dsn['new_link'])
411 && ($this->dsn['new_link'] == 'true' || $this->dsn['new_link'] === true))
412 {
413 if (version_compare(phpversion(), '4.3.0', '>=')) {
414 $params[] = PGSQL_CONNECT_FORCE_NEW;
415 }
416 }
417
418 $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
419
420 $connection = @call_user_func_array($connect_function, $params);
421 if (!$connection) {
422 return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
423 'unable to establish a connection', __FUNCTION__);
424 }
425
426 if (empty($this->dsn['disable_iso_date'])) {
427 if (!@pg_query($connection, "SET SESSION DATESTYLE = 'ISO'")) {
428 return $this->raiseError(null, null, null,
429 'Unable to set date style to iso', __FUNCTION__);
430 }
431 }
432
433 if (!empty($this->dsn['charset'])) {
434 $result = $this->setCharset($this->dsn['charset'], $connection);
435 if (PEAR::isError($result)) {
436 return $result;
437 }
438 }
439
440 return $connection;
441 }
442
443 // }}}
444 // {{{ connect()
445
452 function connect()
453 {
454 if (is_resource($this->connection)) {
455 if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
456 && $this->connected_database_name == $this->database_name
457 && ($this->opened_persistent == $this->options['persistent'])
458 ) {
459 return MDB2_OK;
460 }
461 $this->disconnect(false);
462 }
463
464 if (!PEAR::loadExtension($this->phptype)) {
465 return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
466 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
467 }
468
469 if ($this->database_name) {
470 $connection = $this->_doConnect($this->database_name, $this->options['persistent']);
472 return $connection;
473 }
474 $this->connection = $connection;
475 $this->connected_dsn = $this->dsn;
476 $this->connected_database_name = $this->database_name;
477 $this->opened_persistent = $this->options['persistent'];
478 $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
479 }
480 return MDB2_OK;
481 }
482
483 // }}}
484 // {{{ setCharset()
485
494 function setCharset($charset, $connection = null)
495 {
496 if (is_null($connection)) {
497 $connection = $this->getConnection();
499 return $connection;
500 }
501 }
502
503 $result = @pg_set_client_encoding($connection, $charset);
504 if ($result == -1) {
505 return $this->raiseError(null, null, null,
506 'Unable to set client charset: '.$charset, __FUNCTION__);
507 }
508 return MDB2_OK;
509 }
510
511 // }}}
512 // {{{ disconnect()
513
523 function disconnect($force = true)
524 {
525 if (is_resource($this->connection)) {
526 if ($this->in_transaction) {
529 $persistent = $this->options['persistent'];
530 $this->dsn = $this->connected_dsn;
531 $this->database_name = $this->connected_database_name;
532 $this->options['persistent'] = $this->opened_persistent;
533 $this->rollback();
534 $this->dsn = $dsn;
535 $this->database_name = $database_name;
536 $this->options['persistent'] = $persistent;
537 }
538
539 if (!$this->opened_persistent || $force) {
540 @pg_close($this->connection);
541 }
542 }
543 return parent::disconnect($force);
544 }
545
546 // }}}
547 // {{{ standaloneQuery()
548
559 function &standaloneQuery($query, $types = null, $is_manip = false)
560 {
561 $connection = $this->_doConnect('template1', false);
563 $err =& $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
564 'Cannot connect to template1', __FUNCTION__);
565 return $err;
566 }
567
570 $this->offset = $this->limit = 0;
571 $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
572
573 $result =& $this->_doQuery($query, $is_manip, $connection, false);
574 @pg_close($connection);
575 if (PEAR::isError($result)) {
576 return $result;
577 }
578
579 if ($is_manip) {
580 $affected_rows = $this->_affectedRows($connection, $result);
581 return $affected_rows;
582 }
583 $result =& $this->_wrapResult($result, $types, true, false, $limit, $offset);
584 return $result;
585 }
586
587 // }}}
588 // {{{ _doQuery()
589
599 function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
600 {
601 $this->last_query = $query;
602 $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
603 if ($result) {
604 if (PEAR::isError($result)) {
605 return $result;
606 }
607 $query = $result;
608 }
609 if ($this->options['disable_query']) {
610 $result = $is_manip ? 0 : null;
611 return $result;
612 }
613
614 if (is_null($connection)) {
615 $connection = $this->getConnection();
617 return $connection;
618 }
619 }
620
621 $function = $this->options['multi_query'] ? 'pg_send_query' : 'pg_query';
622 $result = @$function($connection, $query);
623 if (!$result) {
624 $err =& $this->raiseError(null, null, null,
625 'Could not execute statement', __FUNCTION__);
626 return $err;
627 } elseif ($this->options['multi_query']) {
628 if (!($result = @pg_get_result($connection))) {
629 $err =& $this->raiseError(null, null, null,
630 'Could not get the first result from a multi query', __FUNCTION__);
631 return $err;
632 }
633 }
634
635 $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
636 return $result;
637 }
638
639 // }}}
640 // {{{ _affectedRows()
641
651 {
652 if (is_null($connection)) {
653 $connection = $this->getConnection();
655 return $connection;
656 }
657 }
658 return @pg_affected_rows($result);
659 }
660
661 // }}}
662 // {{{ _modifyQuery()
663
674 function _modifyQuery($query, $is_manip, $limit, $offset)
675 {
676 if ($limit > 0
677 && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\‍)]*)?$/i', $query)
678 ) {
679 $query = rtrim($query);
680 if (substr($query, -1) == ';') {
681 $query = substr($query, 0, -1);
682 }
683 if ($is_manip) {
685 } else {
686 $query.= " LIMIT $limit OFFSET $offset";
687 }
688 }
689 return $query;
690 }
691
692 // }}}
693 // {{{ _modifyManipQuery()
694
704 {
705 $pos = strpos(strtolower($query), 'where');
706 $where = $pos ? substr($query, $pos) : '';
707
708 $manip_clause = '(\bDELETE\b\s+(?:\*\s+)?\bFROM\b|\bUPDATE\b)';
709 $from_clause = '([\w\.]+)';
710 $where_clause = '(?:(.*)\bWHERE\b\s+(.*))|(.*)';
711 $pattern = '/^'. $manip_clause . '\s+' . $from_clause .'(?:\s)*(?:'. $where_clause .')?$/i';
712 $matches = preg_match($pattern, $query, $match);
713 if ($matches) {
714 $manip = $match[1];
715 $from = $match[2];
716 $what = (count($matches) == 6) ? $match[5] : $match[3];
717 return $manip.' '.$from.' '.$what.' WHERE ctid=(SELECT ctid FROM '.$from.' '.$where.' LIMIT '.$limit.')';
718 }
719 //return error?
720 return $query;
721 }
722
723 // }}}
724 // {{{ getServerVersion()
725
733 function getServerVersion($native = false)
734 {
735 $query = 'SHOW SERVER_VERSION';
736 if ($this->connected_server_info) {
737 $server_info = $this->connected_server_info;
738 } else {
739 $server_info = $this->queryOne($query, 'text');
740 if (PEAR::isError($server_info)) {
741 return $server_info;
742 }
743 }
744 // cache server_info
745 $this->connected_server_info = $server_info;
746 if (!$native && !PEAR::isError($server_info)) {
747 $tmp = explode('.', $server_info, 3);
748 if (empty($tmp[2])
749 && isset($tmp[1])
750 && preg_match('/(\d+)(.*)/', $tmp[1], $tmp2)
751 ) {
752 $server_info = array(
753 'major' => $tmp[0],
754 'minor' => $tmp2[1],
755 'patch' => null,
756 'extra' => $tmp2[2],
757 'native' => $server_info,
758 );
759 } else {
760 $server_info = array(
761 'major' => isset($tmp[0]) ? $tmp[0] : null,
762 'minor' => isset($tmp[1]) ? $tmp[1] : null,
763 'patch' => isset($tmp[2]) ? $tmp[2] : null,
764 'extra' => null,
765 'native' => $server_info,
766 );
767 }
768 }
769 return $server_info;
770 }
771
772 // }}}
773 // {{{ prepare()
774
795 function &prepare($query, $types = null, $result_types = null, $lobs = array())
796 {
797 if ($this->options['emulate_prepared']) {
798 $obj =& parent::prepare($query, $types, $result_types, $lobs);
799 return $obj;
800 }
801 $is_manip = ($result_types === MDB2_PREPARE_MANIP);
804 $this->offset = $this->limit = 0;
805 $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
806 if ($result) {
807 if (PEAR::isError($result)) {
808 return $result;
809 }
810 $query = $result;
811 }
812 $pgtypes = function_exists('pg_prepare') ? false : array();
813 if ($pgtypes !== false && !empty($types)) {
814 $this->loadModule('Datatype', null, true);
815 }
816 $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
817 $placeholder_type_guess = $placeholder_type = null;
818 $question = '?';
819 $colon = ':';
820 $positions = array();
821 $position = $parameter = 0;
822 while ($position < strlen($query)) {
823 $q_position = strpos($query, $question, $position);
824 $c_position = strpos($query, $colon, $position);
825 if ($q_position && $c_position) {
826 $p_position = min($q_position, $c_position);
827 } elseif ($q_position) {
828 $p_position = $q_position;
829 } elseif ($c_position) {
830 $p_position = $c_position;
831 } else {
832 break;
833 }
834 if (is_null($placeholder_type)) {
835 $placeholder_type_guess = $query[$p_position];
836 }
837
838 $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
839 if (PEAR::isError($new_pos)) {
840 return $new_pos;
841 }
842 if ($new_pos != $position) {
843 $position = $new_pos;
844 continue; //evaluate again starting from the new position
845 }
846
847 if ($query[$position] == $placeholder_type_guess) {
848 if (is_null($placeholder_type)) {
849 $placeholder_type = $query[$p_position];
850 $question = $colon = $placeholder_type;
851 if (!empty($types) && is_array($types)) {
852 if ($placeholder_type == ':') {
853 } else {
854 $types = array_values($types);
855 }
856 }
857 }
858 if ($placeholder_type_guess == '?') {
859 $length = 1;
860 $name = $parameter;
861 } else {
862 $name = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query);
863 if ($name === '') {
864 $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
865 'named parameter with an empty name', __FUNCTION__);
866 return $err;
867 }
868 $length = strlen($name) + 1;
869 }
870 if ($pgtypes !== false) {
871 if (is_array($types) && array_key_exists($name, $types)) {
872 $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$name]);
873 } elseif (is_array($types) && array_key_exists($parameter, $types)) {
874 $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$parameter]);
875 } else {
876 $pgtypes[] = 'text';
877 }
878 }
879 if (($key_parameter = array_search($name, $positions))) {
880 $next_parameter = 1;
881 foreach ($positions as $key => $value) {
882 if ($key_parameter == $key) {
883 break;
884 }
885 ++$next_parameter;
886 }
887 } else {
888 ++$parameter;
889 $next_parameter = $parameter;
890 $positions[] = $name;
891 }
892 $query = substr_replace($query, '$'.$parameter, $position, $length);
893 $position = $p_position + strlen($parameter);
894 } else {
895 $position = $p_position;
896 }
897 }
898 $connection = $this->getConnection();
900 return $connection;
901 }
902
903 $statement_name = sprintf($this->options['statement_format'], $this->phptype, md5(time() + rand()));
904 $statement_name = strtolower($statement_name);
905 if ($pgtypes === false) {
906 $result = @pg_prepare($connection, $statement_name, $query);
907 if (!$result) {
908 $err =& $this->raiseError(null, null, null,
909 'Unable to create prepared statement handle', __FUNCTION__);
910 return $err;
911 }
912 } else {
913 $types_string = '';
914 if ($pgtypes) {
915 $types_string = ' ('.implode(', ', $pgtypes).') ';
916 }
917 $query = 'PREPARE '.$statement_name.$types_string.' AS '.$query;
918 $statement =& $this->_doQuery($query, true, $connection);
919 if (PEAR::isError($statement)) {
920 return $statement;
921 }
922 }
923
924 $class_name = 'MDB2_Statement_'.$this->phptype;
925 $obj =& new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
926 $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
927 return $obj;
928 }
929
930 // }}}
931 // {{{ function getSequenceName($sqn)
932
942 function getSequenceName($sqn)
943 {
944 list($table, $field) = explode('_', $sqn);
945 $query = "SELECT substring((SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
946 FROM pg_attrdef d
947 WHERE d.adrelid = a.attrelid
948 AND d.adnum = a.attnum
949 AND a.atthasdef
950 ) FROM 'nextval[^\']*\'([^\']*)')
951 FROM pg_attribute a
952 LEFT JOIN pg_class c ON c.oid = a.attrelid
953 LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef
954 WHERE (c.relname = ".$this->quote($sqn, 'text');
955 if (!empty($field)) {
956 $query .= " OR (c.relname = ".$this->quote($table, 'text')." AND a.attname = ".$this->quote($field, 'text').")";
957 }
958 $query .= " )
959 AND NOT a.attisdropped
960 AND a.attnum > 0
961 AND pg_get_expr(d.adbin, d.adrelid) LIKE 'nextval%'
962 ORDER BY a.attnum";
963 $seqname = $this->queryOne($query);
964 if (!PEAR::isError($seqname) && !empty($seqname) && is_string($seqname)) {
965 return $seqname;
966 }
967
968 return sprintf($this->options['seqname_format'],
969 preg_replace('/[^\w\$.]/i', '_', $sqn));
970 }
971
972 // }}}
973 // {{{ nextID()
974
985 function nextID($seq_name, $ondemand = true)
986 {
987 $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
988 $query = "SELECT NEXTVAL('$sequence_name')";
990 $result = $this->queryOne($query, 'integer');
991 $this->popExpect();
992 if (PEAR::isError($result)) {
993 if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) {
994 $this->loadModule('Manager', null, true);
995 $result = $this->manager->createSequence($seq_name);
996 if (PEAR::isError($result)) {
997 return $this->raiseError($result, null, null,
998 'on demand sequence could not be created', __FUNCTION__);
999 }
1000 return $this->nextId($seq_name, false);
1001 }
1002 }
1003 return $result;
1004 }
1005
1006 // }}}
1007 // {{{ lastInsertID()
1008
1018 function lastInsertID($table = null, $field = null)
1019 {
1020 if (empty($table) && empty($field)) {
1021 return $this->queryOne('SELECT lastval()', 'integer');
1022 }
1023 $seq = $table.(empty($field) ? '' : '_'.$field);
1024 $sequence_name = $this->getSequenceName($seq);
1025 return $this->queryOne("SELECT currval('$sequence_name')", 'integer');
1026 }
1027
1028 // }}}
1029 // {{{ currID()
1030
1038 function currID($seq_name)
1039 {
1040 $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true);
1041 return $this->queryOne("SELECT last_value FROM $sequence_name", 'integer');
1042 }
1043}
1044
1053{
1054 // }}}
1055 // {{{ fetchRow()
1056
1065 function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
1066 {
1067 if (!is_null($rownum)) {
1068 $seek = $this->seek($rownum);
1069 if (PEAR::isError($seek)) {
1070 return $seek;
1071 }
1072 }
1073 if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
1074 $fetchmode = $this->db->fetchmode;
1075 }
1076 if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
1077 $row = @pg_fetch_array($this->result, null, PGSQL_ASSOC);
1078 if (is_array($row)
1079 && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
1080 ) {
1081 $row = array_change_key_case($row, $this->db->options['field_case']);
1082 }
1083 } else {
1084 $row = @pg_fetch_row($this->result);
1085 }
1086 if (!$row) {
1087 if ($this->result === false) {
1088 $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1089 'resultset has already been freed', __FUNCTION__);
1090 return $err;
1091 }
1092 $null = null;
1093 return $null;
1094 }
1095 $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
1096 $rtrim = false;
1097 if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
1098 if (empty($this->types)) {
1099 $mode += MDB2_PORTABILITY_RTRIM;
1100 } else {
1101 $rtrim = true;
1102 }
1103 }
1104 if ($mode) {
1105 $this->db->_fixResultArrayValues($row, $mode);
1106 }
1107 if (!empty($this->types)) {
1108 $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
1109 }
1110 if (!empty($this->values)) {
1111 $this->_assignBindColumns($row);
1112 }
1113 if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
1114 $object_class = $this->db->options['fetch_class'];
1115 if ($object_class == 'stdClass') {
1116 $row = (object) $row;
1117 } else {
1118 $row = &new $object_class($row);
1119 }
1120 }
1121 ++$this->rownum;
1122 return $row;
1123 }
1124
1125 // }}}
1126 // {{{ _getColumnNames()
1127
1138 {
1139 $columns = array();
1140 $numcols = $this->numCols();
1141 if (PEAR::isError($numcols)) {
1142 return $numcols;
1143 }
1144 for ($column = 0; $column < $numcols; $column++) {
1145 $column_name = @pg_field_name($this->result, $column);
1146 $columns[$column_name] = $column;
1147 }
1148 if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
1149 $columns = array_change_key_case($columns, $this->db->options['field_case']);
1150 }
1151 return $columns;
1152 }
1153
1154 // }}}
1155 // {{{ numCols()
1156
1164 function numCols()
1165 {
1166 $cols = @pg_num_fields($this->result);
1167 if (is_null($cols)) {
1168 if ($this->result === false) {
1169 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1170 'resultset has already been freed', __FUNCTION__);
1171 } elseif (is_null($this->result)) {
1172 return count($this->types);
1173 }
1174 return $this->db->raiseError(null, null, null,
1175 'Could not get column count', __FUNCTION__);
1176 }
1177 return $cols;
1178 }
1179
1180 // }}}
1181 // {{{ nextResult()
1182
1189 function nextResult()
1190 {
1191 $connection = $this->db->getConnection();
1192 if (PEAR::isError($connection)) {
1193 return $connection;
1194 }
1195
1196 if (!($this->result = @pg_get_result($connection))) {
1197 return false;
1198 }
1199 return MDB2_OK;
1200 }
1201
1202 // }}}
1203 // {{{ free()
1204
1211 function free()
1212 {
1213 if (is_resource($this->result) && $this->db->connection) {
1214 $free = @pg_free_result($this->result);
1215 if ($free === false) {
1216 return $this->db->raiseError(null, null, null,
1217 'Could not free result', __FUNCTION__);
1218 }
1219 }
1220 $this->result = false;
1221 return MDB2_OK;
1222 }
1223}
1224
1233{
1234 // {{{ seek()
1235
1243 function seek($rownum = 0)
1244 {
1245 if ($this->rownum != ($rownum - 1) && !@pg_result_seek($this->result, $rownum)) {
1246 if ($this->result === false) {
1247 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1248 'resultset has already been freed', __FUNCTION__);
1249 } elseif (is_null($this->result)) {
1250 return MDB2_OK;
1251 }
1252 return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
1253 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
1254 }
1255 $this->rownum = $rownum - 1;
1256 return MDB2_OK;
1257 }
1258
1259 // }}}
1260 // {{{ valid()
1261
1268 function valid()
1269 {
1270 $numrows = $this->numRows();
1271 if (PEAR::isError($numrows)) {
1272 return $numrows;
1273 }
1274 return $this->rownum < ($numrows - 1);
1275 }
1276
1277 // }}}
1278 // {{{ numRows()
1279
1286 function numRows()
1287 {
1288 $rows = @pg_num_rows($this->result);
1289 if (is_null($rows)) {
1290 if ($this->result === false) {
1291 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
1292 'resultset has already been freed', __FUNCTION__);
1293 } elseif (is_null($this->result)) {
1294 return 0;
1295 }
1296 return $this->db->raiseError(null, null, null,
1297 'Could not get row count', __FUNCTION__);
1298 }
1299 return $rows;
1300 }
1301}
1302
1311{
1312 // {{{ _execute()
1313
1322 function &_execute($result_class = true, $result_wrap_class = false)
1323 {
1324 if (is_null($this->statement)) {
1325 $result =& parent::_execute($result_class, $result_wrap_class);
1326 return $result;
1327 }
1328 $this->db->last_query = $this->query;
1329 $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values));
1330 if ($this->db->getOption('disable_query')) {
1331 $result = $this->is_manip ? 0 : null;
1332 return $result;
1333 }
1334
1335 $connection = $this->db->getConnection();
1336 if (PEAR::isError($connection)) {
1337 return $connection;
1338 }
1339
1340 $query = false;
1341 $parameters = array();
1342 // todo: disabled until pg_execute() bytea issues are cleared up
1343 if (true || !function_exists('pg_execute')) {
1344 $query = 'EXECUTE '.$this->statement;
1345 }
1346 if (!empty($this->positions)) {
1347 foreach ($this->positions as $parameter) {
1348 if (!array_key_exists($parameter, $this->values)) {
1349 return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
1350 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
1351 }
1352 $value = $this->values[$parameter];
1353 $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
1354 if (is_resource($value) || $type == 'clob' || $type == 'blob') {
1355 if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1356 if ($match[1] == 'file://') {
1357 $value = $match[2];
1358 }
1359 $value = @fopen($value, 'r');
1360 $close = true;
1361 }
1362 if (is_resource($value)) {
1363 $data = '';
1364 while (!@feof($value)) {
1365 $data.= @fread($value, $this->db->options['lob_buffer_length']);
1366 }
1367 if ($close) {
1368 @fclose($value);
1369 }
1370 $value = $data;
1371 }
1372 }
1373 $parameters[] = $this->db->quote($value, $type, $query);
1374 }
1375 if ($query) {
1376 $query.= ' ('.implode(', ', $parameters).')';
1377 }
1378 }
1379
1380 if (!$query) {
1381 $result = @pg_execute($connection, $this->statement, $parameters);
1382 if (!$result) {
1383 $err =& $this->db->raiseError(null, null, null,
1384 'Unable to execute statement', __FUNCTION__);
1385 return $err;
1386 }
1387 } else {
1388 $result = $this->db->_doQuery($query, $this->is_manip, $connection);
1389 if (PEAR::isError($result)) {
1390 return $result;
1391 }
1392 }
1393
1394 if ($this->is_manip) {
1395 $affected_rows = $this->db->_affectedRows($connection, $result);
1396 return $affected_rows;
1397 }
1398
1399 $result =& $this->db->_wrapResult($result, $this->result_types,
1400 $result_class, $result_wrap_class, $this->limit, $this->offset);
1401 $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result));
1402 return $result;
1403 }
1404
1405 // }}}
1406 // {{{ free()
1407
1414 function free()
1415 {
1416 if (is_null($this->positions)) {
1417 return $this->db->raiseError(MDB2_ERROR, null, null,
1418 'Prepared statement has already been freed', __FUNCTION__);
1419 }
1420 $result = MDB2_OK;
1421
1422 if (!is_null($this->statement)) {
1423 $connection = $this->db->getConnection();
1424 if (PEAR::isError($connection)) {
1425 return $connection;
1426 }
1427 $query = 'DEALLOCATE PREPARE '.$this->statement;
1428 $result = $this->db->_doQuery($query, true, $connection);
1429 }
1430
1431 parent::free();
1432 return $result;
1433 }
1434}
1435?>
$result
const MDB2_ERROR_ACCESS_VIOLATION
Definition: MDB2.php:94
const MDB2_ERROR_UNSUPPORTED
Definition: MDB2.php:73
const MDB2_PORTABILITY_RTRIM
Portability: right trim the data output by query*() and fetch*().
Definition: MDB2.php:164
const MDB2_FETCHMODE_DEFAULT
This is a special constant that tells MDB2 the user hasn't specified any particular get mode,...
Definition: MDB2.php:119
const MDB2_ERROR_INVALID
Definition: MDB2.php:75
const MDB2_ERROR_CONSTRAINT_NOT_NULL
Definition: MDB2.php:96
const MDB2_OK
The method mapErrorCode in each MDB2_dbtype implementation maps native error codes to one of these.
Definition: MDB2.php:67
const MDB2_PORTABILITY_FIX_CASE
Portability: convert names of tables and fields to case defined in the "field_case" option when using...
Definition: MDB2.php:158
const MDB2_ERROR_SYNTAX
Definition: MDB2.php:69
const MDB2_ERROR_NOT_FOUND
Definition: MDB2.php:71
const MDB2_ERROR_VALUE_COUNT_ON_ROW
Definition: MDB2.php:89
const MDB2_ERROR_INVALID_NUMBER
Definition: MDB2.php:78
const MDB2_PREPARE_MANIP
These are just helper constants to more verbosely express parameters to prepare()
Definition: MDB2.php:109
const MDB2_ERROR_ALREADY_EXISTS
Definition: MDB2.php:72
const MDB2_PORTABILITY_EMPTY_TO_NULL
Portability: convert empty values to null strings in data output by query*() and fetch*().
Definition: MDB2.php:198
const MDB2_ERROR_DIVZERO
Definition: MDB2.php:80
const MDB2_ERROR_CONSTRAINT
Definition: MDB2.php:70
const MDB2_FETCHMODE_OBJECT
Column data as object properties.
Definition: MDB2.php:134
const MDB2_ERROR_NOSUCHFIELD
Definition: MDB2.php:86
const MDB2_ERROR_NEED_MORE_DATA
Definition: MDB2.php:87
const MDB2_ERROR
Definition: MDB2.php:68
const MDB2_ERROR_NOSUCHTABLE
Definition: MDB2.php:85
const MDB2_ERROR_CONNECT_FAILED
Definition: MDB2.php:91
const MDB2_FETCHMODE_ASSOC
Column data indexed by column names.
Definition: MDB2.php:129
if(! $in) $columns
Definition: Utf8Test.php:46
numRows()
Returns the number of rows in a result object.
Definition: pgsql.php:1286
seek($rownum=0)
Seek to a specific row in a result set.
Definition: pgsql.php:1243
valid()
Check if the end of the result set has been reached.
Definition: pgsql.php:1268
getConnection()
Returns a native connection.
Definition: MDB2.php:1733
debug($message, $scope='', $context=array())
set a debug message
Definition: MDB2.php:1582
queryOne($query, $type=null, $colnum=0)
Execute the specified query, fetch the value from the first column of the first row of the result set...
Definition: MDB2.php:3203
& _wrapResult($result, $types=array(), $result_class=true, $result_wrap_class=false, $limit=null, $offset=null)
wrap a result set into the correct class
Definition: MDB2.php:2541
& loadModule($module, $property=null, $phptype_specific=null)
loads a module
Definition: MDB2.php:1840
quoteIdentifier($str, $check_option=false)
Quote a string so it can be safely used as a table or column name.
Definition: MDB2.php:1700
escapePattern($text)
Quotes pattern (% and _) characters in a string)
Definition: MDB2.php:1652
_skipDelimitedStrings($query, $position, $p_position)
Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
Definition: MDB2.php:2963
quote($value, $type=null, $quote=true, $escape_wildcards=false)
Convert a text value into a DBMS specific format that is suitable to compose query statements.
Definition: MDB2.php:3009
& raiseError($code=null, $mode=null, $options=null, $userinfo=null, $method=null)
This method is used to communicate an error and invoke error callbacks etc.
Definition: MDB2.php:1407
currID($seq_name)
Returns the current id of a sequence.
Definition: pgsql.php:1038
_doConnect($database_name, $persistent=false)
Does the grunt work of connecting to the database.
Definition: pgsql.php:360
errorInfo($error=null)
This method is used to collect information about an error.
Definition: pgsql.php:107
rollback($savepoint=null)
Cancel any database changes done during a transaction or since a specific savepoint that is in progre...
Definition: pgsql.php:296
__construct()
Constructor.
Definition: pgsql.php:67
getServerVersion($native=false)
return version information about the server
Definition: pgsql.php:733
_affectedRows($connection, $result=null)
Returns the number of rows affected.
Definition: pgsql.php:650
_modifyQuery($query, $is_manip, $limit, $offset)
Changes a query string for various DBMS specific reasons.
Definition: pgsql.php:674
setTransactionIsolation($isolation)
Set the transacton isolation level.
Definition: pgsql.php:333
setCharset($charset, $connection=null)
Set the charset on the current connection.
Definition: pgsql.php:494
nextID($seq_name, $ondemand=true)
Returns the next free id of a sequence.
Definition: pgsql.php:985
escape($text, $escape_wildcards=false)
Quotes a string so it can be safely used in a query.
Definition: pgsql.php:195
lastInsertID($table=null, $field=null)
Returns the autoincrement ID if supported or $id or fetches the current ID in a sequence called: $tab...
Definition: pgsql.php:1018
_modifyManipQuery($query, $limit)
Changes a manip query string for various DBMS specific reasons.
Definition: pgsql.php:703
beginTransaction($savepoint=null)
Start a transaction or set a savepoint.
Definition: pgsql.php:223
commit($savepoint=null)
Commit the database changes done during a transaction that is in progress or release a savepoint.
Definition: pgsql.php:262
& prepare($query, $types=null, $result_types=null, $lobs=array())
Prepares a query for multiple execution with execute().
Definition: pgsql.php:795
connect()
Connect to the database.
Definition: pgsql.php:452
disconnect($force=true)
Log out and disconnect from the database.
Definition: pgsql.php:523
& _doQuery($query, $is_manip=false, $connection=null, $database_name=null)
Execute a query.
Definition: pgsql.php:599
getSequenceName($sqn)
adds sequence name formatting to a sequence name
Definition: pgsql.php:942
& standaloneQuery($query, $types=null, $is_manip=false)
execute a query as DBA
Definition: pgsql.php:559
_assignBindColumns($row)
Bind a variable to a value in the result row.
Definition: MDB2.php:3786
seek($rownum=0)
Seek to a specific row in a result set.
Definition: MDB2.php:3432
free()
Free the internal resources associated with result.
Definition: pgsql.php:1211
& fetchRow($fetchmode=MDB2_FETCHMODE_DEFAULT, $rownum=null)
Fetch a row and insert the data into an existing array.
Definition: pgsql.php:1065
_getColumnNames()
Retrieve the names of columns returned by the DBMS in a query result.
Definition: pgsql.php:1137
numCols()
Count the number of columns returned by the DBMS in a query result.
Definition: pgsql.php:1164
nextResult()
Move the internal result pointer to the next available result.
Definition: pgsql.php:1189
free()
Release resources allocated for the specified prepared query.
Definition: pgsql.php:1414
& _execute($result_class=true, $result_wrap_class=false)
Execute a prepared query statement helper method.
Definition: pgsql.php:1322
loadExtension($ext)
OS independant PHP extension load.
Definition: PEAR.php:745
popExpect()
This method pops one element off the expected error codes stack.
Definition: PEAR.php:409
expectError($code=' *')
This method is used to tell which errors you expect to get.
Definition: PEAR.php:390
isError($data, $code=null)
Tell whether a value is a PEAR error.
Definition: PEAR.php:279
$data
$text
$params
Definition: example_049.php:96
$code
Definition: example_050.php:99