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