xref: /php-src/ext/mysqlnd/mysqlnd_driver.c (revision a8c92700)
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 #if MYSQLND_CHARSETS_SANITY_CHECK == 1
92 		void mysqlnd_charsets_sanity_check(void);
93 		mysqlnd_charsets_sanity_check();
94 #endif
95 	}
96 }
97 /* }}} */
98 
99 
100 /* {{{ mysqlnd_object_factory::get_connection */
101 static MYSQLND *
MYSQLND_METHOD(mysqlnd_object_factory,get_connection)102 MYSQLND_METHOD(mysqlnd_object_factory, get_connection)(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory, const bool persistent)
103 {
104 	const size_t alloc_size_ret = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
105 	const size_t alloc_size_ret_data = sizeof(MYSQLND_CONN_DATA) + mysqlnd_plugin_count() * sizeof(void *);
106 	MYSQLND * new_object;
107 	MYSQLND_CONN_DATA * data;
108 
109 	DBG_ENTER("mysqlnd_driver::get_connection");
110 	DBG_INF_FMT("persistent=%u", persistent);
111 	new_object = mnd_pecalloc(1, alloc_size_ret, persistent);
112 	if (!new_object) {
113 		DBG_RETURN(NULL);
114 	}
115 	new_object->data = mnd_pecalloc(1, alloc_size_ret_data, persistent);
116 	if (!new_object->data) {
117 		mnd_pefree(new_object, persistent);
118 		DBG_RETURN(NULL);
119 	}
120 	new_object->persistent = persistent;
121 	new_object->m = mysqlnd_conn_get_methods();
122 	data = new_object->data;
123 
124 	mysqlnd_error_info_init(&data->error_info_impl, persistent);
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 	ret->m = mysqlnd_stmt_get_methods();
197 
198 	stmt = ret->data = mnd_ecalloc(1, sizeof(MYSQLND_STMT_DATA));
199 	DBG_INF_FMT("stmt=%p", stmt);
200 
201 	mysqlnd_error_info_init(&stmt->error_info_impl, 0);
202 	stmt->error_info = &stmt->error_info_impl;
203 
204 	mysqlnd_upsert_status_init(&stmt->upsert_status_impl);
205 	stmt->upsert_status = &(stmt->upsert_status_impl);
206 	stmt->state = MYSQLND_STMT_INITTED;
207 	stmt->execute_cmd_buffer.length = MYSQLND_NET_CMD_BUFFER_MIN_SIZE;
208 	stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length);
209 	stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
210 
211 	/*
212 	  Mark that we reference the connection, thus it won't be
213 	  be destructed till there is open statements. The last statement
214 	  or normal query result will close it then.
215 	*/
216 	stmt->conn = conn->m->get_reference(conn);
217 
218 	DBG_RETURN(ret);
219 }
220 /* }}} */
221 
222 
223 /* {{{ mysqlnd_object_factory::get_pfc */
224 static MYSQLND_PFC *
MYSQLND_METHOD(mysqlnd_object_factory,get_pfc)225 MYSQLND_METHOD(mysqlnd_object_factory, get_pfc)(const bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
226 {
227 	const size_t pfc_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_PFC) + mysqlnd_plugin_count() * sizeof(void *));
228 	const size_t pfc_data_alloc_size = sizeof(MYSQLND_PFC_DATA) + mysqlnd_plugin_count() * sizeof(void *);
229 	MYSQLND_PFC * pfc = mnd_pecalloc(1, pfc_alloc_size + pfc_data_alloc_size, persistent);
230 
231 	DBG_ENTER("mysqlnd_object_factory::get_pfc");
232 	DBG_INF_FMT("persistent=%u", persistent);
233 	if (pfc) {
234 		pfc->data = (MYSQLND_PFC_DATA*)((char*)pfc + pfc_alloc_size);
235 		pfc->persistent = pfc->data->persistent = persistent;
236 		pfc->data->m = *mysqlnd_pfc_get_methods();
237 
238 		pfc->data->m.init(pfc, stats, error_info);
239 	}
240 	DBG_RETURN(pfc);
241 }
242 /* }}} */
243 
244 
245 /* {{{ mysqlnd_object_factory::get_vio */
246 static MYSQLND_VIO *
MYSQLND_METHOD(mysqlnd_object_factory,get_vio)247 MYSQLND_METHOD(mysqlnd_object_factory, get_vio)(const bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
248 {
249 	const size_t vio_alloc_size = ZEND_MM_ALIGNED_SIZE(sizeof(MYSQLND_VIO) + mysqlnd_plugin_count() * sizeof(void *));
250 	const size_t vio_data_alloc_size = sizeof(MYSQLND_VIO_DATA) + mysqlnd_plugin_count() * sizeof(void *);
251 	MYSQLND_VIO * vio = mnd_pecalloc(1, vio_alloc_size + vio_data_alloc_size, persistent);
252 
253 	DBG_ENTER("mysqlnd_object_factory::get_vio");
254 	DBG_INF_FMT("persistent=%u", persistent);
255 	if (vio) {
256 		vio->data = (MYSQLND_VIO_DATA*)((char*)vio + vio_alloc_size);
257 		vio->persistent = vio->data->persistent = persistent;
258 		vio->data->m = *mysqlnd_vio_get_methods();
259 
260 		vio->data->m.init(vio, stats, error_info);
261 	}
262 	DBG_RETURN(vio);
263 }
264 /* }}} */
265 
266 
267 /* {{{ mysqlnd_object_factory::get_protocol_payload_decoder_factory */
268 static MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
MYSQLND_METHOD(mysqlnd_object_factory,get_protocol_payload_decoder_factory)269 MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)(MYSQLND_CONN_DATA * conn, const bool persistent)
270 {
271 	const size_t alloc_size = sizeof(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY) + mysqlnd_plugin_count() * sizeof(void *);
272 	MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *ret = mnd_pecalloc(1, alloc_size, persistent);
273 
274 	DBG_ENTER("mysqlnd_object_factory::get_protocol_payload_decoder_factory");
275 	DBG_INF_FMT("persistent=%u", persistent);
276 	if (ret) {
277 		ret->persistent = persistent;
278 		ret->conn = conn;
279 		ret->m = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_protocol_payload_decoder_factory);
280 	}
281 
282 	DBG_RETURN(ret);
283 }
284 /* }}} */
285 
286 
287 PHPAPI MYSQLND_CLASS_METHODS_START(mysqlnd_object_factory)
288 	MYSQLND_METHOD(mysqlnd_object_factory, get_connection),
289 	MYSQLND_METHOD(mysqlnd_object_factory, clone_connection_object),
290 	MYSQLND_METHOD(mysqlnd_object_factory, get_prepared_statement),
291 	MYSQLND_METHOD(mysqlnd_object_factory, get_pfc),
292 	MYSQLND_METHOD(mysqlnd_object_factory, get_vio),
293 	MYSQLND_METHOD(mysqlnd_object_factory, get_protocol_payload_decoder_factory)
294 MYSQLND_CLASS_METHODS_END;
295