xref: /PHP-8.3/ext/mysqlnd/mysqlnd_driver.c (revision 3508b07b)
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 = MYSQLND_NET_CMD_BUFFER_MIN_SIZE;
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 		pfc->data->m.init(pfc, stats, error_info);
234 	}
235 	DBG_RETURN(pfc);
236 }
237 /* }}} */
238 
239 
240 /* {{{ mysqlnd_object_factory::get_vio */
241 static MYSQLND_VIO *
MYSQLND_METHOD(mysqlnd_object_factory,get_vio)242 MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
243 {
244 	const size_t vio_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *));
245 	const size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
246 	MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size + vio_data_alloc_size, persistent);
247 
248 	DBG_ENTER("mysqlnd_object_factory::get_vio");
249 	DBG_INF_FMT("persistent=%u", persistent);
250 	if (vio) {
251 		vio->data = (MYSQLND_VIO_DATA*)((char*)vio + vio_alloc_size);
252 		vio->persistent = vio->data->persistent = persistent;
253 		vio->data->m = *mysqlnd_vio_get_methods();
254 
255 		vio->data->m.init(vio, stats, error_info);
256 	}
257 	DBG_RETURN(vio);
258 }
259 /* }}} */
260 
261 
262 /* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
263 static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
MYSQLND_METHOD(mysqlnd_object_factory,get_protocol_payload_decoder_factory)264 MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const bool persistent)
265 {
266 	const size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
267 	MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
268 
269 	DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
270 	DBG_INF_FMT("persistent=%u", persistent);
271 	if (ret) {
272 		ret->persistent = persistent;
273 		ret->conn = conn;
274 		ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
275 	}
276 
277 	DBG_RETURN(ret);
278 }
279 /* }}} */
280 
281 
282 PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
283 	MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
284 	MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
285 	MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
286 	MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
287 	MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
288 	MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
289 MYSQLND_CLASS_METHODS_END;
290