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