1 /++
2 Connect to a MySQL/MariaDB database using vibe.d's
3 $(LINK2 http://vibed.org/api/vibe.core.connectionpool/ConnectionPool, ConnectionPool).
4 
5 You have to include vibe.d in your project to be able to use this class.
6 If you don't want to, refer to `mysql.connection.Connection`.
7 
8 This provides various benefits over creating a new connection manually,
9 such as automatically reusing old connections, and automatic cleanup (no need to close
10 the connection when done).
11 +/
12 module mysql.pool;
13 
14 import std.conv;
15 import mysql.connection;
16 import mysql.protocol.constants;
17 debug(MYSQLN_TESTS)
18 {
19 	import mysql.test.common;
20 }
21 
22 version(Have_vibe_d_core) version = IncludeMySQLPool;
23 version(MySQLDocs)        version = IncludeMySQLPool;
24 
25 version(IncludeMySQLPool)
26 {
27 	version(Have_vibe_d_core)
28 		import vibe.core.connectionpool;
29 	else
30 	{
31 		/++
32 		Vibe.d's
33 		$(LINK2 http://vibed.org/api/vibe.core.connectionpool/ConnectionPool, ConnectionPool)
34 		class.
35 
36 		Not actually included in module `mysql.pool`. Only listed here for
37 		documentation purposes. For ConnectionPool and it's documentation, see:
38 		$(LINK http://vibed.org/api/vibe.core.connectionpool/ConnectionPool)
39 		+/
40 		class ConnectionPool(T)
41 		{
42 			/// See: $(LINK http://vibed.org/api/vibe.core.connectionpool/ConnectionPool.this)
43 			this(Connection delegate() connection_factory, uint max_concurrent = (uint).max)
44 			{}
45 
46 			/// See: $(LINK http://vibed.org/api/vibe.core.connectionpool/ConnectionPool.lockConnection)
47 			LockedConnection!T lockConnection() { return LockedConnection!T(); }
48 
49 			/// See: $(LINK http://vibed.org/api/vibe.core.connectionpool/ConnectionPool.maxConcurrency)
50 			uint maxConcurrency;
51 		}
52 
53 		/++
54 		Vibe.d's
55 		$(LINK2 http://vibed.org/api/vibe.core.connectionpool/LockedConnection, LockedConnection)
56 		struct.
57 
58 		Not actually included in module `mysql.pool`. Only listed here for
59 		documentation purposes. For LockedConnection and it's documentation, see:
60 		$(LINK http://vibed.org/api/vibe.core.connectionpool/LockedConnection)
61 		+/
62 		struct LockedConnection(Connection) {}
63 	}
64 
65 	/++
66 	A lightweight convenience interface to a MySQL/MariaDB database using vibe.d's
67 	$(LINK2 http://vibed.org/api/vibe.core.connectionpool/ConnectionPool, ConnectionPool).
68 
69 	You have to include vibe.d in your project to be able to use this class.
70 	If you don't want to, refer to `mysql.connection.Connection`.
71 
72 	If, for any reason, this class doesn't suit your needs, it's easy to just
73 	use vibe.d's $(LINK2 http://vibed.org/api/vibe.core.connectionpool/ConnectionPool, ConnectionPool)
74 	directly. Simply provide it with a delegate that creates a new `mysql.connection.Connection`
75 	and does any other custom processing if needed.
76 	+/
77 	class MySQLPool {
78 		private {
79 			string m_host;
80 			string m_user;
81 			string m_password;
82 			string m_database;
83 			ushort m_port;
84 			SvrCapFlags m_capFlags;
85 			void delegate(Connection) m_onNewConnection;
86 			ConnectionPool!Connection m_pool;
87 		}
88 
89 		/// Sets up a connection pool with the provided connection settings.
90 		///
91 		/// The optional `onNewConnection` param allows you to set a callback
92 		/// which will be run every time a new connection is created.
93 		this(string host, string user, string password, string database,
94 			ushort port = 3306, uint maxConcurrent = (uint).max,
95 			SvrCapFlags capFlags = defaultClientFlags,
96 			void delegate(Connection) onNewConnection = null)
97 		{
98 			m_host = host;
99 			m_user = user;
100 			m_password = password;
101 			m_database = database;
102 			m_port = port;
103 			m_capFlags = capFlags;
104 			m_onNewConnection = onNewConnection;
105 			m_pool = new ConnectionPool!Connection(&createConnection);
106 		}
107 
108 		///ditto
109 		this(string host, string user, string password, string database,
110 			ushort port, SvrCapFlags capFlags, void delegate(Connection) onNewConnection = null)
111 		{
112 			this(host, user, password, database, port, (uint).max, capFlags, onNewConnection);
113 		}
114 
115 		///ditto
116 		this(string host, string user, string password, string database,
117 			ushort port, void delegate(Connection) onNewConnection)
118 		{
119 			this(host, user, password, database, port, (uint).max, defaultClientFlags, onNewConnection);
120 		}
121 
122 		///ditto
123 		this(string connStr, uint maxConcurrent = (uint).max, SvrCapFlags capFlags = defaultClientFlags,
124 			void delegate(Connection) onNewConnection = null)
125 		{
126 			auto parts = Connection.parseConnectionString(connStr);
127 			this(parts[0], parts[1], parts[2], parts[3], to!ushort(parts[4]), capFlags, onNewConnection);
128 		}
129 
130 		///ditto
131 		this(string connStr, SvrCapFlags capFlags, void delegate(Connection) onNewConnection = null)
132 		{
133 			this(connStr, (uint).max, capFlags, onNewConnection);
134 		}
135 
136 		///ditto
137 		this(string connStr, void delegate(Connection) onNewConnection)
138 		{
139 			this(connStr, (uint).max, defaultClientFlags, onNewConnection);
140 		}
141 
142 		/++
143 		Obtain a connection. If one isn't available, a new one will be created.
144 
145 		The connection returned is actually a `LockedConnection!Connection`,
146 		but it uses `alias this`, and so can be used just like a Connection.
147 		(See vibe.d's
148 		$(LINK2 http://vibed.org/api/vibe.core.connectionpool/LockedConnection, LockedConnection documentation).)
149 
150 		No other fiber will be given this `mysql.connection.Connection` as long as your fiber still holds it.
151 
152 		There is no need to close, release or unlock this connection. It is
153 		reference-counted and will automatically be returned to the pool once
154 		your fiber is done with it.
155 		+/
156 		auto lockConnection() { return m_pool.lockConnection(); }
157 
158 		private Connection createConnection()
159 		{
160 			auto conn = new Connection(m_host, m_user, m_password, m_database, m_port, m_capFlags);
161 
162 			if(m_onNewConnection)
163 				m_onNewConnection(conn);
164 
165 			return conn;
166 		}
167 
168 		
169 		/// Get/set a callback delegate to be run every time a new connection
170 		/// is created.
171 		@property void onNewConnection(void delegate(Connection) onNewConnection)
172 		{
173 			m_onNewConnection = onNewConnection;
174 		}
175 
176 		///ditto
177 		@property void delegate(Connection) onNewConnection()
178 		{
179 			return m_onNewConnection;
180 		}
181 
182 		debug(MYSQLN_TESTS)
183 		unittest
184 		{
185 			auto count = 0;
186 			void callback(Connection conn)
187 			{
188 				count++;
189 			}
190 
191 			// Test getting/setting
192 			auto poolA = new MySQLPool(testConnectionStr, &callback);
193 			auto poolB = new MySQLPool(testConnectionStr);
194 			auto poolNoCallback = new MySQLPool(testConnectionStr);
195 			
196 			assert(poolA.onNewConnection == &callback);
197 			assert(poolB.onNewConnection is null);
198 			assert(poolNoCallback.onNewConnection is null);
199 			
200 			poolB.onNewConnection = &callback;
201 			assert(poolB.onNewConnection == &callback);
202 			assert(count == 0);
203 
204 			// Ensure callback is called
205 			{
206 				auto connA = poolA.lockConnection();
207 				assert(!connA.closed);
208 				assert(count == 1);
209 				
210 				auto connB = poolB.lockConnection();
211 				assert(!connB.closed);
212 				assert(count == 2);
213 			}
214 
215 			// Ensure works with no callback
216 			{
217 				auto oldCount = count;
218 				auto poolC = new MySQLPool(testConnectionStr);
219 				auto connC = poolC.lockConnection();
220 				assert(!connC.closed);
221 				assert(count == oldCount);
222 			}
223 		}
224 
225 		
226 		/++
227 		Forwards to vibe.d's
228 		$(LINK2 http://vibed.org/api/vibe.core.connectionpool/ConnectionPool.maxConcurrency, ConnectionPool.maxConcurrency)
229 		+/
230 		@property uint maxConcurrency()
231 		{
232 			return m_pool.maxConcurrency;
233 		}
234 
235 		///ditto
236 		@property void maxConcurrency(uint maxConcurrent)
237 		{
238 			m_pool.maxConcurrency = maxConcurrent;
239 		}
240 	}
241 }