rdbms: ignore DBO_NOBUFFER flag in IDatabase
[lhc/web/wiklou.git] / includes / libs / rdbms / database / DatabaseMysqli.php
1 <?php
2 /**
3 * This is the MySQLi database abstraction layer.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Database
22 */
23 namespace Wikimedia\Rdbms;
24
25 use mysqli;
26 use mysqli_result;
27 use IP;
28 use stdClass;
29
30 /**
31 * Database abstraction object for PHP extension mysqli.
32 *
33 * @ingroup Database
34 * @since 1.22
35 * @see Database
36 */
37 class DatabaseMysqli extends DatabaseMysqlBase {
38 /**
39 * @param string $sql
40 * @return mysqli_result|bool
41 */
42 protected function doQuery( $sql ) {
43 return $this->getBindingHandle()->query( $sql );
44 }
45
46 /**
47 * @param string $realServer
48 * @param string|null $dbName
49 * @return mysqli|null
50 * @throws DBConnectionError
51 */
52 protected function mysqlConnect( $realServer, $dbName ) {
53 if ( !function_exists( 'mysqli_init' ) ) {
54 throw $this->newExceptionAfterConnectError(
55 "MySQLi functions missing, have you compiled PHP with the --with-mysqli option?"
56 );
57 }
58
59 // Other than mysql_connect, mysqli_real_connect expects an explicit port
60 // and socket parameters. So we need to parse the port and socket out of
61 // $realServer
62 $port = null;
63 $socket = null;
64 $hostAndPort = IP::splitHostAndPort( $realServer );
65 if ( $hostAndPort ) {
66 $realServer = $hostAndPort[0];
67 if ( $hostAndPort[1] ) {
68 $port = $hostAndPort[1];
69 }
70 } elseif ( substr_count( $realServer, ':' ) == 1 ) {
71 // If we have a colon and something that's not a port number
72 // inside the hostname, assume it's the socket location
73 list( $realServer, $socket ) = explode( ':', $realServer, 2 );
74 }
75
76 $mysqli = mysqli_init();
77 // Make affectedRows() for UPDATE reflect the number of matching rows, regardless
78 // of whether any column values changed. This is what callers want to know and is
79 // consistent with what Postgres, SQLite, and SQL Server return.
80 $connFlags = MYSQLI_CLIENT_FOUND_ROWS;
81 if ( $this->getFlag( self::DBO_SSL ) ) {
82 $connFlags |= MYSQLI_CLIENT_SSL;
83 $mysqli->ssl_set(
84 $this->sslKeyPath,
85 $this->sslCertPath,
86 $this->sslCAFile,
87 $this->sslCAPath,
88 $this->sslCiphers
89 );
90 }
91 if ( $this->getFlag( self::DBO_COMPRESS ) ) {
92 $connFlags |= MYSQLI_CLIENT_COMPRESS;
93 }
94 if ( $this->getFlag( self::DBO_PERSISTENT ) ) {
95 $realServer = 'p:' . $realServer;
96 }
97
98 if ( $this->utf8Mode ) {
99 // Tell the server we're communicating with it in UTF-8.
100 // This may engage various charset conversions.
101 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'utf8' );
102 } else {
103 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'binary' );
104 }
105 $mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT, 3 );
106
107 if ( $mysqli->real_connect(
108 $realServer,
109 $this->user,
110 $this->password,
111 $dbName,
112 $port,
113 $socket,
114 $connFlags
115 ) ) {
116 return $mysqli;
117 }
118
119 return null;
120 }
121
122 /**
123 * @return bool
124 */
125 protected function closeConnection() {
126 $conn = $this->getBindingHandle();
127
128 return $conn->close();
129 }
130
131 /**
132 * @return int
133 */
134 function insertId() {
135 $conn = $this->getBindingHandle();
136
137 return (int)$conn->insert_id;
138 }
139
140 /**
141 * @return int
142 */
143 function lastErrno() {
144 if ( $this->conn instanceof mysqli ) {
145 return $this->conn->errno;
146 } else {
147 return mysqli_connect_errno();
148 }
149 }
150
151 /**
152 * @return int
153 */
154 protected function fetchAffectedRowCount() {
155 $conn = $this->getBindingHandle();
156
157 return $conn->affected_rows;
158 }
159
160 /**
161 * @param mysqli_result $res
162 * @return bool
163 */
164 protected function mysqlFreeResult( $res ) {
165 $res->free_result();
166
167 return true;
168 }
169
170 /**
171 * @param mysqli_result $res
172 * @return stdClass|bool
173 */
174 protected function mysqlFetchObject( $res ) {
175 $object = $res->fetch_object();
176 if ( $object === null ) {
177 return false;
178 }
179
180 return $object;
181 }
182
183 /**
184 * @param mysqli_result $res
185 * @return array|false
186 */
187 protected function mysqlFetchArray( $res ) {
188 $array = $res->fetch_array();
189 if ( $array === null ) {
190 return false;
191 }
192
193 return $array;
194 }
195
196 /**
197 * @param mysqli_result $res
198 * @return mixed
199 */
200 protected function mysqlNumRows( $res ) {
201 return $res->num_rows;
202 }
203
204 /**
205 * @param mysqli_result $res
206 * @return mixed
207 */
208 protected function mysqlNumFields( $res ) {
209 return $res->field_count;
210 }
211
212 /**
213 * @param mysqli_result $res
214 * @param int $n
215 * @return mixed
216 */
217 protected function mysqlFetchField( $res, $n ) {
218 $field = $res->fetch_field_direct( $n );
219
220 // Add missing properties to result (using flags property)
221 // which will be part of function mysql-fetch-field for backward compatibility
222 $field->not_null = $field->flags & MYSQLI_NOT_NULL_FLAG;
223 $field->primary_key = $field->flags & MYSQLI_PRI_KEY_FLAG;
224 $field->unique_key = $field->flags & MYSQLI_UNIQUE_KEY_FLAG;
225 $field->multiple_key = $field->flags & MYSQLI_MULTIPLE_KEY_FLAG;
226 $field->binary = $field->flags & MYSQLI_BINARY_FLAG;
227 $field->numeric = $field->flags & MYSQLI_NUM_FLAG;
228 $field->blob = $field->flags & MYSQLI_BLOB_FLAG;
229 $field->unsigned = $field->flags & MYSQLI_UNSIGNED_FLAG;
230 $field->zerofill = $field->flags & MYSQLI_ZEROFILL_FLAG;
231
232 return $field;
233 }
234
235 /**
236 * @param mysqli_result $res
237 * @param int $n
238 * @return mixed
239 */
240 protected function mysqlFieldName( $res, $n ) {
241 $field = $res->fetch_field_direct( $n );
242
243 return $field->name;
244 }
245
246 /**
247 * @param mysqli_result $res
248 * @param int $n
249 * @return mixed
250 */
251 protected function mysqlFieldType( $res, $n ) {
252 $field = $res->fetch_field_direct( $n );
253
254 return $field->type;
255 }
256
257 /**
258 * @param mysqli_result $res
259 * @param int $row
260 * @return mixed
261 */
262 protected function mysqlDataSeek( $res, $row ) {
263 return $res->data_seek( $row );
264 }
265
266 /**
267 * @param mysqli|null $conn Optional connection object
268 * @return string
269 */
270 protected function mysqlError( $conn = null ) {
271 if ( $conn === null ) {
272 return mysqli_connect_error();
273 } else {
274 return $conn->error;
275 }
276 }
277
278 /**
279 * Escapes special characters in a string for use in an SQL statement
280 * @param string $s
281 * @return string
282 */
283 protected function mysqlRealEscapeString( $s ) {
284 $conn = $this->getBindingHandle();
285
286 return $conn->real_escape_string( (string)$s );
287 }
288
289 /**
290 * @return mysqli
291 */
292 protected function getBindingHandle() {
293 return parent::getBindingHandle();
294 }
295 }
296
297 /**
298 * @deprecated since 1.29
299 */
300 class_alias( DatabaseMysqli::class, 'DatabaseMysqli' );