1 /// 2 module dpq.serialisation; 3 4 // TODO: merge all serialisers' imports 5 import dpq.serialisers.composite; 6 import dpq.serialisers.array; 7 import dpq.serialisers.scalar; 8 import dpq.serialisers.systime; 9 import dpq.serialisers..string; 10 import dpq.serialisers.bytea; 11 12 import dpq.meta; 13 import dpq.attributes; 14 import dpq.value : Type; 15 16 import std.datetime : SysTime, DateTime; 17 import std.typecons : Nullable, TypedefType; 18 import std.traits; 19 import std.bitmanip; 20 import std..string : format; 21 import std.meta; 22 23 import libpq.libpq; 24 25 26 /** 27 Converts the given type to an ubyte[], as PostgreSQL expects it. Ignores 28 any Nullable specifiers and Typedefs. 29 */ 30 package Nullable!(ubyte[]) toBytes(T)(T val) 31 { 32 alias AT = RealType!T; 33 34 if (isAnyNull(val)) 35 return Nullable!(ubyte[]).init; 36 37 alias serialiser = SerialiserFor!AT; 38 39 static if (isInstanceOf!(Nullable, T)) 40 auto av = val.get; 41 else 42 auto av = val; 43 44 return serialiser.serialise(cast(AT) av); 45 } 46 47 /*****************************************************************************/ 48 49 struct SerialiserAttribute(alias T) 50 { 51 alias serialiser = T; 52 } 53 54 SerialiserAttribute!T serialiser(alias T)() 55 { 56 return SerialiserAttribute!T(); 57 } 58 59 template SerialiserFor(T) 60 if (isBuiltinType!T) 61 { 62 static if (isSomeString!T) 63 alias SerialiserFor = StringSerialiser; 64 else static if (is(T == ubyte[])) 65 alias SerialiserFor = ByteaSerialiser; 66 else static if (isArray!T) 67 alias SerialiserFor = ArraySerialiser; 68 else static if (isScalarType!T) 69 alias SerialiserFor = ScalarSerialiser; 70 } 71 72 template SerialiserFor(alias T) 73 if (!isBuiltinType!T) 74 { 75 import std.meta; 76 77 alias UDAs = getUDAs!(T, SerialiserAttribute); 78 79 // First see if a custom serialiser is specified for the type 80 static if (UDAs.length > 0) 81 alias SerialiserFor = UDAs[0].serialiser; 82 else 83 { 84 alias RT = RealType!T; 85 86 static if (isBuiltinType!RT) 87 alias SerialiserFor = SerialiserFor!RT; 88 // Otherwise, pick one from the bunch of pre-set ones. 89 else static if (isArray!RT) 90 alias SerialiserFor = ArraySerialiser; 91 // Support for SysTime 92 else static if (is(RT == SysTime)) 93 alias SerialiserFor = SysTimeSerialiser; 94 else static if (is(RT == class) || is(RT == struct)) 95 alias SerialiserFor = CompositeTypeSerialiser; 96 else 97 static assert(false, "Cannot find serialiser for " ~ T.stringof); 98 } 99 } 100 101 unittest 102 { 103 import std.stdio; 104 105 writeln(" * SerialiserFor"); 106 107 struct Test1 {} 108 109 static assert(is(SerialiserFor!int == ScalarSerialiser)); 110 static assert(is(SerialiserFor!Test1 == CompositeTypeSerialiser)); 111 static assert(is(SerialiserFor!(int[][]) == ArraySerialiser)); 112 static assert(is(SerialiserFor!(Test1[][]) == ArraySerialiser)); 113 114 @serialiser!Test1() struct Test2 {} 115 116 static assert(is(SerialiserFor!(Test2) == Test1)); 117 } 118 119 package T fromBytes(T)(const(ubyte)[] bytes, size_t len = 0) 120 if (isInstanceOf!(Nullable, T)) 121 { 122 alias AT = RealType!T; 123 124 return T(fromBytes!AT(bytes, len).get); 125 } 126 127 package Nullable!T fromBytes(T)(const(ubyte)[] bytes, size_t len = 0) 128 if (!isInstanceOf!(Nullable, T)) 129 { 130 if (len == -1) 131 return Nullable!T.init; 132 133 alias AT = RealType!T; 134 135 return Nullable!T(cast(T) fromBytesImpl!AT(bytes, len)); 136 } 137 138 package T fromBytesImpl(T)(const(ubyte)[] bytes, size_t len) 139 { 140 alias serialiser = SerialiserFor!T; 141 return Nullable!T(serialiser.deserialise!T(bytes[0 .. len])).get; 142 } 143 144 unittest 145 { 146 import std.bitmanip; 147 import std..string; 148 import std.stdio; 149 150 writeln(" * fromBytes"); 151 152 int x = 123; 153 154 auto bs = nativeToBigEndian(x); 155 assert(fromBytes!int(bs, x.sizeof) == x); 156 157 x = -555; 158 bs = nativeToBigEndian(x); 159 assert(fromBytes!int(bs, x.sizeof) == x); 160 161 x = int.min; 162 bs = nativeToBigEndian(x); 163 assert(fromBytes!int(bs, x.sizeof) == x); 164 165 x = int.max; 166 bs = nativeToBigEndian(x); 167 assert(fromBytes!int(bs, x.sizeof) == x); 168 169 string s = "some random string"; 170 assert(fromBytes!string(s.representation, s.representation.length) == s); 171 172 s = ""; 173 assert(fromBytes!string(s.representation, s.representation.length) == s); 174 } 175 176 /*****************************************************************************/ 177 178 bool isAnyNull(T)(T val) 179 { 180 static if (is(T == class)) 181 return val is null; 182 else static if (isInstanceOf!(Nullable, T)) 183 return val.isNull; 184 else 185 return false; 186 } 187 188 /** 189 Shortuct to the type's serialiser's oidForType 190 */ 191 Oid oidFor(T)() 192 { 193 alias RT = RealType!T; 194 return SerialiserFor!RT.oidForType!RT; 195 } 196 197 deprecated("Use Serialisers and their oidForType instead") 198 template typeOid(T) 199 { 200 alias TU = std.typecons.Unqual!T; 201 static if (isArray!T && !isSomeString!T) 202 { 203 alias BT = BaseType!T; 204 static if (is(BT == int)) 205 enum typeOid = Type.INT4ARRAY; 206 else static if (is(BT == long)) 207 enum typeOid = Type.INT8ARRAY; 208 else static if (is(BT == short)) 209 enum typeOid = Type.INT2ARRAY; 210 else static if (is(BT == float)) 211 enum typeOid = Type.FLOAT4ARRAY; 212 else static if (is(BT == string)) 213 enum typeOid = Type.TEXTARRAY; 214 else static if (is(BT == byte) || is (BT == ubyte)) 215 enum typeOid = Type.BYTEA; 216 else 217 static assert(false, "Cannot map array type " ~ T.stringof ~ " to Oid"); 218 } 219 else 220 { 221 static if (is(TU == int)) 222 enum typeOid = Type.INT4; 223 else static if (is(TU == long)) 224 enum typeOid = Type.INT8; 225 else static if (is(TU == bool)) 226 enum typeOid = Type.BOOL; 227 else static if (is(TU == byte)) 228 enum typeOid = Type.CHAR; 229 else static if (is(TU == char)) 230 enum typeOid = Type.CHAR; 231 else static if (isSomeString!TU) 232 enum typeOid = Type.TEXT; 233 else static if (is(TU == short)) 234 enum typeOid = Type.INT2; 235 else static if (is(TU == float)) 236 enum typeOid = Type.FLOAT4; 237 else static if (is(TU == double)) 238 enum typeOid = Type.FLOAT8; 239 else static if (is(TU == SysTime)) 240 enum typeOid = Type.TIMESTAMP; 241 242 /** 243 Since unsigned types are not supported by PostgreSQL, we use signed 244 types for them. Transfer and representation in D will still work correctly, 245 but SELECTing them in the psql console, or as a string might result in 246 a negative number. 247 248 It is recommended not to use unsigned types in structures, that will 249 be used in the DB directly. 250 */ 251 else static if (is(TU == ulong)) 252 enum typeOid = Type.INT8; 253 else static if (is(TU == uint)) 254 enum typeOid = Type.INT4; 255 else static if (is(TU == ushort) || is(TU == char)) 256 enum typeOid = Type.INT2; 257 else static if (is(TU == ubyte)) 258 enum typeOid = Type.CHAR; 259 else 260 // Try to infer 261 enum typeOid = Type.INFER; 262 } 263 } 264 265 unittest 266 { 267 import std.stdio; 268 writeln("\t * oidFor"); 269 270 static assert(oidFor!int == Type.INT4, "int"); 271 static assert(oidFor!string == Type.TEXT, "string"); 272 assert(oidFor!(int[]) == Type.INT4ARRAY, "int[]"); 273 assert(oidFor!(int[][]) == Type.INT4ARRAY, "int[][]"); 274 assert(oidFor!(ubyte[]) == Type.BYTEA, "ubyte[]"); 275 } 276 277 /** 278 Custom serialisers - Serialiser is a struct providing all the required data 279 that dpq needs to serialise/deserialise the custom type, ensure it exists in 280 the schema, and in some cases, receive the type's OID. 281 282 All serialisers must support the following static methods: 283 284 - static bool isSupportedType(T)(); 285 Must return true iff all the other functions in serialiser know how to handle this type 286 287 - static T deserialise(T)(ubyte[]); 288 Must return T, when given postgresql-compatible representation of the type 289 290 - static ubyte[] serialise(T)(T val); 291 Must return postgresql-compatible binary representation of the type 292 293 - static Oid oidForType(T)(); 294 Must return the given type's OID, as recognised by PostgreSQL 295 296 - static string nameForType(T)(); 297 Must return a valid, unescaped name for the type, as recognised by PostgreSQL 298 299 - static void ensureExistence(T)(Connection conn); 300 Must ensure the type exists and can be used in the DB, can simply return 301 if no work is needed. 302 Must not throw or otherwise fail unless type creation failed, in case the type 303 does not yet exist, it should be silently created. 304 305 Example: 306 ----------------------- 307 struct MyTypeSerialiser 308 { 309 static bool isSupportedType(T)() 310 { 311 // magic 312 } 313 314 static T deserialise(T)(ubyte[]) 315 { 316 // magic 317 } 318 319 static ubyte[] serialise(T)(T val) 320 { 321 // magic 322 } 323 324 static Oid oidForType(T)() 325 { 326 // magic 327 } 328 329 static string nameForType(T)() 330 { 331 // magic 332 } 333 334 static void ensureExistence(T)(Connection conn) 335 { 336 // magic 337 } 338 } 339 ----------------------- 340 */