1 module mysql.common; 2 3 import std.algorithm; 4 import std.conv; 5 import std.datetime; 6 import std.digest.sha; 7 import std.exception; 8 import std.range; 9 import std.socket; 10 import std.stdio; 11 import std.string; 12 import std.traits; 13 import std.variant; 14 15 version(Have_vibe_d) 16 { 17 static if(__traits(compiles, (){ import vibe.core.net; } )) 18 import vibe.core.net; 19 else 20 static assert(false, "mysql-native can't find Vibe.d's 'vibe.core.net'."); 21 } 22 23 /** 24 * An exception type to distinguish exceptions thrown by this module. 25 */ 26 class MySQLException: Exception 27 { 28 this(string msg, string file = __FILE__, size_t line = __LINE__) pure 29 { 30 super(msg, file, line); 31 } 32 } 33 alias MySQLException MYX; 34 35 /** 36 * Received invalid data from the server which violates the MySQL network protocol. 37 */ 38 class MySQLProtocolException: MySQLException 39 { 40 this(string msg, string file, size_t line) pure 41 { 42 super(msg, file, line); 43 } 44 } 45 alias MySQLProtocolException MYXProtocol; 46 47 // Phobos/Vibe.d type aliases 48 package alias std.socket.TcpSocket PlainPhobosSocket; 49 version(Have_vibe_d) 50 { 51 package alias vibe.core.net.TCPConnection PlainVibeDSocket; 52 } 53 else 54 { 55 // Dummy types 56 package alias Object PlainVibeDSocket; 57 } 58 59 alias PlainPhobosSocket function(string,ushort) OpenSocketCallbackPhobos; 60 alias PlainVibeDSocket function(string,ushort) OpenSocketCallbackVibeD; 61 62 enum MySQLSocketType { phobos, vibed } 63 64 // A minimal socket interface similar to Vibe.d's TCPConnection. 65 // Used to wrap both Phobos and Vibe.d sockets with a common interface. 66 package interface MySQLSocket 67 { 68 void close(); 69 @property bool connected() const; 70 void read(ubyte[] dst); 71 void write(in ubyte[] bytes); 72 73 void acquire(); 74 void release(); 75 bool isOwner(); 76 bool amOwner(); 77 } 78 79 // Wraps a Phobos socket with the common interface 80 package class MySQLSocketPhobos : MySQLSocket 81 { 82 private PlainPhobosSocket socket; 83 84 // The socket should already be open 85 this(PlainPhobosSocket socket) 86 { 87 enforceEx!MYX(socket, "Tried to use a null Phobos socket - Maybe the 'openSocket' callback returned null?"); 88 enforceEx!MYX(socket.isAlive, "Tried to use a closed Phobos socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 89 this.socket = socket; 90 } 91 92 invariant() 93 { 94 assert(!!socket); 95 } 96 97 void close() 98 { 99 socket.shutdown(SocketShutdown.BOTH); 100 socket.close(); 101 } 102 103 @property bool connected() const 104 { 105 return socket.isAlive; 106 } 107 108 void read(ubyte[] dst) 109 { 110 // Note: I'm a little uncomfortable with this line as it doesn't 111 // (and can't) update Connection._open. Not sure what can be done, 112 // but perhaps Connection._open should be eliminated in favor of 113 // querying the socket's opened/closed state directly. 114 scope(failure) socket.close(); 115 116 for (size_t off, len; off < dst.length; off += len) { 117 len = socket.receive(dst[off..$]); 118 enforceEx!MYX(len != 0, "Server closed the connection"); 119 enforceEx!MYX(len != socket.ERROR, "Received std.socket.Socket.ERROR"); 120 } 121 } 122 123 void write(in ubyte[] bytes) 124 { 125 socket.send(bytes); 126 } 127 128 void acquire() { /+ Do nothing +/ } 129 void release() { /+ Do nothing +/ } 130 bool isOwner() { return true; } 131 bool amOwner() { return true; } 132 } 133 134 // Wraps a Vibe.d socket with the common interface 135 version(Have_vibe_d) { 136 package class MySQLSocketVibeD : MySQLSocket 137 { 138 private PlainVibeDSocket socket; 139 140 // The socket should already be open 141 this(PlainVibeDSocket socket) 142 { 143 enforceEx!MYX(socket, "Tried to use a null Vibe.d socket - Maybe the 'openSocket' callback returned null?"); 144 enforceEx!MYX(socket.connected, "Tried to use a closed Vibe.d socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 145 this.socket = socket; 146 } 147 148 invariant() 149 { 150 assert(!!socket); 151 } 152 153 void close() 154 { 155 socket.close(); 156 } 157 158 @property bool connected() const 159 { 160 return socket.connected; 161 } 162 163 void read(ubyte[] dst) 164 { 165 socket.read(dst); 166 } 167 168 void write(in ubyte[] bytes) 169 { 170 socket.write(bytes); 171 } 172 173 static if (is(typeof(&TCPConnection.isOwner))) { 174 void acquire() { socket.acquire(); } 175 void release() { socket.release(); } 176 bool isOwner() { return socket.isOwner(); } 177 bool amOwner() { return socket.isOwner(); } 178 } else { 179 void acquire() { /+ Do nothing +/ } 180 void release() { /+ Do nothing +/ } 181 bool isOwner() { return true; } 182 bool amOwner() { return true; } 183 } 184 } 185 }