1 // Written in D programming language 2 /** 3 * Utilities for conversion from PostgreSQL binary format. 4 * 5 * Copyright: © 2014 DSoftOut 6 * License: Subject to the terms of the MIT license, as written in the included LICENSE file. 7 * Authors: NCrashed <ncrashed@gmail.com> 8 */ 9 module pgator.db.pq.types.conv; 10 11 import pgator.db.pq.types.oids; 12 import pgator.db.connection; 13 import vibe.data.bson; 14 import dlogg.log; 15 import std.conv; 16 import std.traits; 17 import std.typetuple; 18 import std.datetime : SysTime; 19 //import util; 20 21 import pgator.db.pq.types.all; 22 23 bool nonConvertable(PQType type) 24 { 25 switch(type) 26 { 27 case PQType.RegProc: return true; 28 case PQType.RegProcArray: return true; 29 case PQType.TypeCatalog: return true; 30 case PQType.AttributeCatalog: return true; 31 case PQType.ProcCatalog: return true; 32 case PQType.ClassCatalog: return true; 33 case PQType.StorageManager: return true; 34 case PQType.Tid: return true; 35 case PQType.TidArray: return true; 36 case PQType.Line: return true; 37 case PQType.AccessControlList: return true; 38 case PQType.AccessControlListArray: return true; 39 40 // awaiting implementation 41 case PQType.FixedBitString: return true; 42 case PQType.FixedBitStringArray: return true; 43 case PQType.VariableBitString: return true; 44 case PQType.VariableBitStringArray: return true; 45 46 case PQType.RefCursor: return true; 47 case PQType.RefCursorArray: return true; 48 case PQType.RegProcWithArgs: return true; 49 case PQType.RegProcWithArgsArray: return true; 50 case PQType.RegOperator: return true; 51 case PQType.RegOperatorArray: return true; 52 case PQType.RegOperatorWithArgs: return true; 53 case PQType.RegOperatorWithArgsArray: return true; 54 case PQType.RegClass: return true; 55 case PQType.RegClassArray: return true; 56 case PQType.RegType: return true; 57 case PQType.RegTypeArray: return true; 58 59 case PQType.UUID: return true; 60 case PQType.UUIDArray: return true; 61 case PQType.TSVector: return true; 62 case PQType.TSVectorArray: return true; 63 case PQType.GTSVector: return true; 64 case PQType.GTSVectorArray: return true; 65 case PQType.TSQuery: return true; 66 case PQType.TSQueryArray: return true; 67 case PQType.RegConfig: return true; 68 case PQType.RegConfigArray: return true; 69 case PQType.RegDictionary: return true; 70 case PQType.RegDictionaryArray: return true; 71 case PQType.TXidSnapshot: return true; 72 case PQType.TXidSnapshotArray: return true; 73 74 case PQType.Int4Range: return true; 75 case PQType.Int4RangeArray: return true; 76 case PQType.NumRange: return true; 77 case PQType.NumRangeArray: return true; 78 case PQType.TimeStampRange: return true; 79 case PQType.TimeStampRangeArray: return true; 80 case PQType.TimeStampWithZoneRange: return true; 81 case PQType.TimeStampWithZoneRangeArray: return true; 82 case PQType.DateRange: return true; 83 case PQType.DateRangeArray: return true; 84 case PQType.Int8Range: return true; 85 case PQType.Int8RangeArray: return true; 86 87 // Pseudo types 88 case PQType.CString: return true; 89 case PQType.Record: return true; 90 case PQType.RecordArray: return true; 91 case PQType.AnyVoid: return true; 92 case PQType.AnyArray: return true; 93 case PQType.Trigger: return true; 94 case PQType.EventTrigger: return true; 95 case PQType.LanguageHandler: return true; 96 case PQType.Internal: return true; 97 case PQType.Opaque: return true; 98 case PQType.AnyElement: return true; 99 case PQType.AnyNoArray: return true; 100 case PQType.AnyEnum: return true; 101 case PQType.FDWHandler: return true; 102 case PQType.AnyRange: return true; 103 default: return false; 104 } 105 } 106 107 Bson toBson(PQType type)(ubyte[] val, shared IConnection conn) 108 { 109 template IsNativeSupport(T) 110 { 111 import std.range; 112 113 static if (is(T == string) || is(T == ubyte[]) || is(T == Json)) 114 { 115 enum IsNativeSupport = true; 116 } 117 else static if(isArray!T) 118 { 119 enum IsNativeSupport = IsNativeSupport!(ElementType!T); 120 } 121 else 122 { 123 enum IsNativeSupport = 124 is(T == bool) 125 || is(T == float) 126 || is(T == double) 127 || is(T == short) 128 || is(T == ushort) 129 || is(T == int) 130 || is(T == uint) 131 || is(T == long) 132 || is(T == ulong) 133 || is(T == PGNumeric); 134 } 135 } 136 137 bool checkNullValues(T)(out Bson bson) 138 { 139 static if(isSomeString!T) 140 { 141 if(val.length == 0) 142 { 143 bson = serializeToBson(""); 144 return true; 145 } 146 } 147 else static if(isArray!T) 148 { 149 if(val.length == 0) 150 { 151 bson = serializeToBson(cast(T)[]); 152 return true; 153 } 154 } else 155 { 156 if(val.length == 0) 157 { 158 bson = Bson(null); 159 return true; 160 } 161 } 162 return false; 163 } 164 165 // Checking if the convert function needs connection for reverse link 166 static if(is(ParameterTypeTuple!(convert!type) == TypeTuple!(ubyte[]))) 167 { 168 alias typeof(convert!type(val)) T; 169 170 Bson retBson; if(checkNullValues!T(retBson)) return retBson; 171 172 auto convVal = convert!type(val); 173 } else static if(is(ParameterTypeTuple!(convert!type) == TypeTuple!(ubyte[], shared IConnection))) 174 { 175 alias typeof(convert!type(val, conn)) T; 176 177 Bson retBson; if(checkNullValues!T(retBson)) return retBson; 178 179 auto convVal = convert!type(val, conn); 180 } else 181 { 182 static assert(false, text("Doesn't support '",ParameterTypeTuple!(convert!type),"' signature of converting function")); 183 } 184 185 static if(is(T == ubyte[])) 186 { 187 return serializeToBson(new BsonBinData(BsonBinData.Type.generic, convVal.idup)); 188 } 189 else static if(IsNativeSupport!T) 190 { 191 return serializeToBson(convVal); 192 } 193 else static if(is(T == SysTime)) 194 { 195 return serializeToBson(convVal.stdTime); 196 } 197 else static if(is(T == PGNumeric)) 198 { 199 double store; 200 if(convVal.canBeNative(store)) 201 return serializeToBson(store); 202 else 203 return serializeToBson(convVal.to!string); 204 } 205 else static if(is(T == struct) || is(T == class)) 206 { 207 return serializeToBson(convVal); 208 } 209 else 210 { 211 return serializeToBson(convVal.to!string); 212 } 213 } 214 215 Bson pqToBson(PQType type, ubyte[] val, shared IConnection conn, shared ILogger logger) 216 { 217 try 218 { 219 foreach(ts; __traits(allMembers, PQType)) 220 { 221 enum t = mixin("PQType."~ts); 222 if(type == t) 223 { 224 static if(nonConvertable(t)) 225 { 226 enum errMsg = ts ~ " is not supported!"; 227 pragma(msg, errMsg); 228 assert(false,errMsg); 229 } else 230 { 231 return toBson!t(val, conn); 232 } 233 } 234 } 235 } 236 catch(Exception e) 237 { 238 logger.logError(text("Binary protocol exception: ", e.msg)); 239 logger.logError(text("Converting from: ", type)); 240 logger.logError(text("Payload: ", val)); 241 logger.logError(text("Stack trace: ", e)); 242 throw e; 243 } 244 catch(Error err) 245 { 246 logger.logError(text("Binary protocol error (logic error): ", err.msg)); 247 logger.logError(text("Converting from: ", type)); 248 logger.logError(text("Payload: ", val)); 249 logger.logError(text("Stack trace: ", err)); 250 throw err; 251 } 252 253 debug assert(false, "Unknown type "~to!string(type)~"!"); 254 else 255 { 256 throw new Exception(text("pgator doesn't support typeid ", type," at the moment! Please, visit " 257 "https://github.com/DSoftOut/pgator and open an issue.")); 258 } 259 } 260 261 version(IntegrationTest2) 262 { 263 import pgator.db.pool; 264 import dlogg.log; 265 266 void testConvertions(shared ILogger logger, shared IConnectionPool pool) 267 { 268 foreach(t; __traits(allMembers, PQType)) 269 { 270 enum type = mixin("PQType."~t); 271 static if(!nonConvertable(type)) 272 { 273 test!type(logger, pool); 274 } 275 } 276 } 277 }