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