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