Merge branch 'MDL-68067' of https://github.com/Chocolate-lightning/moodle
[moodle.git] / lib / dml / pgsql_native_moodle_recordset.php
blobd6ef4113fd16fb449063886ccf87e5212470abfb
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
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.
8 //
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/>.
17 /**
18 * Native postgresql recordset.
20 * @package core_dml
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');
29 /**
30 * pgsql specific moodle recordset class
32 * @package core_dml
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 */
39 protected $result;
40 /** @var current row as array.*/
41 protected $current;
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 */
48 protected $db;
50 /** @var bool True if there are no more rows to fetch from the cursor */
51 protected $lastbatch;
53 /**
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;
67 $this->db = $db;
68 $this->cursorname = $cursorname;
70 // When there is a cursor, do the initial fetch.
71 if ($cursorname) {
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();
87 /**
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() {
93 if ($this->result) {
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);
97 if (!$this->result) {
98 throw new coding_exception('Unexpected failure when fetching from cursor');
102 public function __destruct() {
103 $this->close();
106 private function fetch_next() {
107 if (!$this->result) {
108 return false;
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;
121 return false;
123 } else {
124 return false;
128 if ($this->blobs) {
129 foreach ($this->blobs as $blob) {
130 $row[$blob] = $row[$blob] !== null ? pg_unescape_bytea($row[$blob]) : null;
134 return $row;
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) {
145 return false;
147 $key = reset($this->current);
148 return $key;
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() {
160 if ($this->result) {
161 pg_free_result($this->result);
162 $this->result = null;
164 $this->current = null;
165 $this->blobs = null;
167 // If using cursors, close the cursor.
168 if ($this->cursorname) {
169 $this->db->close_cursor($this->cursorname);
170 $this->cursorname = null;