xref: /PHP-8.1/ext/mysqlnd/mysqlnd_driver.c (revision 01b3fc03)
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   | https://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 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 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 	mysqlnd_error_info_init(&data->error_info_impl, persistent);
120 	data->error_info = &data->error_info_impl;
121 
122 	data->options = &(data->options_impl);
123 
124 	mysqlnd_upsert_status_init(&data->upsert_status_impl);
125 	data->upsert_status = &(data->upsert_status_impl);
126 	UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(data->upsert_status);
127 
128 	data->persistent = persistent;
129 	data->m = mysqlnd_conn_data_get_methods();
130 	data->object_factory = *factory;
131 
132 	mysqlnd_connection_state_init(&data->state);
133 
134 	data->m->get_reference(data);
135 
136 	mysqlnd_stats_init(&data->stats, STAT_LAST, persistent);
137 
138 	data->protocol_frame_codec = mysqlnd_pfc_init(persistent, factory, data->stats, data->error_info);
139 	data->vio = mysqlnd_vio_init(persistent, factory, data->stats, data->error_info);
140 	data->payload_decoder_factory = mysqlnd_protocol_payload_decoder_factory_init(data, persistent);
141 	data->command = mysqlnd_command_get_methods();
142 
143 	if (!data->protocol_frame_codec || !data->vio || !data->payload_decoder_factory || !data->command) {
144 		new_object->m->dtor(new_object);
145 		DBG_RETURN(NULL);
146 	}
147 
148 	DBG_RETURN(new_object);
149 }
150 /* }}} */
151 
152 
153 /* {{{ mysqlnd_object_factory::clone_connection_object */
154 static MYSQLND *
MYSQLND_METHOD(mysqlnd_object_factory,clone_connection_object)155 MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object)(MYSQLND * to_be_cloned)
156 {
157 	const size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
158 	MYSQLND * new_object;
159 
160 	DBG_ENTER("mysqlnd_driver::clone_connection_object");
161 	DBG_INF_FMT("persistent=%u", to_be_cloned->persistent);
162 	if (!to_be_cloned || !to_be_cloned->data) {
163 		DBG_RETURN(NULL);
164 	}
165 	new_object = mnd_pecalloc(1, alloc_size_ret, to_be_cloned->persistent);
166 	if (!new_object) {
167 		DBG_RETURN(NULL);
168 	}
169 	new_object->persistent = to_be_cloned->persistent;
170 	new_object->m = to_be_cloned->m;
171 
172 	new_object->data = to_be_cloned->data->m->get_reference(to_be_cloned->data);
173 	if (!new_object->data) {
174 		new_object->m->dtor(new_object);
175 		new_object = NULL;
176 	}
177 	DBG_RETURN(new_object);
178 }
179 /* }}} */
180 
181 
182 /* {{{ mysqlnd_object_factory::get_prepared_statement */
183 static MYSQLND_STMT *
MYSQLND_METHOD(mysqlnd_object_factory,get_prepared_statement)184 MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement)(MYSQLND_CONN_DATA * const conn)
185 {
186 	const size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
187 	MYSQLND_STMT * ret = mnd_ecalloc(1, alloc_size);
188 	MYSQLND_STMT_DATA * stmt = NULL;
189 
190 	DBG_ENTER("mysqlnd_object_factory::get_prepared_statement");
191 	ret->m = mysqlnd_stmt_get_methods();
192 
193 	stmt = ret->data = mnd_ecalloc(1, sizeof(MYSQLND_STMT_DATA));
194 	DBG_INF_FMT("stmt=%p", stmt);
195 
196 	mysqlnd_error_info_init(&stmt->error_info_impl, 0);
197 	stmt->error_info = &stmt->error_info_impl;
198 
199 	mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
200 	stmt->upsert_status = &(stmt->upsert_status_impl);
201 	stmt->state = MYSQLND_STMT_INITTED;
202 	stmt->execute_cmd_buffer.length = 4096;
203 	stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length);
204 	stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
205 
206 	/*
207 	  Mark that we reference the connection, thus it won't be
208 	  be destructed till there is open statements. The last statement
209 	  or normal query result will close it then.
210 	*/
211 	stmt->conn = conn->m->get_reference(conn);
212 
213 	DBG_RETURN(ret);
214 }
215 /* }}} */
216 
217 
218 /* {{{ mysqlnd_object_factory::get_pfc */
219 static MYSQLND_PFC *
MYSQLND_METHOD(mysqlnd_object_factory,get_pfc)220 MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
221 {
222 	const size_t pfc_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *));
223 	const size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
224 	MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size + pfc_data_alloc_size, persistent);
225 
226 	DBG_ENTER("mysqlnd_object_factory::get_pfc");
227 	DBG_INF_FMT("persistent=%u", persistent);
228 	if (pfc) {
229 		pfc->data = (MYSQLND_PFC_DATA*)((char*)pfc + pfc_alloc_size);
230 		pfc->persistent = pfc->data->persistent = persistent;
231 		pfc->data->m = *mysqlnd_pfc_get_methods();
232 
233 		if (PASS != pfc->data->m.init(pfc, stats, error_info)) {
234 			pfc->data->m.dtor(pfc, stats, error_info);
235 			pfc = NULL;
236 		}
237 	}
238 	DBG_RETURN(pfc);
239 }
240 /* }}} */
241 
242 
243 /* {{{ mysqlnd_object_factory::get_vio */
244 static MYSQLND_VIO *
MYSQLND_METHOD(mysqlnd_object_factory,get_vio)245 MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
246 {
247 	const size_t vio_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *));
248 	const size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
249 	MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size + vio_data_alloc_size, persistent);
250 
251 	DBG_ENTER("mysqlnd_object_factory::get_vio");
252 	DBG_INF_FMT("persistent=%u", persistent);
253 	if (vio) {
254 		vio->data = (MYSQLND_VIO_DATA*)((char*)vio + vio_alloc_size);
255 		vio->persistent = vio->data->persistent = persistent;
256 		vio->data->m = *mysqlnd_vio_get_methods();
257 
258 		if (PASS != vio->data->m.init(vio, stats, error_info)) {
259 			vio->data->m.dtor(vio, stats, error_info);
260 			vio = NULL;
261 		}
262 	}
263 	DBG_RETURN(vio);
264 }
265 /* }}} */
266 
267 
268 /* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
269 static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
MYSQLND_METHOD(mysqlnd_object_factory,get_protocol_payload_decoder_factory)270 MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const bool persistent)
271 {
272 	const size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
273 	MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
274 
275 	DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
276 	DBG_INF_FMT("persistent=%u", persistent);
277 	if (ret) {
278 		ret->persistent = persistent;
279 		ret->conn = conn;
280 		ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
281 	}
282 
283 	DBG_RETURN(ret);
284 }
285 /* }}} */
286 
287 
288 PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
289 	MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
290 	MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
291 	MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
292 	MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
293 	MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
294 	MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
295 MYSQLND_CLASS_METHODS_END;
296