1 /// Connect to a MySQL/MariaDB server.
2 module mysql.connection;
3 
4 import std.algorithm;
5 import std.conv;
6 import std.exception;
7 import std.range;
8 import std.socket;
9 import std.string;
10 import std.typecons;
11 
12 import mysql.commands;
13 import mysql.exceptions;
14 import mysql.prepared;
15 import mysql.protocol.comms;
16 import mysql.protocol.constants;
17 import mysql.protocol.packets;
18 import mysql.protocol.sockets;
19 import mysql.result;
20 
21 version(Have_vibe_core)
22 {
23 	static if(__traits(compiles, (){ import vibe.core.net; } ))
24 		import vibe.core.net;
25 	else
26 		static assert(false, "mysql-native can't find Vibe.d's 'vibe.core.net'.");
27 }
28 
29 /// The default `mysql.protocol.constants.SvrCapFlags` used when creating a connection.
30 immutable SvrCapFlags defaultClientFlags =
31 		SvrCapFlags.OLD_LONG_PASSWORD | SvrCapFlags.ALL_COLUMN_FLAGS |
32 		SvrCapFlags.WITH_DB | SvrCapFlags.PROTOCOL41 |
33 		SvrCapFlags.SECURE_CONNECTION;// | SvrCapFlags.MULTI_STATEMENTS |
34 		//SvrCapFlags.MULTI_RESULTS;
35 
36 /++
37 Submit an SQL command to the server to be compiled into a prepared statement.
38 
39 This will automatically register the prepared statement on the provided connection.
40 The resulting `mysql.prepared.Prepared` can then be used freely on ANY `Connection`,
41 as it will automatically be registered upon its first use on other connections.
42 Or, pass it to `Connection.register` if you prefer eager registration.
43 
44 Internally, the result of a successful outcome will be a statement handle - an ID -
45 for the prepared statement, a count of the parameters required for
46 execution of the statement, and a count of the columns that will be present
47 in any result set that the command generates.
48 
49 The server will then proceed to send prepared statement headers,
50 including parameter descriptions, and result set field descriptions,
51 followed by an EOF packet.
52 
53 Throws: `mysql.exceptions.MYX` if the server has a problem.
54 +/
55 Prepared prepare(Connection conn, const(char[]) sql)
56 {
57 	auto info = conn.registerIfNeeded(sql);
58 	return Prepared(sql, info.headers, info.numParams);
59 }
60 
61 /++
62 This function is provided ONLY as a temporary aid in upgrading to mysql-native v2.0.0.
63 
64 See `BackwardCompatPrepared` for more info.
65 +/
66 deprecated("This is provided ONLY as a temporary aid in upgrading to mysql-native v2.0.0. You should migrate from this to the Prepared-compatible exec/query overloads in 'mysql.commands'.")
67 BackwardCompatPrepared prepareBackwardCompat(Connection conn, const(char[]) sql)
68 {
69 	return prepareBackwardCompatImpl(conn, sql);
70 }
71 
72 /// Allow mysql-native tests to get around the deprecation message
73 package BackwardCompatPrepared prepareBackwardCompatImpl(Connection conn, const(char[]) sql)
74 {
75 	return BackwardCompatPrepared(conn, prepare(conn, sql));
76 }
77 
78 /++
79 Convenience function to create a prepared statement which calls a stored function.
80 
81 Be careful that your `numArgs` is correct. If it isn't, you may get a
82 `mysql.exceptions.MYX` with a very unclear error message.
83 
84 Throws: `mysql.exceptions.MYX` if the server has a problem.
85 
86 Params:
87 	name = The name of the stored function.
88 	numArgs = The number of arguments the stored procedure takes.
89 +/
90 Prepared prepareFunction(Connection conn, string name, int numArgs)
91 {
92 	auto sql = "select " ~ name ~ preparedPlaceholderArgs(numArgs);
93 	return prepare(conn, sql);
94 }
95 
96 /++
97 Convenience function to create a prepared statement which calls a stored procedure.
98 
99 OUT parameters are currently not supported. It should generally be
100 possible with MySQL to present them as a result set.
101 
102 Be careful that your `numArgs` is correct. If it isn't, you may get a
103 `mysql.exceptions.MYX` with a very unclear error message.
104 
105 Throws: `mysql.exceptions.MYX` if the server has a problem.
106 
107 Params:
108 	name = The name of the stored procedure.
109 	numArgs = The number of arguments the stored procedure takes.
110 
111 +/
112 Prepared prepareProcedure(Connection conn, string name, int numArgs)
113 {
114 	auto sql = "call " ~ name ~ preparedPlaceholderArgs(numArgs);
115 	return prepare(conn, sql);
116 }
117 
118 private string preparedPlaceholderArgs(int numArgs)
119 {
120 	auto sql = "(";
121 	bool comma = false;
122 	foreach(i; 0..numArgs)
123 	{
124 		if (comma)
125 			sql ~= ",?";
126 		else
127 		{
128 			sql ~= "?";
129 			comma = true;
130 		}
131 	}
132 	sql ~= ")";
133 
134 	return sql;
135 }
136 
137 @("preparedPlaceholderArgs")
138 debug(MYSQLN_TESTS)
139 unittest
140 {
141 	assert(preparedPlaceholderArgs(3) == "(?,?,?)");
142 	assert(preparedPlaceholderArgs(2) == "(?,?)");
143 	assert(preparedPlaceholderArgs(1) == "(?)");
144 	assert(preparedPlaceholderArgs(0) == "()");
145 }
146 
147 /// Per-connection info from the server about a registered prepared statement.
148 package struct PreparedServerInfo
149 {
150 	/// Server's identifier for this prepared statement.
151 	/// Apperently, this is never 0 if it's been registered,
152 	/// although mysql-native no longer relies on that.
153 	uint statementId;
154 
155 	ushort psWarnings;
156 
157 	/// Number of parameters this statement takes.
158 	///
159 	/// This will be the same on all connections, but it's returned
160 	/// by the server upon registration, so it's stored here.
161 	ushort numParams;
162 
163 	/// Prepared statement headers
164 	///
165 	/// This will be the same on all connections, but it's returned
166 	/// by the server upon registration, so it's stored here.
167 	PreparedStmtHeaders headers;
168 
169 	/// Not actually from the server. Connection uses this to keep track
170 	/// of statements that should be treated as having been released.
171 	bool queuedForRelease = false;
172 }
173 
174 /++
175 This is a wrapper over `mysql.prepared.Prepared`, provided ONLY as a
176 temporary aid in upgrading to mysql-native v2.0.0 and its
177 new connection-independent model of prepared statements. See the
178 $(LINK2 https://github.com/mysql-d/mysql-native/blob/master/MIGRATING_TO_V2.md, migration guide)
179 for more info.
180 
181 In most cases, this layer shouldn't even be needed. But if you have many
182 lines of code making calls to exec/query the same prepared statement,
183 then this may be helpful.
184 
185 To use this temporary compatability layer, change instances of:
186 
187 ---
188 auto stmt = conn.prepare(...);
189 ---
190 
191 to this:
192 
193 ---
194 auto stmt = conn.prepareBackwardCompat(...);
195 ---
196 
197 And then your prepared statement should work as before.
198 
199 BUT DO NOT LEAVE IT LIKE THIS! Ultimately, you should update
200 your prepared statement code to the mysql-native v2.0.0 API, by changing
201 instances of:
202 
203 ---
204 stmt.exec()
205 stmt.query()
206 stmt.queryRow()
207 stmt.queryRowTuple(outputArgs...)
208 stmt.queryValue()
209 ---
210 
211 to this:
212 
213 ---
214 conn.exec(stmt)
215 conn.query(stmt)
216 conn.queryRow(stmt)
217 conn.queryRowTuple(stmt, outputArgs...)
218 conn.queryValue(stmt)
219 ---
220 
221 Both of the above syntaxes can be used with a `BackwardCompatPrepared`
222 (the `Connection` passed directly to `mysql.commands.exec`/`mysql.commands.query`
223 will override the one embedded associated with your `BackwardCompatPrepared`).
224 
225 Once all of your code is updated, you can change `prepareBackwardCompat`
226 back to `prepare` again, and your upgrade will be complete.
227 +/
228 struct BackwardCompatPrepared
229 {
230 	import std.variant;
231 
232 	private Connection _conn;
233 	Prepared _prepared;
234 
235 	/// Access underlying `Prepared`
236 	@property Prepared prepared() { return _prepared; }
237 
238 	alias _prepared this;
239 
240 	/++
241 	This function is provided ONLY as a temporary aid in upgrading to mysql-native v2.0.0.
242 
243 	See `BackwardCompatPrepared` for more info.
244 	+/
245 	deprecated("Change 'preparedStmt.exec()' to 'conn.exec(preparedStmt)'")
246 	ulong exec()
247 	{
248 		return .exec(_conn, _prepared);
249 	}
250 
251 	///ditto
252 	deprecated("Change 'preparedStmt.query()' to 'conn.query(preparedStmt)'")
253 	ResultRange query()
254 	{
255 		return .query(_conn, _prepared);
256 	}
257 
258 	///ditto
259 	deprecated("Change 'preparedStmt.queryRow()' to 'conn.queryRow(preparedStmt)'")
260 	Nullable!Row queryRow()
261 	{
262 		return .queryRow(_conn, _prepared);
263 	}
264 
265 	///ditto
266 	deprecated("Change 'preparedStmt.queryRowTuple(outArgs...)' to 'conn.queryRowTuple(preparedStmt, outArgs...)'")
267 	void queryRowTuple(T...)(ref T args) if(T.length == 0 || !is(T[0] : Connection))
268 	{
269 		return .queryRowTuple(_conn, _prepared, args);
270 	}
271 
272 	///ditto
273 	deprecated("Change 'preparedStmt.queryValue()' to 'conn.queryValue(preparedStmt)'")
274 	Nullable!Variant queryValue()
275 	{
276 		return .queryValue(_conn, _prepared);
277 	}
278 }
279 
280 /++
281 A class representing a database connection.
282 
283 If you are using Vibe.d, consider using `mysql.pool.MySQLPool` instead of
284 creating a new Connection directly. That will provide certain benefits,
285 such as reusing old connections and automatic cleanup (no need to close
286 the connection when done).
287 
288 ------------------
289 // Suggested usage:
290 
291 {
292 	auto con = new Connection("host=localhost;port=3306;user=joe;pwd=pass123;db=myappsdb");
293 	scope(exit) con.close();
294 
295 	// Use the connection
296 	...
297 }
298 ------------------
299 +/
300 //TODO: All low-level commms should be moved into the mysql.protocol package.
301 class Connection
302 {
303 /+
304 The Connection is responsible for handshaking with the server to establish
305 authentication. It then passes client preferences to the server, and
306 subsequently is the channel for all command packets that are sent, and all
307 response packets received.
308 
309 Uncompressed packets consist of a 4 byte header - 3 bytes of length, and one
310 byte as a packet number. Connection deals with the headers and ensures that
311 packet numbers are sequential.
312 
313 The initial packet is sent by the server - essentially a 'hello' packet
314 inviting login. That packet has a sequence number of zero. That sequence
315 number is the incremented by client and server packets through the handshake
316 sequence.
317 
318 After login all further sequences are initialized by the client sending a
319 command packet with a zero sequence number, to which the server replies with
320 zero or more packets with sequential sequence numbers.
321 +/
322 package:
323 	enum OpenState
324 	{
325 		/// We have not yet connected to the server, or have sent QUIT to the
326 		/// server and closed the connection
327 		notConnected,
328 		/// We have connected to the server and parsed the greeting, but not
329 		/// yet authenticated
330 		connected,
331 		/// We have successfully authenticated against the server, and need to
332 		/// send QUIT to the server when closing the connection
333 		authenticated
334 	}
335 	OpenState   _open;
336 	MySQLSocket _socket;
337 
338 	SvrCapFlags _sCaps, _cCaps;
339 	uint    _sThread;
340 	ushort  _serverStatus;
341 	ubyte   _sCharSet, _protocol;
342 	string  _serverVersion;
343 
344 	string _host, _user, _pwd, _db;
345 	ushort _port;
346 
347 	MySQLSocketType _socketType;
348 
349 	OpenSocketCallbackPhobos _openSocketPhobos;
350 	OpenSocketCallbackVibeD  _openSocketVibeD;
351 
352 	ulong _insertID;
353 
354 	// This gets incremented every time a command is issued or results are purged,
355 	// so a ResultRange can tell whether it's been invalidated.
356 	ulong _lastCommandID;
357 
358 	// Whether there are rows, headers or bimary data waiting to be retreived.
359 	// MySQL protocol doesn't permit performing any other action until all
360 	// such data is read.
361 	bool _rowsPending, _headersPending, _binaryPending;
362 
363 	// Field count of last performed command.
364 	//TODO: Does Connection need to store this?
365 	ushort _fieldCount;
366 
367 	// ResultSetHeaders of last performed command.
368 	//TODO: Does Connection need to store this? Is this even used?
369 	ResultSetHeaders _rsh;
370 
371 	// This tiny thing here is pretty critical. Pay great attention to it's maintenance, otherwise
372 	// you'll get the dreaded "packet out of order" message. It, and the socket connection are
373 	// the reason why most other objects require a connection object for their construction.
374 	ubyte _cpn; /// Packet Number in packet header. Serial number to ensure correct
375 				/// ordering. First packet should have 0
376 	@property ubyte pktNumber()   { return _cpn; }
377 	void bumpPacket()       { _cpn++; }
378 	void resetPacket()      { _cpn = 0; }
379 
380 	version(Have_vibe_core) {} else
381 	pure const nothrow invariant()
382 	{
383 		assert(_socketType != MySQLSocketType.vibed);
384 	}
385 
386 	static PlainPhobosSocket defaultOpenSocketPhobos(string host, ushort port)
387 	{
388 		auto s = new PlainPhobosSocket();
389 		s.connect(new InternetAddress(host, port));
390 		return s;
391 	}
392 
393 	static PlainVibeDSocket defaultOpenSocketVibeD(string host, ushort port)
394 	{
395 		version(Have_vibe_core)
396 			return vibe.core.net.connectTCP(host, port);
397 		else
398 			assert(0);
399 	}
400 
401 	void initConnection()
402 	{
403 		kill(); // Ensure internal state gets reset
404 
405 		resetPacket();
406 		final switch(_socketType)
407 		{
408 			case MySQLSocketType.phobos:
409 				_socket = new MySQLSocketPhobos(_openSocketPhobos(_host, _port));
410 				break;
411 
412 			case MySQLSocketType.vibed:
413 				version(Have_vibe_core) {
414 					_socket = new MySQLSocketVibeD(_openSocketVibeD(_host, _port));
415 					break;
416 				} else assert(0, "Unsupported socket type. Need version Have_vibe_core.");
417 		}
418 	}
419 
420 	SvrCapFlags _clientCapabilities;
421 
422 	void connect(SvrCapFlags clientCapabilities)
423 	out
424 	{
425 		assert(_open == OpenState.authenticated);
426 	}
427 	body
428 	{
429 		initConnection();
430 		auto greeting = this.parseGreeting();
431 		_open = OpenState.connected;
432 
433 		_clientCapabilities = clientCapabilities;
434 		_cCaps = setClientFlags(_sCaps, clientCapabilities);
435 		this.authenticate(greeting);
436 	}
437 
438 	/++
439 	Forcefully close the socket without sending the quit command.
440 
441 	Also resets internal state regardless of whether the connection is open or not.
442 
443 	Needed in case an error leaves communatations in an undefined or non-recoverable state.
444 	+/
445 	void kill()
446 	{
447 		if(_socket && _socket.connected)
448 			_socket.close();
449 		_open = OpenState.notConnected;
450 		// any pending data is gone. Any statements to release will be released
451 		// on the server automatically.
452 		_headersPending = _rowsPending = _binaryPending = false;
453 
454 		preparedRegistrations.clear();
455 
456 		_lastCommandID++; // Invalidate result sets
457 	}
458 
459 	// autoPurge is called every time a command is sent,
460 	// so detect & prevent infinite recursion.
461 	private bool isAutoPurging = false;
462 
463 
464 	/// Called whenever mysql-native needs to send a command to the server
465 	/// and be sure there aren't any pending results (which would prevent
466 	/// a new command from being sent).
467 	void autoPurge()
468 	{
469 		if(isAutoPurging)
470 			return;
471 
472 		isAutoPurging = true;
473 		scope(exit) isAutoPurging = false;
474 
475 		try
476 		{
477 			purgeResult();
478 			releaseQueued();
479 		}
480 		catch(Exception e)
481 		{
482 			// Likely the connection was closed, so reset any state (and force-close if needed).
483 			// Don't treat this as a real error, because everything will be reset when we
484 			// reconnect.
485 			kill();
486 		}
487 	}
488 
489 	/// Lookup per-connection prepared statement info by SQL
490 	private PreparedRegistrations!PreparedServerInfo preparedRegistrations;
491 
492 	/// Releases all prepared statements that are queued for release.
493 	void releaseQueued()
494 	{
495 		foreach(sql, info; preparedRegistrations.directLookup)
496 		if(info.queuedForRelease)
497 		{
498 			immediateReleasePrepared(this, info.statementId);
499 			preparedRegistrations.directLookup.remove(sql);
500 		}
501 	}
502 
503 	/// Returns null if not found
504 	Nullable!PreparedServerInfo getPreparedServerInfo(const(char[]) sql) pure nothrow
505 	{
506 		return preparedRegistrations[sql];
507 	}
508 
509 	/// If already registered, simply returns the cached `PreparedServerInfo`.
510 	PreparedServerInfo registerIfNeeded(const(char[]) sql)
511 	{
512 		return preparedRegistrations.registerIfNeeded(sql, sql => performRegister(this, sql));
513 	}
514 
515 public:
516 
517 	/++
518 	Construct opened connection.
519 
520 	Throws `mysql.exceptions.MYX` upon failure to connect.
521 
522 	If you are using Vibe.d, consider using `mysql.pool.MySQLPool` instead of
523 	creating a new Connection directly. That will provide certain benefits,
524 	such as reusing old connections and automatic cleanup (no need to close
525 	the connection when done).
526 
527 	------------------
528 	// Suggested usage:
529 
530 	{
531 	    auto con = new Connection("host=localhost;port=3306;user=joe;pwd=pass123;db=myappsdb");
532 	    scope(exit) con.close();
533 
534 	    // Use the connection
535 	    ...
536 	}
537 	------------------
538 
539 	Params:
540 		cs = A connection string of the form "host=localhost;user=user;pwd=password;db=mysqld"
541 			(TODO: The connection string needs work to allow for semicolons in its parts!)
542 		socketType = Whether to use a Phobos or Vibe.d socket. Default is Phobos,
543 			unless compiled with `-version=Have_vibe_core` (set automatically
544 			if using $(LINK2 http://code.dlang.org/getting_started, DUB)).
545 		openSocket = Optional callback which should return a newly-opened Phobos
546 			or Vibe.d TCP socket. This allows custom sockets to be used,
547 			subclassed from Phobos's or Vibe.d's sockets.
548 		host = An IP address in numeric dotted form, or as a host  name.
549 		user = The user name to authenticate.
550 		password = User's password.
551 		db = Desired initial database.
552 		capFlags = The set of flag bits from the server's capabilities that the client requires
553 	+/
554 	//After the connection is created, and the initial invitation is received from the server
555 	//client preferences can be set, and authentication can then be attempted.
556 	this(string host, string user, string pwd, string db, ushort port = 3306, SvrCapFlags capFlags = defaultClientFlags)
557 	{
558 		version(Have_vibe_core)
559 			enum defaultSocketType = MySQLSocketType.vibed;
560 		else
561 			enum defaultSocketType = MySQLSocketType.phobos;
562 
563 		this(defaultSocketType, host, user, pwd, db, port, capFlags);
564 	}
565 
566 	///ditto
567 	this(MySQLSocketType socketType, string host, string user, string pwd, string db, ushort port = 3306, SvrCapFlags capFlags = defaultClientFlags)
568 	{
569 		version(Have_vibe_core) {} else
570 			enforce!MYX(socketType != MySQLSocketType.vibed, "Cannot use Vibe.d sockets without -version=Have_vibe_core");
571 
572 		this(socketType, &defaultOpenSocketPhobos, &defaultOpenSocketVibeD,
573 			host, user, pwd, db, port, capFlags);
574 	}
575 
576 	///ditto
577 	this(OpenSocketCallbackPhobos openSocket,
578 		string host, string user, string pwd, string db, ushort port = 3306, SvrCapFlags capFlags = defaultClientFlags)
579 	{
580 		this(MySQLSocketType.phobos, openSocket, null, host, user, pwd, db, port, capFlags);
581 	}
582 
583 	version(Have_vibe_core)
584 	///ditto
585 	this(OpenSocketCallbackVibeD openSocket,
586 		string host, string user, string pwd, string db, ushort port = 3306, SvrCapFlags capFlags = defaultClientFlags)
587 	{
588 		this(MySQLSocketType.vibed, null, openSocket, host, user, pwd, db, port, capFlags);
589 	}
590 
591 	///ditto
592 	private this(MySQLSocketType socketType,
593 		OpenSocketCallbackPhobos openSocketPhobos, OpenSocketCallbackVibeD openSocketVibeD,
594 		string host, string user, string pwd, string db, ushort port = 3306, SvrCapFlags capFlags = defaultClientFlags)
595 	in
596 	{
597 		final switch(socketType)
598 		{
599 			case MySQLSocketType.phobos: assert(openSocketPhobos !is null); break;
600 			case MySQLSocketType.vibed:  assert(openSocketVibeD  !is null); break;
601 		}
602 	}
603 	body
604 	{
605 		enforce!MYX(capFlags & SvrCapFlags.PROTOCOL41, "This client only supports protocol v4.1");
606 		enforce!MYX(capFlags & SvrCapFlags.SECURE_CONNECTION, "This client only supports protocol v4.1 connection");
607 		version(Have_vibe_core) {} else
608 			enforce!MYX(socketType != MySQLSocketType.vibed, "Cannot use Vibe.d sockets without -version=Have_vibe_core");
609 
610 		_socketType = socketType;
611 		_host = host;
612 		_user = user;
613 		_pwd = pwd;
614 		_db = db;
615 		_port = port;
616 
617 		_openSocketPhobos = openSocketPhobos;
618 		_openSocketVibeD  = openSocketVibeD;
619 
620 		connect(capFlags);
621 	}
622 
623 	///ditto
624 	//After the connection is created, and the initial invitation is received from the server
625 	//client preferences can be set, and authentication can then be attempted.
626 	this(string cs, SvrCapFlags capFlags = defaultClientFlags)
627 	{
628 		string[] a = parseConnectionString(cs);
629 		this(a[0], a[1], a[2], a[3], to!ushort(a[4]), capFlags);
630 	}
631 
632 	///ditto
633 	this(MySQLSocketType socketType, string cs, SvrCapFlags capFlags = defaultClientFlags)
634 	{
635 		string[] a = parseConnectionString(cs);
636 		this(socketType, a[0], a[1], a[2], a[3], to!ushort(a[4]), capFlags);
637 	}
638 
639 	///ditto
640 	this(OpenSocketCallbackPhobos openSocket, string cs, SvrCapFlags capFlags = defaultClientFlags)
641 	{
642 		string[] a = parseConnectionString(cs);
643 		this(openSocket, a[0], a[1], a[2], a[3], to!ushort(a[4]), capFlags);
644 	}
645 
646 	version(Have_vibe_core)
647 	///ditto
648 	this(OpenSocketCallbackVibeD openSocket, string cs, SvrCapFlags capFlags = defaultClientFlags)
649 	{
650 		string[] a = parseConnectionString(cs);
651 		this(openSocket, a[0], a[1], a[2], a[3], to!ushort(a[4]), capFlags);
652 	}
653 
654 	/++
655 	Check whether this `Connection` is still connected to the server, or if
656 	the connection has been closed.
657 	+/
658 	@property bool closed()
659 	{
660 		return _open == OpenState.notConnected || !_socket.connected;
661 	}
662 
663 	/++
664 	Explicitly close the connection.
665 
666 	Idiomatic use as follows is suggested:
667 	------------------
668 	{
669 	    auto con = new Connection("localhost:user:password:mysqld");
670 	    scope(exit) con.close();
671 	    // Use the connection
672 	    ...
673 	}
674 	------------------
675 	+/
676 	void close()
677 	{
678 		// This is a two-stage process. First tell the server we are quitting this
679 		// connection, and then close the socket.
680 
681 		if (_open == OpenState.authenticated && _socket.connected)
682 			quit();
683 
684 		if (_open == OpenState.connected)
685 			kill();
686 		resetPacket();
687 	}
688 
689 	/++
690 	Reconnects to the server using the same connection settings originally
691 	used to create the `Connection`.
692 
693 	Optionally takes a `mysql.protocol.constants.SvrCapFlags`, allowing you to
694 	reconnect using a different set of server capability flags.
695 
696 	Normally, if the connection is already open, this will do nothing. However,
697 	if you request a different set of `mysql.protocol.constants.SvrCapFlags`
698 	then was originally used to create the `Connection`, the connection will
699 	be closed and then reconnected using the new `mysql.protocol.constants.SvrCapFlags`.
700 	+/
701 	void reconnect()
702 	{
703 		reconnect(_clientCapabilities);
704 	}
705 
706 	///ditto
707 	void reconnect(SvrCapFlags clientCapabilities)
708 	{
709 		bool sameCaps = clientCapabilities == _clientCapabilities;
710 		if(!closed)
711 		{
712 			// Same caps as before?
713 			if(clientCapabilities == _clientCapabilities)
714 				return; // Nothing to do, just keep current connection
715 
716 			close();
717 		}
718 
719 		connect(clientCapabilities);
720 	}
721 
722 	private void quit()
723 	in
724 	{
725 		assert(_open == OpenState.authenticated);
726 	}
727 	body
728 	{
729 		this.sendCmd(CommandType.QUIT, []);
730 		// No response is sent for a quit packet
731 		_open = OpenState.connected;
732 	}
733 
734 	/++
735 	Parses a connection string of the form
736 	`"host=localhost;port=3306;user=joe;pwd=pass123;db=myappsdb"`
737 
738 	Port is optional and defaults to 3306.
739 
740 	Whitespace surrounding any name or value is automatically stripped.
741 
742 	Returns a five-element array of strings in this order:
743 	$(UL
744 	$(LI [0]: host)
745 	$(LI [1]: user)
746 	$(LI [2]: pwd)
747 	$(LI [3]: db)
748 	$(LI [4]: port)
749 	)
750 
751 	(TODO: The connection string needs work to allow for semicolons in its parts!)
752 	+/
753 	//TODO: Replace the return value with a proper struct.
754 	static string[] parseConnectionString(string cs)
755 	{
756 		string[] rv;
757 		rv.length = 5;
758 		rv[4] = "3306"; // Default port
759 		string[] a = split(cs, ";");
760 		foreach (s; a)
761 		{
762 			string[] a2 = split(s, "=");
763 			enforce!MYX(a2.length == 2, "Bad connection string: " ~ cs);
764 			string name = strip(a2[0]);
765 			string val = strip(a2[1]);
766 			switch (name)
767 			{
768 				case "host":
769 					rv[0] = val;
770 					break;
771 				case "user":
772 					rv[1] = val;
773 					break;
774 				case "pwd":
775 					rv[2] = val;
776 					break;
777 				case "db":
778 					rv[3] = val;
779 					break;
780 				case "port":
781 					rv[4] = val;
782 					break;
783 				default:
784 					throw new MYX("Bad connection string: " ~ cs, __FILE__, __LINE__);
785 			}
786 		}
787 		return rv;
788 	}
789 
790 	/++
791 	Select a current database.
792 
793 	Throws `mysql.exceptions.MYX` upon failure.
794 
795 	Params: dbName = Name of the requested database
796 	+/
797 	void selectDB(string dbName)
798 	{
799 		this.sendCmd(CommandType.INIT_DB, dbName);
800 		this.getCmdResponse();
801 		_db = dbName;
802 	}
803 
804 	/++
805 	Check the server status.
806 
807 	Throws `mysql.exceptions.MYX` upon failure.
808 
809 	Returns: An `mysql.protocol.packets.OKErrorPacket` from which server status can be determined
810 	+/
811 	OKErrorPacket pingServer()
812 	{
813 		this.sendCmd(CommandType.PING, []);
814 		return this.getCmdResponse();
815 	}
816 
817 	/++
818 	Refresh some feature(s) of the server.
819 
820 	Throws `mysql.exceptions.MYX` upon failure.
821 
822 	Returns: An `mysql.protocol.packets.OKErrorPacket` from which server status can be determined
823 	+/
824 	OKErrorPacket refreshServer(RefreshFlags flags)
825 	{
826 		this.sendCmd(CommandType.REFRESH, [flags]);
827 		return this.getCmdResponse();
828 	}
829 
830 	/++
831 	Flush any outstanding result set elements.
832 
833 	When the server responds to a command that produces a result set, it
834 	queues the whole set of corresponding packets over the current connection.
835 	Before that `Connection` can embark on any new command, it must receive
836 	all of those packets and junk them.
837 
838 	As of v1.1.4, this is done automatically as needed. But you can still
839 	call this manually to force a purge to occur when you want.
840 
841 	See_Also: $(LINK http://www.mysqlperformanceblog.com/2007/07/08/mysql-net_write_timeout-vs-wait_timeout-and-protocol-notes/)
842 	+/
843 	ulong purgeResult()
844 	{
845 		return mysql.protocol.comms.purgeResult(this);
846 	}
847 
848 	/++
849 	Get a textual report on the server status.
850 
851 	(COM_STATISTICS)
852 	+/
853 	string serverStats()
854 	{
855 		return mysql.protocol.comms.serverStats(this);
856 	}
857 
858 	/++
859 	Enable multiple statement commands.
860 
861 	This can be used later if this feature was not requested in the client capability flags.
862 
863 	Warning: This functionality is currently untested.
864 
865 	Params: on = Boolean value to turn the capability on or off.
866 	+/
867 	//TODO: Need to test this
868 	void enableMultiStatements(bool on)
869 	{
870 		mysql.protocol.comms.enableMultiStatements(this, on);
871 	}
872 
873 	/// Return the in-force protocol number.
874 	@property ubyte protocol() pure const nothrow { return _protocol; }
875 	/// Server version
876 	@property string serverVersion() pure const nothrow { return _serverVersion; }
877 	/// Server capability flags
878 	@property uint serverCapabilities() pure const nothrow { return _sCaps; }
879 	/// Server status
880 	@property ushort serverStatus() pure const nothrow { return _serverStatus; }
881 	/// Current character set
882 	@property ubyte charSet() pure const nothrow { return _sCharSet; }
883 	/// Current database
884 	@property string currentDB() pure const nothrow { return _db; }
885 	/// Socket type being used, Phobos or Vibe.d
886 	@property MySQLSocketType socketType() pure const nothrow { return _socketType; }
887 
888 	/// After a command that inserted a row into a table with an auto-increment
889 	/// ID column, this method allows you to retrieve the last insert ID.
890 	@property ulong lastInsertID() pure const nothrow { return _insertID; }
891 
892 	/// This gets incremented every time a command is issued or results are purged,
893 	/// so a `mysql.result.ResultRange` can tell whether it's been invalidated.
894 	@property ulong lastCommandID() pure const nothrow { return _lastCommandID; }
895 
896 	/// Gets whether rows are pending.
897 	///
898 	/// Note, you may want `hasPending` instead.
899 	@property bool rowsPending() pure const nothrow { return _rowsPending; }
900 
901 	/// Gets whether anything (rows, headers or binary) is pending.
902 	/// New commands cannot be sent on a connection while anything is pending
903 	/// (the pending data will automatically be purged.)
904 	@property bool hasPending() pure const nothrow
905 	{
906 		return _rowsPending || _headersPending || _binaryPending;
907 	}
908 
909 	/// Gets the result header's field descriptions.
910 	@property FieldDescription[] resultFieldDescriptions() pure { return _rsh.fieldDescriptions; }
911 
912 	/++
913 	Manually register a prepared statement on this connection.
914 
915 	Does nothing if statement is already registered on this connection.
916 
917 	Calling this is not strictly necessary, as the prepared statement will
918 	automatically be registered upon its first use on any `Connection`.
919 	This is provided for those who prefer eager registration over lazy
920 	for performance reasons.
921 	+/
922 	void register(Prepared prepared)
923 	{
924 		register(prepared.sql);
925 	}
926 
927 	///ditto
928 	void register(const(char[]) sql)
929 	{
930 		registerIfNeeded(sql);
931 	}
932 
933 	/++
934 	Manually release a prepared statement on this connection.
935 
936 	This method tells the server that it can dispose of the information it
937 	holds about the current prepared statement.
938 
939 	Calling this is not strictly necessary. The server considers prepared
940 	statements to be per-connection, so they'll go away when the connection
941 	closes anyway. This is provided in case direct control is actually needed.
942 
943 	If you choose to use a reference counted struct to call this automatically,
944 	be aware that embedding reference counted structs inside garbage collectible
945 	heap objects is dangerous and should be avoided, as it can lead to various
946 	hidden problems, from crashes to race conditions. (See the discussion at issue
947 	$(LINK2 https://github.com/mysql-d/mysql-native/issues/159, #159)
948 	for details.) Instead, it may be better to simply avoid trying to manage
949 	their release at all, as it's not usually necessary. Or to periodically
950 	release all prepared statements, and simply allow mysql-native to
951 	automatically re-register them upon their next use.
952 
953 	Notes:
954 
955 	In actuality, the server might not immediately be told to release the
956 	statement (although `isRegistered` will still report `false`).
957 
958 	This is because there could be a `mysql.result.ResultRange` with results
959 	still pending for retrieval, and the protocol doesn't allow sending commands
960 	(such as "release a prepared statement") to the server while data is pending.
961 	Therefore, this function may instead queue the statement to be released
962 	when it is safe to do so: Either the next time a result set is purged or
963 	the next time a command (such as `mysql.commands.query` or
964 	`mysql.commands.exec`) is performed (because such commands automatically
965 	purge any pending results).
966 
967 	This function does NOT auto-purge because, if this is ever called from
968 	automatic resource management cleanup (refcounting, RAII, etc), that
969 	would create ugly situations where hidden, implicit behavior triggers
970 	an unexpected auto-purge.
971 	+/
972 	void release(Prepared prepared)
973 	{
974 		release(prepared.sql);
975 	}
976 
977 	///ditto
978 	void release(const(char[]) sql)
979 	{
980 		//TODO: Don't queue it if nothing is pending. Just do it immediately.
981 		//      But need to be certain both situations are unittested.
982 		preparedRegistrations.queueForRelease(sql);
983 	}
984 
985 	/++
986 	Manually release all prepared statements on this connection.
987 
988 	While minimal, every prepared statement registered on a connection does
989 	use up a small amount of resources in both mysql-native and on the server.
990 	Additionally, servers can be configured
991 	$(LINK2 https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_prepared_stmt_count,
992 	to limit the number of prepared statements)
993 	allowed on a connection at one time (the default, however
994 	is quite high). Note also, that certain overloads of `mysql.commands.exec`,
995 	`mysql.commands.query`, etc. register prepared statements behind-the-scenes
996 	which are cached for quick re-use later.
997 
998 	Therefore, it may occasionally be useful to clear out all prepared
999 	statements on a connection, together with all resources used by them (or
1000 	at least leave the resources ready for garbage-collection). This function
1001 	does just that.
1002 
1003 	Note that this is ALWAYS COMPLETELY SAFE to call, even if you still have
1004 	live prepared statements you intend to use again. This is safe because
1005 	mysql-native will automatically register or re-register prepared statements
1006 	as-needed.
1007 
1008 	Notes:
1009 
1010 	In actuality, the prepared statements might not be immediately released
1011 	(although `isRegistered` will still report `false` for them).
1012 
1013 	This is because there could be a `mysql.result.ResultRange` with results
1014 	still pending for retrieval, and the protocol doesn't allow sending commands
1015 	(such as "release a prepared statement") to the server while data is pending.
1016 	Therefore, this function may instead queue the statement to be released
1017 	when it is safe to do so: Either the next time a result set is purged or
1018 	the next time a command (such as `mysql.commands.query` or
1019 	`mysql.commands.exec`) is performed (because such commands automatically
1020 	purge any pending results).
1021 
1022 	This function does NOT auto-purge because, if this is ever called from
1023 	automatic resource management cleanup (refcounting, RAII, etc), that
1024 	would create ugly situations where hidden, implicit behavior triggers
1025 	an unexpected auto-purge.
1026 	+/
1027 	void releaseAll()
1028 	{
1029 		preparedRegistrations.queueAllForRelease();
1030 	}
1031 
1032 	/// Is the given statement registered on this connection as a prepared statement?
1033 	bool isRegistered(Prepared prepared)
1034 	{
1035 		return isRegistered( prepared.sql );
1036 	}
1037 
1038 	///ditto
1039 	bool isRegistered(const(char[]) sql)
1040 	{
1041 		return isRegistered( preparedRegistrations[sql] );
1042 	}
1043 
1044 	///ditto
1045 	package bool isRegistered(Nullable!PreparedServerInfo info)
1046 	{
1047 		return !info.isNull && !info.get.queuedForRelease;
1048 	}
1049 }