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 Thrown when attempting to communicate with the server (ex: executing SQL or 46 creating a new prepared statement) while the server is still sending results 47 data. Any ResultRange must be consumed or purged before anything else 48 can be done on the connection. 49 +/ 50 class MySQLDataPendingException: MySQLException 51 { 52 this(string file = __FILE__, size_t line = __LINE__) pure 53 { 54 super("Data is pending on the connection. Any existing ResultRange "~ 55 "must be consumed or purged before performing any other communication "~ 56 "with the server.", file, line); 57 } 58 } 59 alias MYXDataPending = MySQLDataPendingException; 60 61 /++ 62 Received invalid data from the server which violates the MySQL network protocol. 63 (Quite possibly mysql-native's fault. Please 64 $(LINK2 https://github.com/mysql-d/mysql-native/issues, file an issue) 65 if you receive this.) 66 +/ 67 class MySQLProtocolException: MySQLException 68 { 69 this(string msg, string file, size_t line) pure 70 { 71 super(msg, file, line); 72 } 73 } 74 alias MYXProtocol = MySQLProtocolException; 75 76 /++ 77 Thrown when attempting to use a prepared statement which had already been released. 78 +/ 79 class MySQLNotPreparedException: MySQLException 80 { 81 this(string file = __FILE__, size_t line = __LINE__) pure 82 { 83 super("The prepared statement has already been released.", file, line); 84 } 85 } 86 alias MYXNotPrepared = MySQLNotPreparedException; 87 88 /++ 89 Common base class of MySQLResultRecievedException and MySQLNoResultRecievedException. 90 91 Thrown when making the wrong choice between exec or query. 92 93 The query functions (query, querySet, queryRow, etc.) are for SQL statements 94 such as SELECT that return results (even if the result set has zero elements.) 95 96 The exec functions are for SQL statements, such as INSERT, that never return 97 result sets, but may return rowsAffected. 98 99 Using one of those functions, when the other should have been used instead, 100 results in an exception derived from this. 101 +/ 102 class MySQLWrongFunctionException: MySQLException 103 { 104 this(string msg, string file = __FILE__, size_t line = __LINE__) pure 105 { 106 super(msg, file, line); 107 } 108 } 109 alias MYXWrongFunction = MySQLWrongFunctionException; 110 111 /++ 112 Thrown when a result set was returned unexpectedly. Use the query functions 113 (query, querySet, queryRow, etc.), not exec for commands that return 114 result sets (such as SELECT), even if the result set has zero elements. 115 +/ 116 class MySQLResultRecievedException: MySQLWrongFunctionException 117 { 118 this(string file = __FILE__, size_t line = __LINE__) pure 119 { 120 super( 121 "A result set was returned. Use the query functions, not exec, "~ 122 "for commands that return result sets.", 123 file, line 124 ); 125 } 126 } 127 alias MYXResultRecieved = MySQLResultRecievedException; 128 129 /++ 130 Thrown when the executed query, unexpectedly, did not produce a result set. 131 Use the exec functions, not query (query, querySet, queryRow, etc.), 132 for commands that don't produce result sets (such as INSERT). 133 +/ 134 class MySQLNoResultRecievedException: MySQLWrongFunctionException 135 { 136 this(string msg, string file = __FILE__, size_t line = __LINE__) pure 137 { 138 super( 139 "The executed query did not produce a result set. Use the exec "~ 140 "functions, not query, for commands that don't produce result sets.", 141 file, line 142 ); 143 } 144 } 145 alias MYXNoResultRecieved = MySQLNoResultRecievedException; 146 147 /++ 148 Thrown when attempting to use a range that's been invalidated. 149 In particular, when using a ResultRange after a new command 150 has been issued on the same connection. 151 +/ 152 class MySQLInvalidatedRangeException: MySQLException 153 { 154 this(string msg, string file = __FILE__, size_t line = __LINE__) pure 155 { 156 super(msg, file, line); 157 } 158 } 159 alias MYXInvalidatedRange = MySQLInvalidatedRangeException; 160 161 debug(MYSQL_INTEGRATION_TESTS) 162 unittest 163 { 164 import std.exception; 165 import mysql.commands; 166 import mysql.prepared; 167 import mysql.test.common : scopedCn, createCn; 168 mixin(scopedCn); 169 170 cn.exec("DROP TABLE IF EXISTS `wrongFunctionException`"); 171 cn.exec("CREATE TABLE `wrongFunctionException` ( 172 `val` INTEGER 173 ) ENGINE=InnoDB DEFAULT CHARSET=utf8"); 174 175 immutable insertSQL = "INSERT INTO `wrongFunctionException` VALUES (1), (2)"; 176 immutable selectSQL = "SELECT * FROM `wrongFunctionException`"; 177 Prepared preparedInsert; 178 Prepared preparedSelect; 179 int queryTupleResult; 180 assertNotThrown!MYXWrongFunction(cn.exec(insertSQL)); 181 assertNotThrown!MYXWrongFunction(cn.querySet(selectSQL)); 182 assertNotThrown!MYXWrongFunction(cn.query(selectSQL).each()); 183 assertNotThrown!MYXWrongFunction(cn.queryRowTuple(selectSQL, queryTupleResult)); 184 assertNotThrown!MYXWrongFunction(preparedInsert = cn.prepare(insertSQL)); 185 assertNotThrown!MYXWrongFunction(preparedSelect = cn.prepare(selectSQL)); 186 assertNotThrown!MYXWrongFunction(preparedInsert.exec()); 187 assertNotThrown!MYXWrongFunction(preparedSelect.querySet()); 188 assertNotThrown!MYXWrongFunction(preparedSelect.query().each()); 189 assertNotThrown!MYXWrongFunction(preparedSelect.queryRowTuple(queryTupleResult)); 190 191 assertThrown!MYXResultRecieved(cn.exec(selectSQL)); 192 assertThrown!MYXNoResultRecieved(cn.querySet(insertSQL)); 193 assertThrown!MYXNoResultRecieved(cn.query(insertSQL).each()); 194 assertThrown!MYXNoResultRecieved(cn.queryRowTuple(insertSQL, queryTupleResult)); 195 assertThrown!MYXResultRecieved(preparedSelect.exec()); 196 assertThrown!MYXNoResultRecieved(preparedInsert.querySet()); 197 assertThrown!MYXNoResultRecieved(preparedInsert.query().each()); 198 assertThrown!MYXNoResultRecieved(preparedInsert.queryRowTuple(queryTupleResult)); 199 }