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 
18 /++
19 The server sent back a MySQL error code and message. If the server is 4.1+,
20 there should also be an ANSI/ODBC-standard SQLSTATE error code.
21 
22 See_Also: $(LINK https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html)
23 +/
24 class MYXReceived: MYX
25 {
26 	ushort errorCode;
27 	char[5] sqlState;
28 
29 	this(OKErrorPacket okp, string file, size_t line) pure
30 	{
31 		this(okp.message, okp.serverStatus, okp.sqlState, file, line);
32 	}
33 
34 	this(string msg, ushort errorCode, char[5] sqlState, string file, size_t line) pure
35 	{
36 		this.errorCode = errorCode;
37 		this.sqlState = sqlState;
38 		super("MySQL error: " ~ msg, file, line);
39 	}
40 }
41 
42 /++
43 Received invalid data from the server which violates the MySQL network protocol.
44 (Quite possibly mysql-native's fault. Please
45 $(LINK2 https://github.com/mysql-d/mysql-native/issues, file an issue)
46 if you receive this.)
47 +/
48 class MYXProtocol: MYX
49 {
50 	this(string msg, string file, size_t line) pure
51 	{
52 		super(msg, file, line);
53 	}
54 }
55 
56 /++
57 Deprecated - No longer thrown by mysql-native.
58 
59 In previous versions, this had been thrown when attempting to use a
60 prepared statement which had already been released.
61 
62 But as of v2.0.0, prepared statements are connection-independent and
63 automatically registered on connections as needed, so this exception
64 is no longer used.
65 +/
66 deprecated("No longer thrown by mysql-native. You can safely remove all handling of this exception from your code.")
67 class MYXNotPrepared: MYX
68 {
69 	this(string file = __FILE__, size_t line = __LINE__) pure
70 	{
71 		super("The prepared statement has already been released.", file, line);
72 	}
73 }
74 
75 /++
76 Common base class of `MYXResultRecieved` and `MYXNoResultRecieved`.
77 
78 Thrown when making the wrong choice between `mysql.commands.exec` versus `mysql.commands.query`.
79 
80 The query functions (`mysql.commands.query`, `mysql.commands.queryRow`, etc.)
81 are for SQL statements such as SELECT that
82 return results (even if the result set has zero elements.)
83 
84 The `mysql.commands.exec` functions
85 are for SQL statements, such as INSERT, that never return result sets,
86 but may return `rowsAffected`.
87 
88 Using one of those functions, when the other should have been used instead,
89 results in an exception derived from this.
90 +/
91 class MYXWrongFunction: MYX
92 {
93 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
94 	{
95 		super(msg, file, line);
96 	}
97 }
98 
99 /++
100 Thrown when a result set was returned unexpectedly.
101 
102 Use the query functions (`mysql.commands.query`, `mysql.commands.queryRow`, etc.),
103 not `mysql.commands.exec` for commands
104 that return result sets (such as SELECT), even if the result set has zero elements.
105 +/
106 class MYXResultRecieved: MYXWrongFunction
107 {
108 	this(string file = __FILE__, size_t line = __LINE__) pure
109 	{
110 		super(
111 			"A result set was returned. Use the query functions, not exec, "~
112 			"for commands that return result sets.",
113 			file, line
114 		);
115 	}
116 }
117 
118 /++
119 Thrown when the executed query, unexpectedly, did not produce a result set.
120 
121 Use the `mysql.commands.exec` functions,
122 not `mysql.commands.query`/`mysql.commands.queryRow`/etc.
123 for commands that don't produce result sets (such as INSERT).
124 +/
125 class MYXNoResultRecieved: MYXWrongFunction
126 {
127 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
128 	{
129 		super(
130 			"The executed query did not produce a result set. Use the exec "~
131 			"functions, not query, for commands that don't produce result sets.",
132 			file, line
133 		);
134 	}
135 }
136 
137 /++
138 Thrown when attempting to use a range that's been invalidated.
139 
140 This can occur when using a `mysql.result.ResultRange` after a new command
141 has been issued on the same connection.
142 +/
143 class MYXInvalidatedRange: MYX
144 {
145 	this(string msg, string file = __FILE__, size_t line = __LINE__) pure
146 	{
147 		super(msg, file, line);
148 	}
149 }
150 
151 debug(MYSQLN_TESTS)
152 unittest
153 {
154 	import std.exception;
155 	import mysql.commands;
156 	import mysql.connection;
157 	import mysql.prepared;
158 	import mysql.test.common : scopedCn, createCn;
159 	mixin(scopedCn);
160 
161 	cn.exec("DROP TABLE IF EXISTS `wrongFunctionException`");
162 	cn.exec("CREATE TABLE `wrongFunctionException` (
163 		`val` INTEGER
164 	) ENGINE=InnoDB DEFAULT CHARSET=utf8");
165 
166 	immutable insertSQL = "INSERT INTO `wrongFunctionException` VALUES (1), (2)";
167 	immutable selectSQL = "SELECT * FROM `wrongFunctionException`";
168 	Prepared preparedInsert;
169 	Prepared preparedSelect;
170 	int queryTupleResult;
171 	assertNotThrown!MYXWrongFunction(cn.exec(insertSQL));
172 	assertNotThrown!MYXWrongFunction(cn.query(selectSQL).each());
173 	assertNotThrown!MYXWrongFunction(cn.queryRowTuple(selectSQL, queryTupleResult));
174 	assertNotThrown!MYXWrongFunction(preparedInsert = cn.prepare(insertSQL));
175 	assertNotThrown!MYXWrongFunction(preparedSelect = cn.prepare(selectSQL));
176 	assertNotThrown!MYXWrongFunction(cn.exec(preparedInsert));
177 	assertNotThrown!MYXWrongFunction(cn.query(preparedSelect).each());
178 	assertNotThrown!MYXWrongFunction(cn.queryRowTuple(preparedSelect, queryTupleResult));
179 
180 	assertThrown!MYXResultRecieved(cn.exec(selectSQL));
181 	assertThrown!MYXNoResultRecieved(cn.query(insertSQL).each());
182 	assertThrown!MYXNoResultRecieved(cn.queryRowTuple(insertSQL, queryTupleResult));
183 	assertThrown!MYXResultRecieved(cn.exec(preparedSelect));
184 	assertThrown!MYXNoResultRecieved(cn.query(preparedInsert).each());
185 	assertThrown!MYXNoResultRecieved(cn.queryRowTuple(preparedInsert, queryTupleResult));
186 }