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 auto bytesRead = socket.receive(dst); 117 enforceEx!MYX(bytesRead == dst.length, "Wrong number of bytes read"); 118 enforceEx!MYX(bytesRead != socket.ERROR, "Received std.socket.Socket.ERROR"); 119 } 120 121 void write(in ubyte[] bytes) 122 { 123 socket.send(bytes); 124 } 125 126 void acquire() { /+ Do nothing +/ } 127 void release() { /+ Do nothing +/ } 128 bool isOwner() { return true; } 129 bool amOwner() { return true; } 130 } 131 132 // Wraps a Vibe.d socket with the common interface 133 version(Have_vibe_d) { 134 package class MySQLSocketVibeD : MySQLSocket 135 { 136 private PlainVibeDSocket socket; 137 138 // The socket should already be open 139 this(PlainVibeDSocket socket) 140 { 141 enforceEx!MYX(socket, "Tried to use a null Vibe.d socket - Maybe the 'openSocket' callback returned null?"); 142 enforceEx!MYX(socket.connected, "Tried to use a closed Vibe.d socket - Maybe the 'openSocket' callback created a socket but forgot to open it?"); 143 this.socket = socket; 144 } 145 146 invariant() 147 { 148 assert(!!socket); 149 } 150 151 void close() 152 { 153 socket.close(); 154 } 155 156 @property bool connected() const 157 { 158 return socket.connected; 159 } 160 161 void read(ubyte[] dst) 162 { 163 socket.read(dst); 164 } 165 166 void write(in ubyte[] bytes) 167 { 168 socket.write(bytes); 169 } 170 171 static if (is(typeof(&TCPConnection.isOwner))) { 172 void acquire() { socket.acquire(); } 173 void release() { socket.release(); } 174 bool isOwner() { return socket.isOwner(); } 175 bool amOwner() { return socket.isOwner(); } 176 } else { 177 void acquire() { /+ Do nothing +/ } 178 void release() { /+ Do nothing +/ } 179 bool isOwner() { return true; } 180 bool amOwner() { return true; } 181 } 182 } 183 }