xref: /PHP-7.1/ext/mysqlnd/mysqlnd_driver.c (revision 7f6387b5)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2018 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 	size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
102 	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_factory = mysqlnd_command_factory_get();
147 
148 	if (!data->protocol_frame_codec || !data->vio || !data->payload_decoder_factory || !data->command_factory) {
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 	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, const zend_bool persistent)
190 {
191 	size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
192 	MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
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 		ret->persistent = conn->persistent;
202 
203 		stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), persistent);
204 		DBG_INF_FMT("stmt=%p", stmt);
205 		if (!stmt) {
206 			break;
207 		}
208 		stmt->persistent = persistent;
209 
210 		if (FAIL == mysqlnd_error_info_init(&stmt->error_info_impl, persistent)) {
211 			break;
212 		}
213 		stmt->error_info = &stmt->error_info_impl;
214 
215 		mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
216 		stmt->upsert_status = &(stmt->upsert_status_impl);
217 		stmt->state = MYSQLND_STMT_INITTED;
218 		stmt->execute_cmd_buffer.length = 4096;
219 		stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent);
220 		if (!stmt->execute_cmd_buffer.buffer) {
221 			break;
222 		}
223 
224 		stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
225 
226 		/*
227 		  Mark that we reference the connection, thus it won't be
228 		  be destructed till there is open statements. The last statement
229 		  or normal query result will close it then.
230 		*/
231 		stmt->conn = conn->m->get_reference(conn);
232 
233 		DBG_RETURN(ret);
234 	} while (0);
235 
236 	SET_OOM_ERROR(conn->error_info);
237 	if (ret) {
238 		ret->m->dtor(ret, TRUE);
239 		ret = NULL;
240 	}
241 	DBG_RETURN(NULL);
242 }
243 /* }}} */
244 
245 
246 /* {{{ mysqlnd_object_factory::get_pfc */
247 static MYSQLND_PFC *
MYSQLND_METHOD(mysqlnd_object_factory,get_pfc)248 MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
249 {
250 	size_t pfc_alloc_size = sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *);
251 	size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
252 	MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size, persistent);
253 	MYSQLND_PFC_DATA * pfc_data = mnd_pecalloc(1, pfc_data_alloc_size, persistent);
254 
255 	DBG_ENTER("mysqlnd_object_factory::get_pfc");
256 	DBG_INF_FMT("persistent=%u", persistent);
257 	if (pfc && pfc_data) {
258 		pfc->data = pfc_data;
259 		pfc->persistent = pfc->data->persistent = persistent;
260 		pfc->data->m = *mysqlnd_pfc_get_methods();
261 
262 		if (PASS != pfc->data->m.init(pfc, stats, error_info)) {
263 			pfc->data->m.dtor(pfc, stats, error_info);
264 			pfc = NULL;
265 		}
266 	} else {
267 		if (pfc_data) {
268 			mnd_pefree(pfc_data, persistent);
269 			pfc_data = NULL;
270 		}
271 		if (pfc) {
272 			mnd_pefree(pfc, persistent);
273 			pfc = NULL;
274 		}
275 	}
276 	DBG_RETURN(pfc);
277 }
278 /* }}} */
279 
280 
281 /* {{{ mysqlnd_object_factory::get_vio */
282 static MYSQLND_VIO *
MYSQLND_METHOD(mysqlnd_object_factory,get_vio)283 MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
284 {
285 	size_t vio_alloc_size = sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *);
286 	size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
287 	MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size, persistent);
288 	MYSQLND_VIO_DATA * vio_data = mnd_pecalloc(1, vio_data_alloc_size, persistent);
289 
290 	DBG_ENTER("mysqlnd_object_factory::get_vio");
291 	DBG_INF_FMT("persistent=%u", persistent);
292 	if (vio && vio_data) {
293 		vio->data = vio_data;
294 		vio->persistent = vio->data->persistent = persistent;
295 		vio->data->m = *mysqlnd_vio_get_methods();
296 
297 		if (PASS != vio->data->m.init(vio, stats, error_info)) {
298 			vio->data->m.dtor(vio, stats, error_info);
299 			vio = NULL;
300 		}
301 	} else {
302 		if (vio_data) {
303 			mnd_pefree(vio_data, persistent);
304 			vio_data = NULL;
305 		}
306 		if (vio) {
307 			mnd_pefree(vio, persistent);
308 			vio = NULL;
309 		}
310 	}
311 	DBG_RETURN(vio);
312 }
313 /* }}} */
314 
315 
316 /* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
317 static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
MYSQLND_METHOD(mysqlnd_object_factory,get_protocol_payload_decoder_factory)318 MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const zend_bool persistent)
319 {
320 	size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
321 	MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
322 
323 	DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
324 	DBG_INF_FMT("persistent=%u", persistent);
325 	if (ret) {
326 		ret->persistent = persistent;
327 		ret->conn = conn;
328 		ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
329 	}
330 
331 	DBG_RETURN(ret);
332 }
333 /* }}} */
334 
335 
336 PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
337 	MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
338 	MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
339 	MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
340 	MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
341 	MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
342 	MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
343 MYSQLND_CLASS_METHODS_END;
344 
345 /*
346  * Local variables:
347  * tab-width: 4
348  * c-basic-offset: 4
349  * End:
350  * vim600: noet sw=4 ts=4 fdm=marker
351  * vim<600: noet sw=4 ts=4
352  */
353