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