2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Native postgresql recordset.
21 * @copyright 2008 Petr Skoda (http://skodak.org)
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
27 require_once(__DIR__
.'/moodle_recordset.php');
30 * pgsql specific moodle recordset class
33 * @copyright 2008 Petr Skoda (http://skodak.org)
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class pgsql_native_moodle_recordset
extends moodle_recordset
{
38 /** @var PgSql\Result|resource|null */
40 /** @var current row as array.*/
42 protected $blobs = array();
44 /** @var string Name of cursor or '' if none */
45 protected $cursorname;
47 /** @var pgsql_native_moodle_database Postgres database resource */
50 /** @var bool True if there are no more rows to fetch from the cursor */
54 * Build a new recordset to iterate over.
56 * When using cursors, $result will be null initially.
58 * @param resource|PgSql\Result|null $result A pg_query() result object to create a recordset from.
59 * @param pgsql_native_moodle_database $db Database object (only required when using cursors)
60 * @param string $cursorname Name of cursor or '' if none
62 public function __construct($result, pgsql_native_moodle_database
$db = null, $cursorname = '') {
63 if ($cursorname && !$db) {
64 throw new coding_exception('When specifying a cursor, $db is required');
66 $this->result
= $result;
68 $this->cursorname
= $cursorname;
70 // When there is a cursor, do the initial fetch.
72 $this->fetch_cursor_block();
75 // Find out if there are any blobs.
76 $numfields = pg_num_fields($this->result
);
77 for ($i = 0; $i < $numfields; $i++
) {
78 $type = $this->db
->pg_field_type($this->result
, $i);
79 if ($type == 'bytea') {
80 $this->blobs
[] = pg_field_name($this->result
, $i);
84 $this->current
= $this->fetch_next();
88 * Fetches the next block of data when using cursors.
90 * @throws coding_exception If you call this when the fetch buffer wasn't freed yet
92 protected function fetch_cursor_block() {
94 throw new coding_exception('Unexpected non-empty result when fetching from cursor');
96 list($this->result
, $this->lastbatch
) = $this->db
->fetch_from_cursor($this->cursorname
);
98 throw new coding_exception('Unexpected failure when fetching from cursor');
102 public function __destruct() {
106 private function fetch_next() {
107 if (!$this->result
) {
110 if (!$row = pg_fetch_assoc($this->result
)) {
111 // There are no more rows in this result.
112 pg_free_result($this->result
);
113 $this->result
= null;
115 // If using a cursor, can we fetch the next block?
116 if ($this->cursorname
&& !$this->lastbatch
) {
117 $this->fetch_cursor_block();
118 if (!$row = pg_fetch_assoc($this->result
)) {
119 pg_free_result($this->result
);
120 $this->result
= null;
129 foreach ($this->blobs
as $blob) {
130 $row[$blob] = $row[$blob] !== null ?
pg_unescape_bytea($row[$blob]) : null;
137 public function current(): stdClass
{
138 return (object)$this->current
;
141 #[\ReturnTypeWillChange]
142 public function key() {
143 // return first column value as key
144 if (!$this->current
) {
147 $key = reset($this->current
);
151 public function next(): void
{
152 $this->current
= $this->fetch_next();
155 public function valid(): bool {
156 return !empty($this->current
);
159 public function close() {
161 pg_free_result($this->result
);
162 $this->result
= null;
164 $this->current
= null;
167 // If using cursors, close the cursor.
168 if ($this->cursorname
) {
169 $this->db
->close_cursor($this->cursorname
);
170 $this->cursorname
= null;