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  */