1 /// Exceptions defined by mysql-native.
2 module mysql.exceptions;
3 
4 import std.algorithm;
5 import mysql.protocol.packets;
6 
7 /++
8 An exception type to distinguish exceptions thrown by this package.
9 +/
10 class MYX: Exception
11 {
12 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
13 	{
14 		super(msg, file, line);
15 	}
16 }
17 deprecated("This has been renamed MYX")
18 alias MySQLException = MYX;
19 
20 /++
21 The server sent back a MySQL error code and message. If the server is 4.1+,
22 there should also be an ANSI/ODBC-standard SQLSTATE error code.
23 
24 See_Also: https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
25 +/
26 class MYXReceived: MYX
27 {
28 	ushort errorCode;
29 	char[5] sqlState;
30 
31 	this(OKErrorPacket okp, string file, size_t line) pure
32 	{
33 		this(okp.message, okp.serverStatus, okp.sqlState, file, line);
34 	}
35 
36 	this(string msg, ushort errorCode, char[5] sqlState, string file, size_t line) pure
37 	{
38 		this.errorCode = errorCode;
39 		this.sqlState = sqlState;
40 		super("MySQL error: " ~ msg, file, line);
41 	}
42 }
43 deprecated("This has been renamed MYXReceived")
44 alias MySQLReceivedException = MYXReceived;
45 
46 /++
47 This exception is no longer used by mysql-native and will be removed
48 in an upcoming release.
49 
50 Previously, this was thrown when attempting to communicate with the server
51 (ex: executing SQL or creating a new prepared statement) while the server
52 was still sending results data. Any `mysql.result.ResultRange` was required to
53 be consumed or purged before anything else was allowed to be done on the
54 connection (as per inherent limitations of the MySQL client-server protocol).
55 
56 But as of mysql-native v1.1.4 (as discussed in
57 $(LINK2 https://github.com/mysql-d/mysql-native/issues/117, issue #117)),
58 this behavior was changed. Any communication with the server now purges any
59 active `mysql.result.ResultRange` automatically (See also,
60 `mysql.result.ResultRange.isValid`). As a result, this exception is never
61 thrown anymore.
62 +/
63 deprecated("No longer thrown by mysql-native, as of v1.1.4. You can safely remove all MYXDataPending handling from your code.")
64 class MYXDataPending: MYX
65 {
66 	this(string file = __FILE__, size_t line = __LINE__) pure
67 	{
68 		super("Data is pending on the connection. Any existing ResultRange "~
69 			"must be consumed or purged before performing any other communication "~
70 			"with the server.", file, line);
71 	}
72 }
73 deprecated("No longer thrown by mysql-native, as of v1.1.4. You can safely remove all MYXDataPending handling from your code.")
74 alias MySQLDataPendingException = MYXDataPending;
75 
76 /++
77 Received invalid data from the server which violates the MySQL network protocol.
78 (Quite possibly mysql-native's fault. Please
79 $(LINK2 https://github.com/mysql-d/mysql-native/issues, file an issue)
80 if you receive this.)
81 +/
82 class MYXProtocol: MYX
83 {
84 	this(string msg, string file, size_t line) pure
85 	{
86 		super(msg, file, line);
87 	}
88 }
89 deprecated("This has been renamed MYXProtocol")
90 alias MySQLProtocolException = MYXProtocol;
91 
92 /++
93 Thrown when attempting to use a prepared statement which had already been released.
94 +/
95 class MYXNotPrepared: MYX
96 {
97 	this(string file = __FILE__, size_t line = __LINE__) pure
98 	{
99 		super("The prepared statement has already been released.", file, line);
100 	}
101 }
102 deprecated("This has been renamed MYXNotPrepared")
103 alias MySQLNotPreparedException = MYXNotPrepared;
104 
105 /++
106 Common base class of MYXResultRecieved and MYXNoResultRecieved.
107 
108 Thrown when making the wrong choice between exec or query.
109 
110 The query functions (query, queryRow, etc.) are for SQL statements
111 such as SELECT that return results (even if the result set has zero elements.)
112 
113 The exec functions are for SQL statements, such as INSERT, that never return
114 result sets, but may return rowsAffected.
115 
116 Using one of those functions, when the other should have been used instead,
117 results in an exception derived from this.
118 +/
119 class MYXWrongFunction: MYX
120 {
121 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
122 	{
123 		super(msg, file, line);
124 	}
125 }
126 deprecated("This has been renamed MYXWrongFunction")
127 alias MySQLWrongFunctionException = MYXWrongFunction;
128 
129 /++
130 Thrown when a result set was returned unexpectedly. Use the query functions
131 (query, queryRow, etc.), not exec for commands that return
132 result sets (such as SELECT), even if the result set has zero elements.
133 +/
134 class MYXResultRecieved: MYXWrongFunction
135 {
136 	this(string file = __FILE__, size_t line = __LINE__) pure
137 	{
138 		super(
139 			"A result set was returned. Use the query functions, not exec, "~
140 			"for commands that return result sets.",
141 			file, line
142 		);
143 	}
144 }
145 deprecated("This has been renamed MYXResultRecieved")
146 alias MySQLResultRecievedException = MYXResultRecieved;
147 
148 /++
149 Thrown when the executed query, unexpectedly, did not produce a result set.
150 Use the exec functions, not query (query, queryRow, etc.),
151 for commands that don't produce result sets (such as INSERT).
152 +/
153 class MYXNoResultRecieved: MYXWrongFunction
154 {
155 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
156 	{
157 		super(
158 			"The executed query did not produce a result set. Use the exec "~
159 			"functions, not query, for commands that don't produce result sets.",
160 			file, line
161 		);
162 	}
163 }
164 deprecated("This has been renamed MYXNoResultRecieved")
165 alias MySQLNoResultRecievedException = MYXNoResultRecieved;
166 
167 /++
168 Thrown when attempting to use a range that's been invalidated.
169 In particular, when using a ResultRange after a new command
170 has been issued on the same connection.
171 +/
172 class MYXInvalidatedRange: MYX
173 {
174 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
175 	{
176 		super(msg, file, line);
177 	}
178 }
179 deprecated("This has been renamed MYXInvalidatedRange")
180 alias MySQLInvalidatedRangeException = MYXInvalidatedRange;
181 
182 debug(MYSQL_INTEGRATION_TESTS)
183 unittest
184 {
185 	import std.exception;
186 	import mysql.commands;
187 	import mysql.prepared;
188 	import mysql.test.common : scopedCn, createCn;
189 	mixin(scopedCn);
190 
191 	cn.exec("DROP TABLE IF EXISTS `wrongFunctionException`");
192 	cn.exec("CREATE TABLE `wrongFunctionException` (
193 		`val` INTEGER
194 	) ENGINE=InnoDB DEFAULT CHARSET=utf8");
195 
196 	immutable insertSQL = "INSERT INTO `wrongFunctionException` VALUES (1), (2)";
197 	immutable selectSQL = "SELECT * FROM `wrongFunctionException`";
198 	Prepared preparedInsert;
199 	Prepared preparedSelect;
200 	int queryTupleResult;
201 	assertNotThrown!MYXWrongFunction(cn.exec(insertSQL));
202 	assertNotThrown!MYXWrongFunction(cn.querySet(selectSQL));
203 	assertNotThrown!MYXWrongFunction(cn.query(selectSQL).each());
204 	assertNotThrown!MYXWrongFunction(cn.queryRowTuple(selectSQL, queryTupleResult));
205 	assertNotThrown!MYXWrongFunction(preparedInsert = cn.prepare(insertSQL));
206 	assertNotThrown!MYXWrongFunction(preparedSelect = cn.prepare(selectSQL));
207 	assertNotThrown!MYXWrongFunction(preparedInsert.exec());
208 	assertNotThrown!MYXWrongFunction(preparedSelect.querySet());
209 	assertNotThrown!MYXWrongFunction(preparedSelect.query().each());
210 	assertNotThrown!MYXWrongFunction(preparedSelect.queryRowTuple(queryTupleResult));
211 
212 	assertThrown!MYXResultRecieved(cn.exec(selectSQL));
213 	assertThrown!MYXNoResultRecieved(cn.querySet(insertSQL));
214 	assertThrown!MYXNoResultRecieved(cn.query(insertSQL).each());
215 	assertThrown!MYXNoResultRecieved(cn.queryRowTuple(insertSQL, queryTupleResult));
216 	assertThrown!MYXResultRecieved(preparedSelect.exec());
217 	assertThrown!MYXNoResultRecieved(preparedInsert.querySet());
218 	assertThrown!MYXNoResultRecieved(preparedInsert.query().each());
219 	assertThrown!MYXNoResultRecieved(preparedInsert.queryRowTuple(queryTupleResult));
220 }