xref: /PHP-7.4/ext/mysqlnd/mysqlnd_driver.c (revision c245898b)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) The PHP Group                                          |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Andrey Hristov <andrey@php.net>                             |
16   |          Ulf Wendel <uw@php.net>                                     |
17   +----------------------------------------------------------------------+
18 */
19 
20 #include "php.h"
21 #include "mysqlnd.h"
22 #include "mysqlnd_vio.h"
23 #include "mysqlnd_protocol_frame_codec.h"
24 #include "mysqlnd_wireprotocol.h"
25 #include "mysqlnd_connection.h"
26 #include "mysqlnd_ps.h"
27 #include "mysqlnd_plugin.h"
28 #include "mysqlnd_priv.h"
29 #include "mysqlnd_statistics.h"
30 #include "mysqlnd_debug.h"
31 #include "mysqlnd_reverse_api.h"
32 #include "mysqlnd_ext_plugin.h"
33 
34 static zend_bool mysqlnd_library_initted = FALSE;
35 
36 static struct st_mysqlnd_plugin_core mysqlnd_plugin_core =
37 {
38 	{
39 		MYSQLND_PLUGIN_API_VERSION,
40 		"mysqlnd",
41 		MYSQLND_VERSION_ID,
42 		PHP_MYSQLND_VERSION,
43 		"PHP License 3.01",
44 		"Andrey Hristov <andrey@php.net>,  Ulf Wendel <uw@php.net>, Georg Richter <georg@php.net>",
45 		{
46 			NULL, /* will be filled later */
47 			mysqlnd_stats_values_names,
48 		},
49 		{
50 			NULL /* plugin shutdown */
51 		}
52 	}
53 };
54 
55 
56 /* {{{ mysqlnd_library_end */
mysqlnd_library_end(void)57 PHPAPI void mysqlnd_library_end(void)
58 {
59 	if (mysqlnd_library_initted == TRUE) {
60 		mysqlnd_plugin_subsystem_end();
61 		mysqlnd_stats_end(mysqlnd_global_stats, 1);
62 		mysqlnd_global_stats = NULL;
63 		mysqlnd_library_initted = FALSE;
64 		mysqlnd_reverse_api_end();
65 	}
66 }
67 /* }}} */
68 
69 
70 /* {{{ mysqlnd_library_init */
mysqlnd_library_init(void)71 PHPAPI void mysqlnd_library_init(void)
72 {
73 	if (mysqlnd_library_initted == FALSE) {
74 		mysqlnd_library_initted = TRUE;
75 		mysqlnd_conn_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn));
76 		mysqlnd_conn_data_set_methods(&MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn_data));
77 		_mysqlnd_init_ps_subsystem();
78 		/* Should be calloc, as mnd_calloc will reference LOCK_access*/
79 		mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST, 1);
80 		mysqlnd_plugin_subsystem_init();
81 		{
82 			mysqlnd_plugin_core.plugin_header.plugin_stats.values = mysqlnd_global_stats;
83 			mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_core);
84 		}
85 #if defined(MYSQLND_DBG_ENABLED) && MYSQLND_DBG_ENABLED == 1
86 		mysqlnd_example_plugin_register();
87 #endif
88 		mysqlnd_debug_trace_plugin_register();
89 		mysqlnd_register_builtin_authentication_plugins();
90 
91 		mysqlnd_reverse_api_init();
92 	}
93 }
94 /* }}} */
95 
96 
97 /* {{{ mysqlnd_object_factory::get_connection */
98 static MYSQLND *
MYSQLND_METHOD(mysqlnd_object_factory,get_connection)99 MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory, const zend_bool persistent)
100 {
101 	const size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
102 	const size_t alloc_size_ret_data = sizeof(MYSQLND_CONN_DATA) + mysqlnd_plugin_count() * sizeof(void *);
103 	MYSQLND * new_object;
104 	MYSQLND_CONN_DATA * data;
105 
106 	DBG_ENTER("mysqlnd_driver::get_connection");
107 	DBG_INF_FMT("persistent=%u", persistent);
108 	new_object = mnd_pecalloc(1, alloc_size_ret, persistent);
109 	if (!new_object) {
110 		DBG_RETURN(NULL);
111 	}
112 	new_object->data = mnd_pecalloc(1, alloc_size_ret_data, persistent);
113 	if (!new_object->data) {
114 		mnd_pefree(new_object, persistent);
115 		DBG_RETURN(NULL);
116 	}
117 	new_object->persistent = persistent;
118 	new_object->m = mysqlnd_conn_get_methods();
119 	data = new_object->data;
120 
121 	if (FAIL == mysqlnd_error_info_init(&data->error_info_impl, persistent)) {
122 		new_object->m->dtor(new_object);
123 		DBG_RETURN(NULL);
124 	}
125 	data->error_info = &data->error_info_impl;
126 
127 	data->options = &(data->options_impl);
128 
129 	mysqlnd_upsert_status_init(&data->upsert_status_impl);
130 	data->upsert_status = &(data->upsert_status_impl);
131 	UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(data->upsert_status);
132 
133 	data->persistent = persistent;
134 	data->m = mysqlnd_conn_data_get_methods();
135 	data->object_factory = *factory;
136 
137 	mysqlnd_connection_state_init(&data->state);
138 
139 	data->m->get_reference(data);
140 
141 	mysqlnd_stats_init(&data->stats, STAT_LAST, persistent);
142 
143 	data->protocol_frame_codec = mysqlnd_pfc_init(persistent, factory, data->stats, data->error_info);
144 	data->vio = mysqlnd_vio_init(persistent, factory, data->stats, data->error_info);
145 	data->payload_decoder_factory = mysqlnd_protocol_payload_decoder_factory_init(data, persistent);
146 	data->command = mysqlnd_command_get_methods();
147 
148 	if (!data->protocol_frame_codec || !data->vio || !data->payload_decoder_factory || !data->command) {
149 		new_object->m->dtor(new_object);
150 		DBG_RETURN(NULL);
151 	}
152 
153 	DBG_RETURN(new_object);
154 }
155 /* }}} */
156 
157 
158 /* {{{ mysqlnd_object_factory::clone_connection_object */
159 static MYSQLND *
MYSQLND_METHOD(mysqlnd_object_factory,clone_connection_object)160 MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object)(MYSQLND * to_be_cloned)
161 {
162 	const size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
163 	MYSQLND * new_object;
164 
165 	DBG_ENTER("mysqlnd_driver::clone_connection_object");
166 	DBG_INF_FMT("persistent=%u", to_be_cloned->persistent);
167 	if (!to_be_cloned || !to_be_cloned->data) {
168 		DBG_RETURN(NULL);
169 	}
170 	new_object = mnd_pecalloc(1, alloc_size_ret, to_be_cloned->persistent);
171 	if (!new_object) {
172 		DBG_RETURN(NULL);
173 	}
174 	new_object->persistent = to_be_cloned->persistent;
175 	new_object->m = to_be_cloned->m;
176 
177 	new_object->data = to_be_cloned->data->m->get_reference(to_be_cloned->data);
178 	if (!new_object->data) {
179 		new_object->m->dtor(new_object);
180 		new_object = NULL;
181 	}
182 	DBG_RETURN(new_object);
183 }
184 /* }}} */
185 
186 
187 /* {{{ mysqlnd_object_factory::get_prepared_statement */
188 static MYSQLND_STMT *
MYSQLND_METHOD(mysqlnd_object_factory,get_prepared_statement)189 MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn)
190 {
191 	const size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
192 	MYSQLND_STMT * ret = mnd_ecalloc(1, alloc_size);
193 	MYSQLND_STMT_DATA * stmt = NULL;
194 
195 	DBG_ENTER("mysqlnd_object_factory::get_prepared_statement");
196 	do {
197 		if (!ret) {
198 			break;
199 		}
200 		ret->m = mysqlnd_stmt_get_methods();
201 
202 		stmt = ret->data = mnd_ecalloc(1, sizeof(MYSQLND_STMT_DATA));
203 		DBG_INF_FMT("stmt=%p", stmt);
204 		if (!stmt) {
205 			break;
206 		}
207 
208 		if (FAIL == mysqlnd_error_info_init(&stmt->error_info_impl, 0)) {
209 			break;
210 		}
211 		stmt->error_info = &stmt->error_info_impl;
212 
213 		mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
214 		stmt->upsert_status = &(stmt->upsert_status_impl);
215 		stmt->state = MYSQLND_STMT_INITTED;
216 		stmt->execute_cmd_buffer.length = 4096;
217 		stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length);
218 		if (!stmt->execute_cmd_buffer.buffer) {
219 			break;
220 		}
221 
222 		stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
223 
224 		/*
225 		  Mark that we reference the connection, thus it won't be
226 		  be destructed till there is open statements. The last statement
227 		  or normal query result will close it then.
228 		*/
229 		stmt->conn = conn->m->get_reference(conn);
230 
231 		DBG_RETURN(ret);
232 	} while (0);
233 
234 	SET_OOM_ERROR(conn->error_info);
235 	if (ret) {
236 		ret->m->dtor(ret, TRUE);
237 		ret = NULL;
238 	}
239 	DBG_RETURN(NULL);
240 }
241 /* }}} */
242 
243 
244 /* {{{ mysqlnd_object_factory::get_pfc */
245 static MYSQLND_PFC *
MYSQLND_METHOD(mysqlnd_object_factory,get_pfc)246 MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
247 {
248 	const size_t pfc_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *));
249 	const size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
250 	MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size + pfc_data_alloc_size, persistent);
251 
252 	DBG_ENTER("mysqlnd_object_factory::get_pfc");
253 	DBG_INF_FMT("persistent=%u", persistent);
254 	if (pfc) {
255 		pfc->data = (MYSQLND_PFC_DATA*)((char*)pfc + pfc_alloc_size);
256 		pfc->persistent = pfc->data->persistent = persistent;
257 		pfc->data->m = *mysqlnd_pfc_get_methods();
258 
259 		if (PASS != pfc->data->m.init(pfc, stats, error_info)) {
260 			pfc->data->m.dtor(pfc, stats, error_info);
261 			pfc = NULL;
262 		}
263 	}
264 	DBG_RETURN(pfc);
265 }
266 /* }}} */
267 
268 
269 /* {{{ mysqlnd_object_factory::get_vio */
270 static MYSQLND_VIO *
MYSQLND_METHOD(mysqlnd_object_factory,get_vio)271 MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
272 {
273 	const size_t vio_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *));
274 	const size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
275 	MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size + vio_data_alloc_size, persistent);
276 
277 	DBG_ENTER("mysqlnd_object_factory::get_vio");
278 	DBG_INF_FMT("persistent=%u", persistent);
279 	if (vio) {
280 		vio->data = (MYSQLND_VIO_DATA*)((char*)vio + vio_alloc_size);
281 		vio->persistent = vio->data->persistent = persistent;
282 		vio->data->m = *mysqlnd_vio_get_methods();
283 
284 		if (PASS != vio->data->m.init(vio, stats, error_info)) {
285 			vio->data->m.dtor(vio, stats, error_info);
286 			vio = NULL;
287 		}
288 	}
289 	DBG_RETURN(vio);
290 }
291 /* }}} */
292 
293 
294 /* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
295 static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
MYSQLND_METHOD(mysqlnd_object_factory,get_protocol_payload_decoder_factory)296 MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
297 {
298 	const size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
299 	MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
300 
301 	DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
302 	DBG_INF_FMT("persistent=%u", persistent);
303 	if (ret) {
304 		ret->persistent = persistent;
305 		ret->conn = conn;
306 		ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
307 	}
308 
309 	DBG_RETURN(ret);
310 }
311 /* }}} */
312 
313 
314 PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
315 	MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
316 	MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
317 	MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
318 	MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
319 	MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
320 	MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
321 MYSQLND_CLASS_METHODS_END;
322