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